mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-26 01:36:18 -04:00
Move service worker digestion into separate class and add static function to check if it is supported by the browser. Change ram-exceed-ios waring accordingly.
This commit is contained in:
parent
90f10910aa
commit
f4a947527d
3 changed files with 178 additions and 105 deletions
|
@ -164,7 +164,7 @@
|
|||
"selected-peer-left": "Selected peer left",
|
||||
"error-sharing-size": "Files too big to be shared. They can be downloaded instead.",
|
||||
"error-sharing-default": "Error while sharing. It can be downloaded instead.",
|
||||
"ram-exceed-ios": "File is bigger than 250 MB and will crash the page on iOS. Use https to prevent this."
|
||||
"ram-exceed-ios": "One of the files is bigger than 250 MB and will crash the page on iOS. Use https and do not use private tabs on the iOS device to prevent this."
|
||||
},
|
||||
"document-titles": {
|
||||
"file-received": "File Received",
|
||||
|
|
|
@ -484,7 +484,7 @@ class Peer {
|
|||
|
||||
_sendData(data) {}
|
||||
|
||||
_onMessage(message) {
|
||||
async _onMessage(message) {
|
||||
switch (message.type) {
|
||||
case 'display-name-changed':
|
||||
this._onDisplayNameChanged(message);
|
||||
|
@ -493,7 +493,7 @@ class Peer {
|
|||
this._onState(message.state);
|
||||
break;
|
||||
case 'transfer-request':
|
||||
this._onTransferRequest(message);
|
||||
await this._onTransferRequest(message);
|
||||
break;
|
||||
case 'transfer-request-response':
|
||||
this._onTransferRequestResponse(message);
|
||||
|
@ -740,44 +740,44 @@ class Peer {
|
|||
}
|
||||
|
||||
// File Receiver Only
|
||||
_onTransferRequest(request) {
|
||||
async _onTransferRequest(request) {
|
||||
// Only accept one request at a time per peer
|
||||
if (this._pendingRequest) {
|
||||
// Only accept one request at a time per peer
|
||||
this._sendTransferRequestResponse(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if each file must be loaded into RAM completely. This might lead to a page crash (Memory limit iOS Safari: ~380 MB)
|
||||
if (!(await FileDigesterWorker.isSupported())) {
|
||||
Logger.warn('Big file transfers might exceed the RAM of the receiver. Use a secure context (https) and do not use private tabs to prevent this.');
|
||||
|
||||
// Check if page will crash on iOS
|
||||
if (window.iOS && await this._filesTooBigForSwOnIOS(request.header)) {
|
||||
Events.fire('notify-user', Localization.getTranslation('notifications.ram-exceed-ios'));
|
||||
|
||||
// Would exceed RAM -> decline request
|
||||
this._sendTransferRequestResponse(false, 'ram-exceed-ios');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._pendingRequest = request;
|
||||
|
||||
if (!window.Worker || !window.isSecureContext) {
|
||||
// Each file must be loaded into RAM completely which might lead to a page crash (Memory limit iOS Safari: ~380 MB)
|
||||
Logger.warn('Big file transfers might exceed the RAM of the receiver. Use a secure context (https) to prevent this.');
|
||||
}
|
||||
|
||||
if (window.iOS && this._filesTooBigForIos(request.header)) {
|
||||
// Page will crash. Decline request
|
||||
Events.fire('notify-user', Localization.getTranslation('notifications.ram-exceed-ios'));
|
||||
this._sendTransferRequestResponse(false, 'ram-exceed-ios');
|
||||
return;
|
||||
}
|
||||
|
||||
// Automatically accept request if auto-accept is set to true via the Edit Paired Devices Dialog
|
||||
if (this._autoAccept) {
|
||||
// auto accept if set via Edit Paired Devices Dialog
|
||||
this._sendTransferRequestResponse(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// default behavior: show user transfer request
|
||||
// Default behavior: show transfer request to user
|
||||
Events.fire('files-transfer-request', {
|
||||
request: request,
|
||||
peerId: this._peerId
|
||||
});
|
||||
}
|
||||
|
||||
_filesTooBigForIos(files) {
|
||||
if (window.Worker && window.isSecureContext) {
|
||||
return false;
|
||||
}
|
||||
async _filesTooBigForSwOnIOS(files) {
|
||||
// Files over 250 MB crash safari if not handled via a service worker
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
if (files[i].size > 250000000) {
|
||||
return true;
|
||||
|
@ -1313,7 +1313,7 @@ class RTCPeer extends Peer {
|
|||
this._state = Peer.STATE_TRANSFER_PROCEEDING;
|
||||
}
|
||||
|
||||
_onMessage(message) {
|
||||
async _onMessage(message) {
|
||||
Logger.debug('RTC Receive:', JSON.parse(message));
|
||||
try {
|
||||
message = JSON.parse(message);
|
||||
|
@ -1321,7 +1321,7 @@ class RTCPeer extends Peer {
|
|||
Logger.warn("RTCPeer: Received JSON is malformed");
|
||||
return;
|
||||
}
|
||||
super._onMessage(message);
|
||||
await super._onMessage(message);
|
||||
}
|
||||
|
||||
getConnectionHash() {
|
||||
|
@ -1412,9 +1412,9 @@ class WSPeer extends Peer {
|
|||
this._sendSignal(true);
|
||||
}
|
||||
|
||||
_onMessage(message) {
|
||||
async _onMessage(message) {
|
||||
Logger.debug('WS Receive:', message);
|
||||
super._onMessage(message);
|
||||
await super._onMessage(message);
|
||||
}
|
||||
|
||||
_onWsRelay(message) {
|
||||
|
@ -1847,12 +1847,14 @@ class FileDigester {
|
|||
if (this._bytesReceived < this._size) return;
|
||||
|
||||
// We are done receiving. Preferably use a file worker to process the file to prevent exceeding of available RAM
|
||||
if (!window.Worker && !window.isSecureContext) {
|
||||
this.processFileViaMemory();
|
||||
return;
|
||||
}
|
||||
|
||||
this.processFileViaWorker();
|
||||
FileDigesterWorker.isSupported()
|
||||
.then(supported => {
|
||||
if (!supported) {
|
||||
this.processFileViaMemory();
|
||||
return;
|
||||
}
|
||||
this.processFileViaWorker();
|
||||
});
|
||||
}
|
||||
|
||||
processFileViaMemory() {
|
||||
|
@ -1865,88 +1867,146 @@ class FileDigester {
|
|||
}
|
||||
|
||||
processFileViaWorker() {
|
||||
// Use service worker to prevent loading the complete file into RAM
|
||||
const fileWorker = new Worker("scripts/sw-file-digester.js");
|
||||
|
||||
let i = 0;
|
||||
let offset = 0;
|
||||
|
||||
const _this = this;
|
||||
|
||||
function sendPart(buffer, offset) {
|
||||
fileWorker.postMessage({
|
||||
type: "part",
|
||||
name: _this._name,
|
||||
buffer: buffer,
|
||||
offset: offset
|
||||
});
|
||||
}
|
||||
|
||||
function getFile() {
|
||||
fileWorker.postMessage({
|
||||
type: "get-file",
|
||||
name: _this._name,
|
||||
});
|
||||
}
|
||||
|
||||
function deleteFile() {
|
||||
fileWorker.postMessage({
|
||||
type: "delete-file",
|
||||
name: _this._name
|
||||
const fileDigesterWorker = new FileDigesterWorker();
|
||||
fileDigesterWorker.digestFileBuffer(this._buffer, this._name)
|
||||
.then(file => {
|
||||
this._fileCompleteCallback(file);
|
||||
})
|
||||
}
|
||||
.catch(reason => {
|
||||
Logger.warn(reason);
|
||||
this.processFileViaWorker();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function onPart(part) {
|
||||
if (i < _this._buffer.length - 1) {
|
||||
// process next chunk
|
||||
offset += part.byteLength;
|
||||
i++;
|
||||
sendPart(_this._buffer[i], offset);
|
||||
return;
|
||||
}
|
||||
class FileDigesterWorker {
|
||||
|
||||
// File processing complete -> retrieve completed file
|
||||
getFile();
|
||||
}
|
||||
constructor() {
|
||||
// Use service worker to prevent loading the complete file into RAM
|
||||
this.fileWorker = new Worker("scripts/sw-file-digester.js");
|
||||
|
||||
function onFile(file) {
|
||||
_this._buffer = [];
|
||||
_this._fileCompleteCallback(file);
|
||||
deleteFile();
|
||||
}
|
||||
|
||||
function onFileDeleted() {
|
||||
// File Digestion complete -> Tidy up
|
||||
fileWorker.terminate();
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
// an error occurred.
|
||||
Logger.error(error);
|
||||
Logger.warn('Failed to process file via service-worker. Do not use Firefox private mode to prevent this.')
|
||||
|
||||
// Use memory method instead and terminate service worker.
|
||||
fileWorker.terminate();
|
||||
_this.processFileViaMemory();
|
||||
}
|
||||
|
||||
sendPart(this._buffer[i], offset);
|
||||
|
||||
fileWorker.onmessage = (e) => {
|
||||
this.fileWorker.onmessage = (e) => {
|
||||
switch (e.data.type) {
|
||||
case "support":
|
||||
this.onSupport(e.data.supported);
|
||||
break;
|
||||
case "part":
|
||||
onPart(e.data.part);
|
||||
this.onPart(e.data.part);
|
||||
break;
|
||||
case "file":
|
||||
onFile(e.data.file);
|
||||
this.onFile(e.data.file);
|
||||
break;
|
||||
case "file-deleted":
|
||||
onFileDeleted();
|
||||
this.onFileDeleted();
|
||||
break;
|
||||
case "error":
|
||||
onError(e.data.error);
|
||||
this.onError(e.data.error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static isSupported() {
|
||||
// Check if web worker is supported and supports specific functions
|
||||
return new Promise(async resolve => {
|
||||
if (!window.Worker || !window.isSecureContext) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fileDigesterWorker = new FileDigesterWorker();
|
||||
|
||||
resolve(await fileDigesterWorker.checkSupport());
|
||||
|
||||
fileDigesterWorker.fileWorker.terminate();
|
||||
})
|
||||
}
|
||||
|
||||
checkSupport() {
|
||||
return new Promise(resolve => {
|
||||
this.resolveSupport = resolve;
|
||||
this.fileWorker.postMessage({
|
||||
type: "check-support"
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
onSupport(supported) {
|
||||
if (!this.resolveSupport) return;
|
||||
|
||||
this.resolveSupport(supported);
|
||||
this.resolveSupport = null;
|
||||
}
|
||||
|
||||
digestFileBuffer(buffer, fileName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.resolveFile = resolve;
|
||||
this.rejectFile = reject;
|
||||
|
||||
this.i = 0;
|
||||
this.offset = 0;
|
||||
|
||||
this.buffer = buffer;
|
||||
this.fileName = fileName;
|
||||
|
||||
this.sendPart(this.buffer[0], 0);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
sendPart(buffer, offset) {
|
||||
this.fileWorker.postMessage({
|
||||
type: "part",
|
||||
name: this.fileName,
|
||||
buffer: buffer,
|
||||
offset: offset
|
||||
});
|
||||
}
|
||||
|
||||
getFile() {
|
||||
this.fileWorker.postMessage({
|
||||
type: "get-file",
|
||||
name: this.fileName,
|
||||
});
|
||||
}
|
||||
|
||||
deleteFile() {
|
||||
this.fileWorker.postMessage({
|
||||
type: "delete-file",
|
||||
name: this.fileName
|
||||
})
|
||||
}
|
||||
|
||||
onPart(part) {
|
||||
if (this.i < this.buffer.length - 1) {
|
||||
// process next chunk
|
||||
this.offset += part.byteLength;
|
||||
this.i++;
|
||||
this.sendPart(this.buffer[this.i], this.offset);
|
||||
return;
|
||||
}
|
||||
|
||||
// File processing complete -> retrieve completed file
|
||||
this.getFile();
|
||||
}
|
||||
|
||||
onFile(file) {
|
||||
this.buffer = [];
|
||||
this.resolveFile(file);
|
||||
this.deleteFile();
|
||||
}
|
||||
|
||||
onFileDeleted() {
|
||||
// File Digestion complete -> Tidy up
|
||||
this.fileWorker.terminate();
|
||||
}
|
||||
|
||||
onError(error) {
|
||||
// an error occurred.
|
||||
Logger.error(error);
|
||||
|
||||
// Use memory method instead and terminate service worker.
|
||||
this.fileWorker.terminate();
|
||||
this.rejectFile("Failed to process file via service-worker. Do not use Firefox private mode to prevent this.");
|
||||
}
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
self.addEventListener('message', async e => {
|
||||
try {
|
||||
switch (e.data.type) {
|
||||
case "check-support":
|
||||
await checkSupport();
|
||||
break;
|
||||
case "part":
|
||||
await this.onPart(e.data.name, e.data.buffer, e.data.offset);
|
||||
await onPart(e.data.name, e.data.buffer, e.data.offset);
|
||||
break;
|
||||
case "get-file":
|
||||
await this.onGetFile(e.data.name);
|
||||
await onGetFile(e.data.name);
|
||||
break;
|
||||
case "delete-file":
|
||||
await this.onDeleteFile(e.data.name);
|
||||
await onDeleteFile(e.data.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +20,16 @@ self.addEventListener('message', async e => {
|
|||
}
|
||||
})
|
||||
|
||||
async function checkSupport() {
|
||||
try {
|
||||
await getAccessHandle("test.txt");
|
||||
self.postMessage({type: "support", supported: true});
|
||||
}
|
||||
catch (e) {
|
||||
self.postMessage({type: "support", supported: false});
|
||||
}
|
||||
}
|
||||
|
||||
async function getFileHandle(fileName) {
|
||||
const root = await navigator.storage.getDirectory();
|
||||
return await root.getFileHandle(fileName, {create: true});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue