Commit d2972196 authored by Noel Gordon's avatar Noel Gordon Committed by Commit Bot

[piexwasm] Make callers handle Piex Module failure

piex-wasm assumes chrome.runtime.reload can be used to reload the page
on Module failures. Other environments (normal web pages, web workers,
NodeJS, etc) obviously can't use chrome.runtime.reload.

Remove the chrome.runtime.reload assumption: change the client code to
provide the failure handler to piex-wasm. Change image_request_task.js
RAW image client code: pass in the chrome.runtime.reload handler.

While here, add or adjust comments to image_request_task.js and change
the direct loading case to arrow function. No change in behavior.

Bug: 1132695
No-try: true
Change-Id: I6a2b641cdc036dd208f4d64deb4f0b7fdff0835f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2432705
Commit-Queue: Noel Gordon <noel@chromium.org>
Reviewed-by: default avatarTrent Apted <tapted@chromium.org>
Cr-Commit-Position: refs/heads/master@{#811085}
parent 3f88f25e
...@@ -287,13 +287,16 @@ ImageRequestTask.prototype.getExternalThumbnail = function(url, onFailure) { ...@@ -287,13 +287,16 @@ ImageRequestTask.prototype.getExternalThumbnail = function(url, onFailure) {
}; };
/** /**
* Downloads an image directly or for remote resources using the XmlHttpRequest. * Loads |this.image_| with the |this.request_.url| source or the thumbnail
* image of the source.
* *
* @param {function()} onSuccess Success callback. * @param {function()} onSuccess Success callback.
* @param {function()} onFailure Failure callback. * @param {function()} onFailure Failure callback.
* @private * @private
*/ */
ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) { ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
// Load methods below set |this.image_.src|. Call revokeObjectURL(src) to
// release resources if the image src was created with createObjectURL().
this.image_.onload = function() { this.image_.onload = function() {
URL.revokeObjectURL(this.image_.src); URL.revokeObjectURL(this.image_.src);
onSuccess(); onSuccess();
...@@ -303,18 +306,22 @@ ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) { ...@@ -303,18 +306,22 @@ ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
onFailure(); onFailure();
}.bind(this); }.bind(this);
// Download data urls directly since they are not supported by XmlHttpRequest. // Load dataURL sources directly.
const dataUrlMatches = this.request_.url.match(/^data:([^,;]*)[,;]/); const dataUrlMatches = this.request_.url.match(/^data:([^,;]*)[,;]/);
if (dataUrlMatches) { if (dataUrlMatches) {
this.image_.src = this.request_.url; this.image_.src = this.request_.url;
this.contentType_ = dataUrlMatches[1]; this.contentType_ = dataUrlMatches[1];
return; return;
} }
// Load Drive source thumbnail.
const drivefsUrlMatches = this.request_.url.match(/^drivefs:(.*)/); const drivefsUrlMatches = this.request_.url.match(/^drivefs:(.*)/);
if (drivefsUrlMatches) { if (drivefsUrlMatches) {
this.getExternalThumbnail(drivefsUrlMatches[1], onFailure); this.getExternalThumbnail(drivefsUrlMatches[1], onFailure);
return; return;
} }
// Load PDF source thumbnail.
if (this.request_.url.endsWith('.pdf')) { if (this.request_.url.endsWith('.pdf')) {
this.getExternalThumbnail(this.request_.url, onFailure); this.getExternalThumbnail(this.request_.url, onFailure);
return; return;
...@@ -322,9 +329,9 @@ ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) { ...@@ -322,9 +329,9 @@ ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
const fileType = FileType.getTypeForName(this.request_.url); const fileType = FileType.getTypeForName(this.request_.url);
// Load RAW images by using Piex loader instead of XHR. // Load RAW image source thumbnail.
if (fileType.type === 'raw') { if (fileType.type === 'raw') {
this.piexLoader_.load(this.request_.url) this.piexLoader_.load(this.request_.url, chrome.runtime.reload)
.then( .then(
function(data) { function(data) {
this.request_.orientation = data.orientation; this.request_.orientation = data.orientation;
...@@ -335,13 +342,13 @@ ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) { ...@@ -335,13 +342,13 @@ ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
this.image_.src = URL.createObjectURL(blob); this.image_.src = URL.createObjectURL(blob);
}.bind(this), }.bind(this),
function() { function() {
// The error has already been logged in PiexLoader. // PiexLoader calls console.error on errors.
onFailure(); onFailure();
}); });
return; return;
} }
// Load video thumbnails by using video tag instead of XHR. // Load video source thumbnail.
if (fileType.type === 'video') { if (fileType.type === 'video') {
this.createVideoThumbnailUrl_(this.request_.url) this.createVideoThumbnailUrl_(this.request_.url)
.then(function(url) { .then(function(url) {
...@@ -354,16 +361,11 @@ ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) { ...@@ -354,16 +361,11 @@ ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
return; return;
} }
// Fetch the image via XHR and parse it. // Load the source directly.
const parseImage = function(contentType, blob) { this.load(this.request_.url, (contentType, blob) => {
if (contentType) { this.image_.src = blob ? URL.createObjectURL(blob) : '!';
this.contentType_ = contentType; this.contentType_ = contentType || null;
} }, onFailure);
this.image_.src = URL.createObjectURL(blob);
}.bind(this);
// Request raw data via XHR.
this.load(this.request_.url, parseImage, onFailure);
}; };
/** /**
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
console.log('[PiexLoader] wasm mode loaded'); console.log('[PiexLoader] loaded');
/** /**
* Declares the piex-wasm Module interface. The Module has many interfaces * Declares the piex-wasm Module interface. The Module has many interfaces
...@@ -25,33 +25,33 @@ let PiexWasmModule; ...@@ -25,33 +25,33 @@ let PiexWasmModule;
const PiexModule = window['Module'] || {}; const PiexModule = window['Module'] || {};
/** /**
* Set true only if the wasm Module.onAbort() handler is called. * Set true only if the Module.onAbort() handler is called.
* @type {boolean} * @type {boolean}
*/ */
let wasmFailed = false; let piexFailed = false;
/** /**
* Installs an (Emscripten) wasm Module.onAbort handler, that records that * Installs an (Emscripten) Module.onAbort handler. Record that the Module
* the Module has failed and re-throws the error. * has failed and re-throw the error.
* @throws {!Error|string} * @throws {!Error|string}
*/ */
PiexModule.onAbort = (error) => { PiexModule.onAbort = (error) => {
wasmFailed = true; piexFailed = true;
throw error; throw error;
}; };
/** /**
* Module failure recovery: if wasmFailed is set via onAbort due to OOM in * Module failure recovery: if piexFailed is set via onAbort due to OOM in
* the C++ for example, or the Module failed to load or call run, then the * the C++ for example, or the Module failed to load or call run, then the
* wasm Module is in a broken, non-functional state. * Module is in a broken, non-functional state.
* *
* Re-loading the page is the only reliable way to attempt to recover from * Loading the entire page is the only reliable way to recover from broken
* broken Module state. * Module state. Log the error, and return true to tell caller to initiate
* failure recovery steps.
*/ */
function wasmModuleFailed() { function piexModuleFailed() {
if (wasmFailed || !PiexModule.calledRun) { if (piexFailed || !PiexModule.calledRun) {
console.error('[PiexLoader] wasmModuleFailed'); console.error('[PiexLoader] piex wasm module failed');
setTimeout(chrome.runtime.reload, 0);
return true; return true;
} }
} }
...@@ -106,15 +106,6 @@ function PiexLoaderResponse(data) { ...@@ -106,15 +106,6 @@ function PiexLoaderResponse(data) {
this.ifd = data.ifd || null; this.ifd = data.ifd || null;
} }
/**
* Creates a PiexLoader for reading RAW image file information.
* @constructor
* @struct
*/
function PiexLoader() {
// TODO(crbug.com/1039141): make this an ES6 class.
}
/** /**
* Resolves the file entry associated with DOM filesystem |url| and returns * Resolves the file entry associated with DOM filesystem |url| and returns
* the file content in an ArrayBuffer. * the file content in an ArrayBuffer.
...@@ -468,16 +459,32 @@ class ImageBuffer { ...@@ -468,16 +459,32 @@ class ImageBuffer {
} }
/** /**
* Starts to load RAW image. * Creates a PiexLoader.
* @constructor
* @struct
*/
function PiexLoader() {}
/**
* Loads a RAW image. Returns the image metadata and the image thumbnail in a
* PiexLoaderResponse.
*
* piexModuleFailed() returns true if the Module is in an unrecoverable error
* state. This is rare, but possible, and the only reliable way to recover is
* to reload the page. Callback |onPiexModuleFailed| is used to indicate that
* the caller should initiate failure recovery steps.
*
* @param {string} url * @param {string} url
* @param {!function()} onPiexModuleFailed
* @return {!Promise<!PiexLoaderResponse>} * @return {!Promise<!PiexLoaderResponse>}
*/ */
PiexLoader.prototype.load = function(url) { PiexLoader.prototype.load = function(url, onPiexModuleFailed) {
let imageBuffer; let imageBuffer;
return readFromFileSystem(url) return readFromFileSystem(url)
.then((buffer) => { .then((buffer) => {
if (wasmModuleFailed() === true) { if (piexModuleFailed() === true) {
// Just reject here: handle in the .catch() clause below.
return Promise.reject('piex wasm module failed'); return Promise.reject('piex wasm module failed');
} }
imageBuffer = new ImageBuffer(buffer); imageBuffer = new ImageBuffer(buffer);
...@@ -488,7 +495,8 @@ PiexLoader.prototype.load = function(url) { ...@@ -488,7 +495,8 @@ PiexLoader.prototype.load = function(url) {
return new PiexLoaderResponse(imageBuffer.preview(result)); return new PiexLoaderResponse(imageBuffer.preview(result));
}) })
.catch((error) => { .catch((error) => {
if (wasmModuleFailed() === true) { if (piexModuleFailed() === true) {
setTimeout(onPiexModuleFailed, 0);
return Promise.reject('piex wasm module failed'); return Promise.reject('piex wasm module failed');
} }
imageBuffer && imageBuffer.close(); imageBuffer && imageBuffer.close();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment