Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(581)

Unified Diff: ui/file_manager/image_loader/piex_loader.js

Issue 2714413004: Unload Piex NaCl module when idling. (Closed)
Patch Set: Fix issues + add comments. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/file_manager/image_loader/piex_loader.js
diff --git a/ui/file_manager/image_loader/piex_loader.js b/ui/file_manager/image_loader/piex_loader.js
index d4bb42e080a5ccdb7c9ce4c7f00dfe09a8cceb79..e667692f3e3d13394d321ad5c7a689b1cd7a1291 100644
--- a/ui/file_manager/image_loader/piex_loader.js
+++ b/ui/file_manager/image_loader/piex_loader.js
@@ -54,32 +54,130 @@ function PiexLoaderResponse(data) {
}
/**
+ * Creates a PiexLoader for loading RAW files using a Piex NaCl module.
+ *
+ * All of the arguments are optional and used for tests only. If not passed,
+ * then default implementations and values will be used.
+ *
+ * @param {function()=} opt_createModule Creates a NaCl module.
+ * @param {function(!Element)=} opt_destroyModule Destroys a NaCl module.
+ * @param {number=} opt_idleTimeout Idle timeout to destroy NaCl module.
* @constructor
* @struct
*/
-function PiexLoader() {
+function PiexLoader(opt_createModule, opt_destroyModule, opt_idleTimeout) {
/**
- * @private {!Element}
- * @const
+ * @private {function():!Element}
+ */
+ this.createModule_ = opt_createModule || this.defaultCreateModule_.bind(this);
+
+ /**
+ * @private {function():!Element}
+ */
+ this.destroyModule_ =
+ opt_destroyModule || this.defaultDestroyModule_.bind(this);
+
+ this.idleTimeoutMs_ = opt_idleTimeout !== undefined ?
+ opt_idleTimeout :
+ PiexLoader.DEFAULT_IDLE_TIMEOUT_MS;
+
+ /**
+ * @private {Element}
+ */
+ this.naclModule_ = null;
+
+ /**
+ * @private {Element}
+ */
+ this.containerElement_ = null;
+
+ /**
+ * @private {number}
*/
- this.naclModule_ = document.createElement('embed');
+ this.unloadTimer_ = 0;
/**
- * @private {!Promise<boolean>}
+ * @private {Promise<boolean>}
+ */
+ this.naclPromise_ = null;
+
+ /**
+ * @private {?function(boolean)}
+ */
+ this.naclPromiseFulfill_ = null;
+
+ /**
+ * @private {?function(string=)}
+ */
+ this.naclPromiseReject_ = null;
+
+ /**
+ * @private {!Object<number, ?PiexRequestCallbacks>}
* @const
*/
+ this.requests_ = {};
+
+ /**
+ * @private {number}
+ */
+ this.requestIdCount_ = 0;
+
+ // Bound function so the listeners can be unregistered.
+ this.onNaclLoadBound_ = this.onNaclLoad_.bind(this);
+ this.onNaclMessageBound_ = this.onNaclMessage_.bind(this);
+ this.onNaclErrorBound_ = this.onNaclError_.bind(this);
+ this.onNaclCrashBound_ = this.onNaclCrash_.bind(this);
+}
+
+/**
+ * Idling time before the NaCl module is unloaded. This lets the image loader
+ * extension close when inactive.
+ *
+ * @const {number}
+ */
+PiexLoader.DEFAULT_IDLE_TIMEOUT_MS = 3000; // 3 seconds.
+
+/**
+ * Creates a NaCl module element.
+ *
+ * Do not call directly. Use this.loadModule_ instead to support
+ * tests.
+ *
+ * @return {!Element}
+ * @private
+ */
+PiexLoader.prototype.defaultCreateModule_ = function() {
+ var embed = document.createElement('embed');
+ embed.setAttribute('type', 'application/x-pnacl');
+ // The extension nmf is not allowed to load. We uses .nmf.js instead.
+ embed.setAttribute('src', '/piex/piex.nmf.txt');
+ embed.width = 0;
+ embed.height = 0;
+ return embed;
+};
+
+PiexLoader.prototype.defaultDestroyModule_ = function(module) {
+ // The module is destroyed by removing it from DOM in loadNaclModule_().
+};
+
+/**
+ * @return {!Promise<boolean>}
+ * @private
+ */
+PiexLoader.prototype.loadNaclModule_ = function() {
+ if (this.naclPromise_) {
+ return this.naclPromise_;
+ }
+
this.naclPromise_ = new Promise(function(fulfill) {
chrome.fileManagerPrivate.isPiexLoaderEnabled(fulfill);
}).then(function(enabled) {
if (!enabled)
return false;
return new Promise(function(fulfill, reject) {
- var embed = this.naclModule_;
- embed.setAttribute('type', 'application/x-pnacl');
- // The extension nmf is not allowed to load. We uses .nmf.js instead.
- embed.setAttribute('src', '/piex/piex.nmf.txt');
- embed.width = 0;
- embed.height = 0;
+ this.naclPromiseFulfill_ = fulfill;
+ this.naclPromiseReject_ = reject;
+ this.naclModule_ = this.createModule_();
// The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
// and a 'message' event listener attached. This wrapping method is used
@@ -87,18 +185,14 @@ function PiexLoader() {
// element to ensure that the listeners are active before the NaCl module
// 'load' event fires.
var listenerContainer = document.createElement('div');
- listenerContainer.appendChild(embed);
+ listenerContainer.appendChild(this.naclModule_);
+ listenerContainer.addEventListener('load', this.onNaclLoadBound_, true);
listenerContainer.addEventListener(
- 'load', fulfill.bind(null, true), true);
- listenerContainer.addEventListener(
- 'message', this.onMessage_.bind(this), true);
- listenerContainer.addEventListener('error', function() {
- reject(embed['lastError']);
- }.bind(this), true);
- listenerContainer.addEventListener('crash', function() {
- reject('PiexLoader crashed.');
- }.bind(this), true);
+ 'message', this.onNaclMessageBound_, true);
+ listenerContainer.addEventListener('error', this.onNaclErrorBound_, true);
+ listenerContainer.addEventListener('crash', this.onNaclCrashBound_, true);
listenerContainer.style.height = '0px';
+ this.containerElement_ = listenerContainer;
document.body.appendChild(listenerContainer);
}.bind(this));
}.bind(this)).catch(function (error) {
@@ -106,31 +200,108 @@ function PiexLoader() {
return false;
});
- /**
- * @private {!Object<number, PiexRequestCallbacks>}
- * @const
- */
- this.requests_ = {};
+ return this.naclPromise_;
+};
- /**
- * @private {number}
- */
- this.requestIdCount_ = 0;
-}
+/**
+ * @private
+ */
+PiexLoader.prototype.unloadNaclModule_ = function() {
+ this.containerElement_.removeEventListener('load', this.onNaclLoadBound_);
+ this.containerElement_.removeEventListener(
+ 'message', this.onNaclMessageBound_);
+ this.containerElement_.removeEventListener('error', this.onNaclErrorBound_);
+ this.containerElement_.removeEventListener('crash', this.onNaclCrashBound_);
+ this.containerElement_.parentNode.removeChild(this.containerElement_);
+ this.containerElement_ = null;
+
+ this.destroyModule_();
+ this.naclModule_ = null;
+ this.naclPromise_ = null;
+ this.naclPromiseFulfill_ = null;
+ this.naclPromiseReject_ = null;
+};
/**
* @param {Event} event
* @private
*/
-PiexLoader.prototype.onMessage_ = function(event) {
+PiexLoader.prototype.onNaclLoad_ = function(event) {
+ console.assert(this.naclPromiseFulfill_);
+ this.naclPromiseFulfill_(true);
+};
+
+/**
+ * @param {Event} event
+ * @private
+ */
+PiexLoader.prototype.onNaclMessage_ = function(event) {
var id = event.data.id;
if (!event.data.error) {
var response = new PiexLoaderResponse(event.data);
+ console.assert(this.requests_[id]);
this.requests_[id].fulfill(response);
} else {
+ console.assert(this.requests_[id]);
this.requests_[id].reject(event.data.error);
}
delete this.requests_[id];
+ if (Object.keys(this.requests_).length === 0)
+ this.scheduleUnloadOnIdle_();
+};
+
+/**
+ * @param {Event} event
+ * @private
+ */
+PiexLoader.prototype.onNaclError_ = function(event) {
+ console.assert(this.naclPromiseReject_);
+ this.naclPromiseReject_(this.naclModule_['lastError']);
+};
+
+/**
+ * @param {Event} event
+ * @private
+ */
+PiexLoader.prototype.onNaclCrash_ = function(event) {
+ console.assert(this.naclPromiseReject_);
+ this.naclPromiseReject_('PiexLoader crashed.');
+};
+
+/**
+ * Schedules unloading the NaCl module after IDLE_TIMEOUT_MS passes.
+ * @private
+ */
+PiexLoader.prototype.scheduleUnloadOnIdle_ = function() {
+ if (this.unloadTimer_)
+ clearTimeout(this.unloadTimer_);
+ this.unloadTimer_ =
+ setTimeout(this.onIdleTimeout_.bind(this), this.idleTimeoutMs_);
+};
+
+/**
+ * @private
+ */
+PiexLoader.prototype.onIdleTimeout_ = function() {
+ this.unloadNaclModule_();
+};
+
+/**
+ * Simulates time passed required to fire the closure enqueued with setTimeout.
+ *
+ * Note, that if there is no active timer set with setTimeout earlier, then
+ * nothing will happen.
+ *
+ * This method is used to avoid waiting for DEFAULT_IDLE_TIMEOUT_MS in tests.
+ * Also, it allows to avoid flakyness by effectively removing any dependency
+ * on execution speed of the test (tests set the timeout to a very large value
+ * and only rely on this method to simulate passed time).
+ */
+PiexLoader.prototype.simulateIdleTimeoutPassedForTests = function() {
+ if (this.unloadTimer_) {
+ clearTimeout(this.unloadTimer_);
+ this.onIdleTimeout_();
+ }
};
/**
@@ -139,20 +310,29 @@ PiexLoader.prototype.onMessage_ = function(event) {
* @return {!Promise<!PiexLoaderResponse>}
*/
PiexLoader.prototype.load = function(url) {
- return this.naclPromise_.then(function(loaded) {
+ var requestId = this.requestIdCount_++;
+
+ if (this.unloadTimer_) {
+ clearTimeout(this.unloadTimer_);
+ this.unloadTimer_ = 0;
+ }
+
+ // Prevents unloading the NaCl module during handling the promises below.
+ this.requests_[requestId] = null;
+
+ return this.loadNaclModule_().then(function(loaded) {
if (!loaded)
return Promise.reject('Piex is not loaded');
- var message = {
- id: this.requestIdCount_++,
- name: 'loadThumbnail',
- url: url
- };
+ var message = {id: requestId, name: 'loadThumbnail', url: url};
this.naclModule_.postMessage(message);
return new Promise(function(fulfill, reject) {
- this.requests_[message.id] = {fulfill: fulfill, reject: reject};
- }.bind(this)).catch(function(error) {
- console.error('PiexLoaderError: ', error);
- return Promise.reject(error);
- });
+ delete this.requests_[requestId];
+ this.requests_[message.id] = {fulfill: fulfill, reject: reject};
+ }.bind(this))
+ .catch(function(error) {
+ delete this.requests_[requestId];
+ console.error('PiexLoaderError: ', error);
+ return Promise.reject(error);
+ });
}.bind(this));
};
« no previous file with comments | « chrome/browser/chromeos/file_manager/image_loader_jstest.cc ('k') | ui/file_manager/image_loader/piex_loader_unittest.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698