Update handling of bake errors.

Add preview thumbnail for image input.
This commit is contained in:
j433866 2019-05-01 17:08:36 +01:00
parent 1cedc94652
commit ff9c68db56
6 changed files with 193 additions and 116 deletions

View file

@ -95,7 +95,7 @@ async function bake(data) {
self.loadRequiredModules(data.recipeConfig); self.loadRequiredModules(data.recipeConfig);
try { try {
self.inputNum = data.inputNum; self.inputNum = parseInt(data.inputNum, 10);
const response = await self.chef.bake( const response = await self.chef.bake(
data.input, // The user's input data.input, // The user's input
data.recipeConfig, // The configuration of the recipe data.recipeConfig, // The configuration of the recipe
@ -104,13 +104,25 @@ async function bake(data) {
data.step // Whether or not to take one step or execute the whole recipe data.step // Whether or not to take one step or execute the whole recipe
); );
if (typeof response.result === "string") { if (typeof response.result === "string") {
self.postMessage({ if (response.progress !== data.progress + data.recipeConfig.length) {
action: "bakeComplete", self.postMessage({
data: Object.assign(response, { action: "bakeError",
id: data.id, data: {
inputNum: data.inputNum error: response.result,
}) id: data.id,
}); inputNum: data.inputNum,
progress: response.progress
}
});
} else {
self.postMessage({
action: "bakeComplete",
data: Object.assign(response, {
id: data.id,
inputNum: data.inputNum
})
});
}
} else { } else {
self.postMessage({ self.postMessage({
action: "bakeComplete", action: "bakeComplete",
@ -123,10 +135,11 @@ async function bake(data) {
} catch (err) { } catch (err) {
self.postMessage({ self.postMessage({
action: "bakeError", action: "bakeError",
data: Object.assign(err, { data: {
error: err,
id: data.id, id: data.id,
inputNum: data.inputNum inputNum: data.inputNum
}) }
}); });
} }
self.inputNum = -1; self.inputNum = -1;

View file

@ -79,6 +79,9 @@ class App {
if (!this.workerLoaded || !this.appLoaded || if (!this.workerLoaded || !this.appLoaded ||
!document.getElementById("loader-wrapper")) return; !document.getElementById("loader-wrapper")) return;
// Bake initial input
this.getAllInput();
// Trigger CSS animations to remove preloader // Trigger CSS animations to remove preloader
document.body.classList.add("loaded"); document.body.classList.add("loaded");

View file

@ -20,8 +20,8 @@ class InputWaiter {
/** /**
* InputWaiter constructor. * InputWaiter constructor.
* *
* @param {App} app - The main view object for CyberChef * @param {App} app - The main view object for CyberChef.
* @param {Manager} manager- The CyberChef event manager. * @param {Manager} manager - The CyberChef event manager.
*/ */
constructor(app, manager) { constructor(app, manager) {
this.app = app; this.app = app;
@ -341,7 +341,6 @@ class InputWaiter {
this.setInputInfo(inputData.input.length, lines); this.setInputInfo(inputData.input.length, lines);
} else { } else {
this.setFile(inputData); this.setFile(inputData);
// show file info here
} }
if (!silent) window.dispatchEvent(this.manager.statechange); if (!silent) window.dispatchEvent(this.manager.statechange);
@ -372,6 +371,15 @@ class InputWaiter {
this.displayFilePreview(inputData); this.displayFilePreview(inputData);
} }
/**
* Reset the input thumbnail to the default icon
*/
resetFileThumb() {
const fileThumb = document.getElementById("input-file-thumbnail");
fileThumb.src = require("./static/images/file-128x128.png");
}
/** /**
* Shows a chunk of the file in the input behind the file overlay * Shows a chunk of the file in the input behind the file overlay
* *
@ -382,11 +390,28 @@ class InputWaiter {
displayFilePreview(inputData) { displayFilePreview(inputData) {
const activeTab = this.getActiveTab(), const activeTab = this.getActiveTab(),
input = inputData.input, input = inputData.input,
inputText = document.getElementById("input-text"); inputText = document.getElementById("input-text"),
fileThumb = document.getElementById("input-file-thumbnail");
if (inputData.inputNum !== activeTab) return; if (inputData.inputNum !== activeTab) return;
inputText.style.overflow = "hidden"; inputText.style.overflow = "hidden";
inputText.classList.add("blur"); inputText.classList.add("blur");
inputText.value = Utils.printable(Utils.arrayBufferToStr(input)); inputText.value = Utils.printable(Utils.arrayBufferToStr(input.slice(0, 4096)));
// Display image in thumbnail if we want
if (this.app.options.imagePreview) {
const inputArr = new Uint8Array(input),
type = isImage(inputArr);
if (type && type !== "image/tiff" && inputArr.byteLength <= 512000) {
const blob = new Blob([inputArr], {type: type}),
url = URL.createObjectURL(blob);
fileThumb.src = url;
} else {
this.resetFileThumb();
}
} else {
this.resetFileThumb();
}
} }
/** /**

View file

@ -312,20 +312,29 @@ self.setInput = function(inputData) {
input: inputVal input: inputVal
}; };
if (typeof inputVal !== "string") { if (typeof inputVal !== "string") {
inputObj.input = inputVal.fileBuffer.slice(0, 4096); const fileSlice = inputVal.fileBuffer.slice(0, 512001);
inputObj.input = fileSlice;
inputObj.name = inputVal.name; inputObj.name = inputVal.name;
inputObj.size = inputVal.size; inputObj.size = inputVal.size;
inputObj.type = inputVal.type; inputObj.type = inputVal.type;
inputObj.progress = input.progress; inputObj.progress = input.progress;
}
self.postMessage({ self.postMessage({
action: "setInput", action: "setInput",
data: { data: {
inputObj: inputObj, inputObj: inputObj,
silent: silent silent: silent
} }
}); }, [fileSlice]);
} else {
self.postMessage({
action: "setInput",
data: {
inputObj: inputObj,
silent: silent
}
});
}
self.getInputProgress(inputNum); self.getInputProgress(inputNum);
}; };

View file

@ -132,15 +132,17 @@ class OutputWaiter {
* *
* @param {Error} error * @param {Error} error
* @param {number} inputNum * @param {number} inputNum
* @param {number} [progress=0]
*/ */
updateOutputError(error, inputNum) { updateOutputError(error, inputNum, progress=0) {
if (this.getOutput(inputNum) === -1) return; if (this.getOutput(inputNum) === -1) return;
this.outputs[inputNum].error = error; this.outputs[inputNum].error = error;
this.outputs[inputNum].progress = progress;
this.updateOutputStatus("error", inputNum);
// call handle error here // call handle error here
// or make the error handling part of set() // or make the error handling part of set()
this.set(inputNum);
} }
/** /**
@ -153,6 +155,11 @@ class OutputWaiter {
if (this.getOutput(inputNum) === -1) return; if (this.getOutput(inputNum) === -1) return;
this.outputs[inputNum].status = status; this.outputs[inputNum].status = status;
if (status !== "error") {
delete this.outputs[inputNum].error;
delete this.outputs[inputNum].progress;
}
this.set(inputNum); this.set(inputNum);
} }
@ -186,9 +193,10 @@ class OutputWaiter {
set(inputNum) { set(inputNum) {
const output = this.outputs[inputNum]; const output = this.outputs[inputNum];
if (output === undefined || output === null) return; if (output === undefined || output === null) return;
if (typeof inputNum !== "number") inputNum = parseInt(inputNum, 10); if (typeof inputNum !== "number") inputNum = parseInt(inputNum, 10);
if (inputNum !== this.getActiveTab()) return;
const outputText = document.getElementById("output-text"); const outputText = document.getElementById("output-text");
const outputHtml = document.getElementById("output-html"); const outputHtml = document.getElementById("output-html");
const outputFile = document.getElementById("output-file"); const outputFile = document.getElementById("output-file");
@ -204,11 +212,35 @@ class OutputWaiter {
this.manager.controls.hideStaleIndicator(); this.manager.controls.hideStaleIndicator();
} }
if (output.status === "inactive") { this.manager.recipe.updateBreakpointIndicator(false);
// An output is inactive when it has been created but has not been baked at all
// show a blank here if (output.status === "pending" || output.status === "baking") {
if (inputNum === this.getActiveTab()) { // show the loader and the status message if it's being shown
this.toggleLoader(false); // otherwise don't do anything
this.toggleLoader(true);
document.querySelector("#output-loader .loading-msg").textContent = output.statusMessage;
} else if (output.status === "error") {
// style the tab if it's being shown
// run app.handleError()
this.toggleLoader(false);
outputText.style.display = "block";
outputHtml.style.display = "none";
outputFile.style.display = "none";
outputHighlighter.display = "none";
inputHighlighter.display = "none";
outputText.value = output.error;
outputHtml.innerHTML = "";
this.manager.recipe.updateBreakpointIndicator(output.progress);
} else if (output.status === "baked" || output.status === "inactive") {
this.displayTabInfo(inputNum);
this.toggleLoader(false);
this.closeFile();
let scriptElements, lines, length;
if (output.data === null) {
outputText.style.display = "block"; outputText.style.display = "block";
outputHtml.style.display = "none"; outputHtml.style.display = "none";
outputFile.style.display = "none"; outputFile.style.display = "none";
@ -218,83 +250,63 @@ class OutputWaiter {
outputText.value = ""; outputText.value = "";
outputHtml.innerHTML = ""; outputHtml.innerHTML = "";
} lines = 0;
} else if (output.status === "pending" || output.status === "baking") { length = 0;
// show the loader and the status message if it's being shown return;
// otherwise don't do anything
if (inputNum === this.getActiveTab()) {
this.toggleLoader(true);
document.querySelector("#output-loader .loading-msg").textContent = output.statusMessage;
} }
} else if (output.status === "error") { switch (output.data.type) {
// style the tab if it's being shown case "html":
// run app.handleError() outputText.style.display = "none";
if (inputNum === this.getActiveTab()) { outputHtml.style.display = "block";
this.toggleLoader(false); outputFile.style.display = "none";
} outputHighlighter.style.display = "none";
} else if (output.status === "baked") { inputHighlighter.style.display = "none";
// Display the output if it's the active tab
this.displayTabInfo(inputNum);
if (inputNum === this.getActiveTab()) {
this.toggleLoader(false);
this.closeFile();
let scriptElements, lines, length;
const duration = output.data.duration;
switch (output.data.type) { outputText.value = "";
case "html": outputHtml.innerHTML = output.data.result;
outputText.style.display = "none";
outputHtml.style.display = "block";
outputFile.style.display = "none";
outputHighlighter.style.display = "none";
inputHighlighter.style.display = "none";
outputText.value = ""; // Execute script sections
outputHtml.innerHTML = output.data.result; scriptElements = outputHtml.querySelectorAll("script");
for (let i = 0; i < scriptElements.length; i++) {
// Execute script sections try {
scriptElements = outputHtml.querySelectorAll("script"); eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
for (let i = 0; i < scriptElements.length; i++) { } catch (err) {
try { log.error(err);
eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
} catch (err) {
log.error(err);
}
} }
length = output.data.dish.value.length; }
length = output.data.dish.value.length;
break; break;
case "ArrayBuffer": case "ArrayBuffer":
outputText.style.display = "block"; outputText.style.display = "block";
outputHtml.style.display = "none"; outputHtml.style.display = "none";
outputHighlighter.display = "none"; outputHighlighter.display = "none";
inputHighlighter.display = "none"; inputHighlighter.display = "none";
outputText.value = ""; outputText.value = "";
outputHtml.innerHTML = ""; outputHtml.innerHTML = "";
length = output.data.result.length; length = output.data.result.length;
this.setFile(output.data.result); this.setFile(output.data.result);
break; break;
case "string": case "string":
default: default:
outputText.style.display = "block"; outputText.style.display = "block";
outputHtml.style.display = "none"; outputHtml.style.display = "none";
outputFile.style.display = "none"; outputFile.style.display = "none";
outputHighlighter.display = "block"; outputHighlighter.display = "block";
inputHighlighter.display = "block"; inputHighlighter.display = "block";
outputText.value = Utils.printable(output.data.result, true); outputText.value = Utils.printable(output.data.result, true);
outputHtml.innerHTML = ""; outputHtml.innerHTML = "";
lines = output.data.result.count("\n") + 1; lines = output.data.result.count("\n") + 1;
length = output.data.result.length; length = output.data.result.length;
break; break;
}
this.setOutputInfo(length, lines, duration);
this.backgroundMagic();
} }
this.setOutputInfo(length, lines, output.data.duration);
this.backgroundMagic();
} }
} }
@ -555,7 +567,7 @@ class OutputWaiter {
*/ */
goToTab() { goToTab() {
const tabNum = parseInt(window.prompt("Enter tab number:", this.getActiveTab().toString()), 10); const tabNum = parseInt(window.prompt("Enter tab number:", this.getActiveTab().toString()), 10);
if (this.getOutputIndex(tabNum) >= 0) { if (this.getOutput(tabNum) !== undefined) {
this.changeTab(tabNum, this.app.options.syncTabs); this.changeTab(tabNum, this.app.options.syncTabs);
} }
} }

View file

@ -137,24 +137,13 @@ class WorkerWaiter {
case "bakeComplete": case "bakeComplete":
log.debug(`Bake ${inputNum} complete.`); log.debug(`Bake ${inputNum} complete.`);
this.updateOutput(r.data, r.data.inputNum); this.updateOutput(r.data, r.data.inputNum);
this.workerFinished(currentWorker);
if (this.inputs.length > 0) {
this.bakeNextInput(this.chefWorkers.indexOf(currentWorker));
} else {
// The ChefWorker is no longer needed
log.debug("No more inputs to bake. Closing ChefWorker.");
currentWorker.active = false;
this.removeChefWorker(currentWorker);
const progress = this.getBakeProgress();
if (progress.total === progress.baked) {
this.bakingComplete();
}
}
break; break;
case "BakeError": case "bakeError":
this.manager.output.updateOutputError(r.data, inputNum); if (!r.data.hasOwnProperty("progress")) this.app.handleError(r.data.error);
this.manager.output.updateOutputError(r.data.error, inputNum, r.data.progress);
this.workerFinished(currentWorker);
// do more here // do more here
break; break;
case "dishReturned": case "dishReturned":
@ -196,7 +185,6 @@ class WorkerWaiter {
* @param {number} inputNum * @param {number} inputNum
*/ */
updateOutput(data, inputNum) { updateOutput(data, inputNum) {
this.manager.output.updateOutputValue(data, inputNum); this.manager.output.updateOutputValue(data, inputNum);
this.manager.output.updateOutputStatus("baked", inputNum); this.manager.output.updateOutputStatus("baked", inputNum);
@ -242,14 +230,40 @@ class WorkerWaiter {
*/ */
cancelBake() { cancelBake() {
for (let i = this.chefWorkers.length - 1; i >= 0; i--) { for (let i = this.chefWorkers.length - 1; i >= 0; i--) {
const inputNum = this.chefWorkers[i].inputNum;
this.removeChefWorker(this.chefWorkers[i]); this.removeChefWorker(this.chefWorkers[i]);
this.manager.output.updateOutputStatus("inactive", inputNum);
} }
this.setBakingStatus(false); this.setBakingStatus(false);
for (let i = 0; i < this.inputs.length; i++) {
this.manager.output.updateOutputStatus("inactive", this.inputs[i].inputNum);
}
this.inputs = []; this.inputs = [];
this.totalOutputs = 0; this.totalOutputs = 0;
this.manager.controls.showStaleIndicator(); this.manager.controls.showStaleIndicator();
} }
/**
* Handle a worker completing baking
*/
workerFinished(workerObj) {
if (this.inputs.length > 0) {
this.bakeNextInput(this.chefWorkers.indexOf(workerObj));
} else {
// The ChefWorker is no longer needed
log.debug("No more inputs to bake. Closing ChefWorker.");
workerObj.active = false;
this.removeChefWorker(workerObj);
const progress = this.getBakeProgress();
if (progress.total === progress.baked) {
this.bakingComplete();
}
}
}
/** /**
* Handler for completed bakes * Handler for completed bakes
*/ */
@ -371,6 +385,7 @@ class WorkerWaiter {
*/ */
queueInput(inputData) { queueInput(inputData) {
this.manager.output.updateOutputStatus("pending", inputData.inputNum); this.manager.output.updateOutputStatus("pending", inputData.inputNum);
this.manager.output.updateOutputMessage(`Input ${inputData.inputNum} has not been baked yet.`);
this.totalOutputs++; this.totalOutputs++;
this.inputs.push(inputData); this.inputs.push(inputData);