diff options
author | yoshiki@chromium.org <yoshiki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-22 12:11:57 +0000 |
---|---|---|
committer | yoshiki@chromium.org <yoshiki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-22 12:11:57 +0000 |
commit | 9c9241c0d4146c879c59ea2889feaa0b69652f24 (patch) | |
tree | d16756c7ec74db3d74345a81f0b89f15a56f4c76 /chrome | |
parent | e08b9fec40842b96974fb44264c6d4ff32189529 (diff) | |
download | chromium_src-9c9241c0d4146c879c59ea2889feaa0b69652f24.zip chromium_src-9c9241c0d4146c879c59ea2889feaa0b69652f24.tar.gz chromium_src-9c9241c0d4146c879c59ea2889feaa0b69652f24.tar.bz2 |
Move Image Loader extension files to ui/file_manager
BUG=345171
TEST=manually tested
R=mtomasz@chromium.org
Review URL: https://codereview.chromium.org/246363004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@265235 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
12 files changed, 2 insertions, 1662 deletions
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 93e0571..5a9ded1 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -339,9 +339,6 @@ <if expr="chromeos"> <include name="IDR_FILEMANAGER_MANIFEST" file="resources\file_manager\manifest.json" type="BINDATA" /> </if> - <if expr="image_loader_extension"> - <include name="IDR_IMAGE_LOADER_MANIFEST" file="resources\image_loader\manifest.json" type="BINDATA" /> - </if> <if expr="chromeos"> <include name="IDR_DEMO_APP_MANIFEST" file="resources\chromeos\demo_app\manifest.json" type="BINDATA" /> <include name="IDR_WALLPAPERMANAGER_MANIFEST" file="resources\chromeos\wallpaper_manager\manifest.json" type="BINDATA" /> diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd index 30735ed..859e937 100644 --- a/chrome/browser/resources/component_extension_resources.grd +++ b/chrome/browser/resources/component_extension_resources.grd @@ -134,14 +134,6 @@ <include name="IDR_FILE_MANAGER_IMG_GALLERY_2X_CURSOR_SWNE" file="file_manager/foreground/images/gallery/2x/cursor_swne.png" type="BINDATA" /> <include name="IDR_FILE_MANAGER_IMG_GALLERY_2X_CURSOR_UPDOWN" file="file_manager/foreground/images/gallery/2x/cursor_updown.png" type="BINDATA" /> </if> - <if expr="image_loader_extension"> - <include name="IDR_IMAGE_LOADER_IMAGE_LOADER_JS" file="image_loader/image_loader.js" type="BINDATA" /> - <include name="IDR_IMAGE_LOADER_CACHE_JS" file="image_loader/cache.js" type="BINDATA" /> - <include name="IDR_IMAGE_LOADER_WORKER_JS" file="image_loader/worker.js" type="BINDATA" /> - <include name="IDR_IMAGE_LOADER_REQUEST_JS" file="image_loader/request.js" type="BINDATA" /> - <include name="IDR_IMAGE_LOADER_BACKGROUND_JS" file="image_loader/background.js" type="BINDATA" /> - <include name="IDR_IMAGE_LOADER_CLIENT_JS" file="image_loader/image_loader_client.js" type="BINDATA" /> - </if> <if expr="enable_google_now"> <include name="IDR_GOOGLE_NOW_BACKGROUND_JS" file="google_now/background.js" type="BINDATA" /> <include name="IDR_GOOGLE_NOW_CARDS_JS" file="google_now/cards.js" type="BINDATA" /> diff --git a/chrome/browser/resources/file_manager/foreground/js/main_scripts.js b/chrome/browser/resources/file_manager/foreground/js/main_scripts.js index 9cd01ee..6e0044a 100644 --- a/chrome/browser/resources/file_manager/foreground/js/main_scripts.js +++ b/chrome/browser/resources/file_manager/foreground/js/main_scripts.js @@ -24,7 +24,7 @@ // //so we want to parse it as early as possible. //<include src="metrics.js"/> // -//<include src="../../../image_loader/image_loader_client.js"/> +//<include src="../../../../../../ui/file_manager/image_loader/image_loader_client.js"/> // //<include src="../../../../../../ui/webui/resources/js/load_time_data.js"/> //<include src="../../../../../../ui/webui/resources/js/cr.js"/> diff --git a/chrome/browser/resources/file_manager/foreground/js/photo/gallery_scripts.js b/chrome/browser/resources/file_manager/foreground/js/photo/gallery_scripts.js index b3eaca2..ec7f796 100644 --- a/chrome/browser/resources/file_manager/foreground/js/photo/gallery_scripts.js +++ b/chrome/browser/resources/file_manager/foreground/js/photo/gallery_scripts.js @@ -10,7 +10,7 @@ //<include src="../metrics.js"> -//<include src="../../../../image_loader/image_loader_client.js"/> +//<include src="../../../../../../../ui/file_manager/image_loader/image_loader_client.js"/> //<include src="../../../../../../../ui/webui/resources/js/cr.js"> //<include src="../../../../../../../ui/webui/resources/js/event_tracker.js"> diff --git a/chrome/browser/resources/image_loader/OWNERS b/chrome/browser/resources/image_loader/OWNERS deleted file mode 100644 index 4eab6e4..0000000 --- a/chrome/browser/resources/image_loader/OWNERS +++ /dev/null @@ -1 +0,0 @@ -mtomasz@chromium.org diff --git a/chrome/browser/resources/image_loader/background.js b/chrome/browser/resources/image_loader/background.js deleted file mode 100644 index 028a082..0000000 --- a/chrome/browser/resources/image_loader/background.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -// Load the extension. -ImageLoader.getInstance(); diff --git a/chrome/browser/resources/image_loader/cache.js b/chrome/browser/resources/image_loader/cache.js deleted file mode 100644 index dde287c..0000000 --- a/chrome/browser/resources/image_loader/cache.js +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Persistent cache storing images in an indexed database on the hard disk. - * @constructor - */ -function Cache() { - /** - * IndexedDB database handle. - * @type {IDBDatabase} - * @private - */ - this.db_ = null; -} - -/** - * Cache database name. - * @type {string} - * @const - */ -Cache.DB_NAME = 'image-loader'; - -/** - * Cache database version. - * @type {number} - * @const - */ -Cache.DB_VERSION = 11; - -/** - * Memory limit for images data in bytes. - * - * @const - * @type {number} - */ -Cache.MEMORY_LIMIT = 250 * 1024 * 1024; // 250 MB. - -/** - * Minimal amount of memory freed per eviction. Used to limit number of - * evictions which are expensive. - * - * @const - * @type {number} - */ -Cache.EVICTION_CHUNK_SIZE = 50 * 1024 * 1024; // 50 MB. - -/** - * Creates a cache key. - * - * @param {Object} request Request options. - * @return {string} Cache key. - */ -Cache.createKey = function(request) { - return JSON.stringify({url: request.url, - scale: request.scale, - width: request.width, - height: request.height, - maxWidth: request.maxWidth, - maxHeight: request.maxHeight}); -}; - -/** - * Initializes the cache database. - * @param {function()} callback Completion callback. - */ -Cache.prototype.initialize = function(callback) { - // Establish a connection to the database or (re)create it if not available - // or not up to date. After changing the database's schema, increment - // Cache.DB_VERSION to force database recreating. - var openRequest = window.webkitIndexedDB.open(Cache.DB_NAME, - Cache.DB_VERSION); - - openRequest.onsuccess = function(e) { - this.db_ = e.target.result; - callback(); - }.bind(this); - - openRequest.onerror = callback; - - openRequest.onupgradeneeded = function(e) { - console.info('Cache database creating or upgrading.'); - var db = e.target.result; - if (db.objectStoreNames.contains('metadata')) - db.deleteObjectStore('metadata'); - if (db.objectStoreNames.contains('data')) - db.deleteObjectStore('data'); - if (db.objectStoreNames.contains('settings')) - db.deleteObjectStore('settings'); - db.createObjectStore('metadata', {keyPath: 'key'}); - db.createObjectStore('data', {keyPath: 'key'}); - db.createObjectStore('settings', {keyPath: 'key'}); - }; -}; - -/** - * Sets size of the cache. - * - * @param {number} size Size in bytes. - * @param {IDBTransaction=} opt_transaction Transaction to be reused. If not - * provided, then a new one is created. - * @private - */ -Cache.prototype.setCacheSize_ = function(size, opt_transaction) { - var transaction = opt_transaction || - this.db_.transaction(['settings'], 'readwrite'); - var settingsStore = transaction.objectStore('settings'); - - settingsStore.put({key: 'size', value: size}); // Update asynchronously. -}; - -/** - * Fetches current size of the cache. - * - * @param {function(number)} onSuccess Callback to return the size. - * @param {function()} onFailure Failure callback. - * @param {IDBTransaction=} opt_transaction Transaction to be reused. If not - * provided, then a new one is created. - * @private - */ -Cache.prototype.fetchCacheSize_ = function( - onSuccess, onFailure, opt_transaction) { - var transaction = opt_transaction || - this.db_.transaction(['settings', 'metadata', 'data'], 'readwrite'); - var settingsStore = transaction.objectStore('settings'); - var sizeRequest = settingsStore.get('size'); - - sizeRequest.onsuccess = function(e) { - if (e.target.result) - onSuccess(e.target.result.value); - else - onSuccess(0); - }; - - sizeRequest.onerror = function() { - console.error('Failed to fetch size from the database.'); - onFailure(); - }; -}; - -/** - * Evicts the least used elements in cache to make space for a new image and - * updates size of the cache taking into account the upcoming item. - * - * @param {number} size Requested size. - * @param {function()} onSuccess Success callback. - * @param {function()} onFailure Failure callback. - * @param {IDBTransaction=} opt_transaction Transaction to be reused. If not - * provided, then a new one is created. - * @private - */ -Cache.prototype.evictCache_ = function( - size, onSuccess, onFailure, opt_transaction) { - var transaction = opt_transaction || - this.db_.transaction(['settings', 'metadata', 'data'], 'readwrite'); - - // Check if the requested size is smaller than the cache size. - if (size > Cache.MEMORY_LIMIT) { - onFailure(); - return; - } - - var onCacheSize = function(cacheSize) { - if (size < Cache.MEMORY_LIMIT - cacheSize) { - // Enough space, no need to evict. - this.setCacheSize_(cacheSize + size, transaction); - onSuccess(); - return; - } - - var bytesToEvict = Math.max(size, Cache.EVICTION_CHUNK_SIZE); - - // Fetch all metadata. - var metadataEntries = []; - var metadataStore = transaction.objectStore('metadata'); - var dataStore = transaction.objectStore('data'); - - var onEntriesFetched = function() { - metadataEntries.sort(function(a, b) { - return b.lastLoadTimestamp - a.lastLoadTimestamp; - }); - - var totalEvicted = 0; - while (bytesToEvict > 0) { - var entry = metadataEntries.pop(); - totalEvicted += entry.size; - bytesToEvict -= entry.size; - metadataStore.delete(entry.key); // Remove asynchronously. - dataStore.delete(entry.key); // Remove asynchronously. - } - - this.setCacheSize_(cacheSize - totalEvicted + size, transaction); - }.bind(this); - - metadataStore.openCursor().onsuccess = function(e) { - var cursor = event.target.result; - if (cursor) { - metadataEntries.push(cursor.value); - cursor.continue(); - } else { - onEntriesFetched(); - } - }; - }.bind(this); - - this.fetchCacheSize_(onCacheSize, onFailure, transaction); -}; - -/** - * Saves an image in the cache. - * - * @param {string} key Cache key. - * @param {string} data Image data. - * @param {number} timestamp Last modification timestamp. Used to detect - * if the cache entry becomes out of date. - */ -Cache.prototype.saveImage = function(key, data, timestamp) { - if (!this.db_) { - console.warn('Cache database not available.'); - return; - } - - var onNotFoundInCache = function() { - var metadataEntry = {key: key, - timestamp: timestamp, - size: data.length, - lastLoadTimestamp: Date.now()}; - var dataEntry = {key: key, - data: data}; - - var transaction = this.db_.transaction(['settings', 'metadata', 'data'], - 'readwrite'); - var metadataStore = transaction.objectStore('metadata'); - var dataStore = transaction.objectStore('data'); - - var onCacheEvicted = function() { - metadataStore.put(metadataEntry); // Add asynchronously. - dataStore.put(dataEntry); // Add asynchronously. - }; - - // Make sure there is enough space in the cache. - this.evictCache_(data.length, onCacheEvicted, function() {}, transaction); - }.bind(this); - - // Check if the image is already in cache. If not, then save it to cache. - this.loadImage(key, timestamp, function() {}, onNotFoundInCache); -}; - -/** - * Loads an image from the cache (if available) or returns null. - * - * @param {string} key Cache key. - * @param {number} timestamp Last modification timestamp. If different - * that the one in cache, then the entry will be invalidated. - * @param {function(<string>)} onSuccess Success callback with the image's data. - * @param {function()} onFailure Failure callback. - */ -Cache.prototype.loadImage = function(key, timestamp, onSuccess, onFailure) { - if (!this.db_) { - console.warn('Cache database not available.'); - onFailure(); - return; - } - - var transaction = this.db_.transaction(['settings', 'metadata', 'data'], - 'readwrite'); - var metadataStore = transaction.objectStore('metadata'); - var dataStore = transaction.objectStore('data'); - var metadataRequest = metadataStore.get(key); - var dataRequest = dataStore.get(key); - - var metadataEntry = null; - var metadataReceived = false; - var dataEntry = null; - var dataReceived = false; - - var onPartialSuccess = function() { - // Check if all sub-requests have finished. - if (!metadataReceived || !dataReceived) - return; - - // Check if both entries are available or both unavailable. - if (!!metadataEntry != !!dataEntry) { - console.warn('Incosistent cache database.'); - onFailure(); - return; - } - - // Process the responses. - if (!metadataEntry) { - // The image not found. - onFailure(); - } else if (metadataEntry.timestamp != timestamp) { - // The image is not up to date, so remove it. - this.removeImage(key, function() {}, function() {}, transaction); - onFailure(); - } else { - // The image is available. Update the last load time and return the - // image data. - metadataEntry.lastLoadTimestamp = Date.now(); - metadataStore.put(metadataEntry); // Added asynchronously. - onSuccess(dataEntry.data); - } - }.bind(this); - - metadataRequest.onsuccess = function(e) { - if (e.target.result) - metadataEntry = e.target.result; - metadataReceived = true; - onPartialSuccess(); - }; - - dataRequest.onsuccess = function(e) { - if (e.target.result) - dataEntry = e.target.result; - dataReceived = true; - onPartialSuccess(); - }; - - metadataRequest.onerror = function() { - console.error('Failed to fetch metadata from the database.'); - metadataReceived = true; - onPartialSuccess(); - }; - - dataRequest.onerror = function() { - console.error('Failed to fetch image data from the database.'); - dataReceived = true; - onPartialSuccess(); - }; -}; - -/** - * Removes the image from the cache. - * - * @param {string} key Cache key. - * @param {function()=} opt_onSuccess Success callback. - * @param {function()=} opt_onFailure Failure callback. - * @param {IDBTransaction=} opt_transaction Transaction to be reused. If not - * provided, then a new one is created. - */ -Cache.prototype.removeImage = function( - key, opt_onSuccess, opt_onFailure, opt_transaction) { - if (!this.db_) { - console.warn('Cache database not available.'); - return; - } - - var transaction = opt_transaction || - this.db_.transaction(['settings', 'metadata', 'data'], 'readwrite'); - var metadataStore = transaction.objectStore('metadata'); - var dataStore = transaction.objectStore('data'); - - var cacheSize = null; - var cacheSizeReceived = false; - var metadataEntry = null; - var metadataReceived = false; - - var onPartialSuccess = function() { - if (!cacheSizeReceived || !metadataReceived) - return; - - // If either cache size or metadata entry is not available, then it is - // an error. - if (cacheSize === null || !metadataEntry) { - if (opt_onFailure) - onFailure(); - return; - } - - if (opt_onSuccess) - opt_onSuccess(); - - this.setCacheSize_(cacheSize - metadataEntry.size, transaction); - metadataStore.delete(key); // Delete asynchronously. - dataStore.delete(key); // Delete asynchronously. - }.bind(this); - - var onCacheSizeFailure = function() { - cacheSizeReceived = true; - }; - - var onCacheSizeSuccess = function(result) { - cacheSize = result; - cacheSizeReceived = true; - onPartialSuccess(); - }; - - // Fetch the current cache size. - this.fetchCacheSize_(onCacheSizeSuccess, onCacheSizeFailure, transaction); - - // Receive image's metadata. - var metadataRequest = metadataStore.get(key); - - metadataRequest.onsuccess = function(e) { - if (e.target.result) - metadataEntry = e.target.result; - metadataReceived = true; - onPartialSuccess(); - }; - - metadataRequest.onerror = function() { - console.error('Failed to remove an image.'); - metadataReceived = true; - onPartialSuccess(); - }; -}; diff --git a/chrome/browser/resources/image_loader/image_loader.js b/chrome/browser/resources/image_loader/image_loader.js deleted file mode 100644 index 24b07a4..0000000 --- a/chrome/browser/resources/image_loader/image_loader.js +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Loads and resizes an image. - * @constructor - */ -function ImageLoader() { - /** - * Persistent cache object. - * @type {Cache} - * @private - */ - this.cache_ = new Cache(); - - /** - * Manages pending requests and runs them in order of priorities. - * @type {Worker} - * @private - */ - this.worker_ = new Worker(); - - // Grant permissions to all volumes, initialize the cache and then start the - // worker. - chrome.fileBrowserPrivate.getVolumeMetadataList(function(volumeMetadataList) { - var initPromises = volumeMetadataList.map(function(volumeMetadata) { - var requestPromise = new Promise(function(callback) { - chrome.fileBrowserPrivate.requestFileSystem( - volumeMetadata.volumeId, - callback); - }); - return requestPromise; - }); - initPromises.push(new Promise(this.cache_.initialize.bind(this.cache_))); - - // After all initializatino promises are done, start the worker. - Promise.all(initPromises).then(this.worker_.start.bind(this.worker_)); - - // Listen for mount events, and grant permissions to volumes being mounted. - chrome.fileBrowserPrivate.onMountCompleted.addListener( - function(event) { - if (event.eventType == 'mount' && event.status == 'success') { - chrome.fileBrowserPrivate.requestFileSystem( - event.volumeMetadata.volumeId, function() {}); - } - }); - }.bind(this)); - - // Listen for incoming requests. - chrome.extension.onMessageExternal.addListener(function(request, - sender, - sendResponse) { - if (ImageLoader.ALLOWED_CLIENTS.indexOf(sender.id) !== -1) { - // Sending a response may fail if the receiver already went offline. - // This is not an error, but a normal and quite common situation. - var failSafeSendResponse = function(response) { - try { - sendResponse(response); - } - catch (e) { - // Ignore the error. - } - }; - return this.onMessage_(sender.id, request, failSafeSendResponse); - } - }.bind(this)); -} - -/** - * List of extensions allowed to perform image requests. - * - * @const - * @type {Array.<string>} - */ -ImageLoader.ALLOWED_CLIENTS = - ['hhaomjibdihmijegdhdafkllkbggdgoj']; // File Manager's extension id. - -/** - * Handles a request. Depending on type of the request, starts or stops - * an image task. - * - * @param {string} senderId Sender's extension id. - * @param {Object} request Request message as a hash array. - * @param {function} callback Callback to be called to return response. - * @return {boolean} True if the message channel should stay alive until the - * callback is called. - * @private - */ -ImageLoader.prototype.onMessage_ = function(senderId, request, callback) { - var requestId = senderId + ':' + request.taskId; - if (request.cancel) { - // Cancel a task. - this.worker_.remove(requestId); - return false; // No callback calls. - } else { - // Create a request task and add it to the worker (queue). - var requestTask = new Request(requestId, this.cache_, request, callback); - this.worker_.add(requestTask); - return true; // Request will call the callback. - } -}; - -/** - * Returns the singleton instance. - * @return {ImageLoader} ImageLoader object. - */ -ImageLoader.getInstance = function() { - if (!ImageLoader.instance_) - ImageLoader.instance_ = new ImageLoader(); - return ImageLoader.instance_; -}; - -/** - * Checks if the options contain any image processing. - * - * @param {number} width Source width. - * @param {number} height Source height. - * @param {Object} options Resizing options as a hash array. - * @return {boolean} True if yes, false if not. - */ -ImageLoader.shouldProcess = function(width, height, options) { - var targetDimensions = ImageLoader.resizeDimensions(width, height, options); - - // Dimensions has to be adjusted. - if (targetDimensions.width != width || targetDimensions.height != height) - return true; - - // Orientation has to be adjusted. - if (options.orientation) - return true; - - // No changes required. - return false; -}; - -/** - * Calculates dimensions taking into account resize options, such as: - * - scale: for scaling, - * - maxWidth, maxHeight: for maximum dimensions, - * - width, height: for exact requested size. - * Returns the target size as hash array with width, height properties. - * - * @param {number} width Source width. - * @param {number} height Source height. - * @param {Object} options Resizing options as a hash array. - * @return {Object} Dimensions, eg. {width: 100, height: 50}. - */ -ImageLoader.resizeDimensions = function(width, height, options) { - var sourceWidth = width; - var sourceHeight = height; - - // Flip dimensions for odd orientation values: 1 (90deg) and 3 (270deg). - if (options.orientation && options.orientation % 2) { - sourceWidth = height; - sourceHeight = width; - } - - var targetWidth = sourceWidth; - var targetHeight = sourceHeight; - - if ('scale' in options) { - targetWidth = sourceWidth * options.scale; - targetHeight = sourceHeight * options.scale; - } - - if (options.maxWidth && - targetWidth > options.maxWidth) { - var scale = options.maxWidth / targetWidth; - targetWidth *= scale; - targetHeight *= scale; - } - - if (options.maxHeight && - targetHeight > options.maxHeight) { - var scale = options.maxHeight / targetHeight; - targetWidth *= scale; - targetHeight *= scale; - } - - if (options.width) - targetWidth = options.width; - - if (options.height) - targetHeight = options.height; - - targetWidth = Math.round(targetWidth); - targetHeight = Math.round(targetHeight); - - return {width: targetWidth, height: targetHeight}; -}; - -/** - * Performs resizing of the source image into the target canvas. - * - * @param {HTMLCanvasElement|Image} source Source image or canvas. - * @param {HTMLCanvasElement} target Target canvas. - * @param {Object} options Resizing options as a hash array. - */ -ImageLoader.resize = function(source, target, options) { - var targetDimensions = ImageLoader.resizeDimensions( - source.width, source.height, options); - - target.width = targetDimensions.width; - target.height = targetDimensions.height; - - // Default orientation is 0deg. - var orientation = options.orientation || 0; - - // For odd orientation values: 1 (90deg) and 3 (270deg) flip dimensions. - var drawImageWidth; - var drawImageHeight; - if (orientation % 2) { - drawImageWidth = target.height; - drawImageHeight = target.width; - } else { - drawImageWidth = target.width; - drawImageHeight = target.height; - } - - var targetContext = target.getContext('2d'); - targetContext.save(); - targetContext.translate(target.width / 2, target.height / 2); - targetContext.rotate(orientation * Math.PI / 2); - targetContext.drawImage( - source, - 0, 0, - source.width, source.height, - -drawImageWidth / 2, -drawImageHeight / 2, - drawImageWidth, drawImageHeight); - targetContext.restore(); -}; diff --git a/chrome/browser/resources/image_loader/image_loader_client.js b/chrome/browser/resources/image_loader/image_loader_client.js deleted file mode 100644 index 9baba59..0000000 --- a/chrome/browser/resources/image_loader/image_loader_client.js +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Client used to connect to the remote ImageLoader extension. Client class runs - * in the extension, where the client.js is included (eg. Files.app). - * It sends remote requests using IPC to the ImageLoader class and forwards - * its responses. - * - * Implements cache, which is stored in the calling extension. - * - * @constructor - */ -function ImageLoaderClient() { - /** - * Hash array with active tasks. - * @type {Object} - * @private - */ - this.tasks_ = {}; - - /** - * @type {number} - * @private - */ - this.lastTaskId_ = 0; - - /** - * LRU cache for images. - * @type {ImageLoaderClient.Cache} - * @private - */ - this.cache_ = new ImageLoaderClient.Cache(); -} - -/** - * Image loader's extension id. - * @const - * @type {string} - */ -ImageLoaderClient.EXTENSION_ID = 'pmfjbimdmchhbnneeidfognadeopoehp'; - -/** - * Returns a singleton instance. - * @return {Client} Client instance. - */ -ImageLoaderClient.getInstance = function() { - if (!ImageLoaderClient.instance_) - ImageLoaderClient.instance_ = new ImageLoaderClient(); - return ImageLoaderClient.instance_; -}; - -/** - * Records binary metrics. Counts for true and false are stored as a histogram. - * @param {string} name Histogram's name. - * @param {boolean} value True or false. - */ -ImageLoaderClient.recordBinary = function(name, value) { - chrome.metricsPrivate.recordValue( - { metricName: 'ImageLoader.Client.' + name, - type: 'histogram-linear', - min: 1, // According to histogram.h, this should be 1 for enums. - max: 2, // Maximum should be exclusive. - buckets: 3 }, // Number of buckets: 0, 1 and overflowing 2. - value ? 1 : 0); -}; - -/** - * Records percent metrics, stored as a histogram. - * @param {string} name Histogram's name. - * @param {number} value Value (0..100). - */ -ImageLoaderClient.recordPercentage = function(name, value) { - chrome.metricsPrivate.recordPercentage('ImageLoader.Client.' + name, - Math.round(value)); -}; - -/** - * Sends a message to the Image Loader extension. - * @param {Object} request Hash array with request data. - * @param {function(Object)=} opt_callback Response handling callback. - * The response is passed as a hash array. - * @private - */ -ImageLoaderClient.sendMessage_ = function(request, opt_callback) { - opt_callback = opt_callback || function(response) {}; - var sendMessage = chrome.runtime ? chrome.runtime.sendMessage : - chrome.extension.sendMessage; - sendMessage(ImageLoaderClient.EXTENSION_ID, request, opt_callback); -}; - -/** - * Handles a message from the remote image loader and calls the registered - * callback to pass the response back to the requester. - * - * @param {Object} message Response message as a hash array. - * @private - */ -ImageLoaderClient.prototype.handleMessage_ = function(message) { - if (!(message.taskId in this.tasks_)) { - // This task has been canceled, but was already fetched, so it's result - // should be discarded anyway. - return; - } - - var task = this.tasks_[message.taskId]; - - // Check if the task is still valid. - if (task.isValid()) - task.accept(message); - - delete this.tasks_[message.taskId]; -}; - -/** - * Loads and resizes and image. Use opt_isValid to easily cancel requests - * which are not valid anymore, which will reduce cpu consumption. - * - * @param {string} url Url of the requested image. - * @param {function} callback Callback used to return response. - * @param {Object=} opt_options Loader options, such as: scale, maxHeight, - * width, height and/or cache. - * @param {function=} opt_isValid Function returning false in case - * a request is not valid anymore, eg. parent node has been detached. - * @return {?number} Remote task id or null if loaded from cache. - */ -ImageLoaderClient.prototype.load = function( - url, callback, opt_options, opt_isValid) { - opt_options = opt_options || {}; - opt_isValid = opt_isValid || function() { return true; }; - - // Record cache usage. - ImageLoaderClient.recordPercentage('Cache.Usage', this.cache_.getUsage()); - - // Cancel old, invalid tasks. - var taskKeys = Object.keys(this.tasks_); - for (var index = 0; index < taskKeys.length; index++) { - var taskKey = taskKeys[index]; - var task = this.tasks_[taskKey]; - if (!task.isValid()) { - // Cancel this task since it is not valid anymore. - this.cancel(taskKey); - delete this.tasks_[taskKey]; - } - } - - // Replace the extension id. - var sourceId = chrome.i18n.getMessage('@@extension_id'); - var targetId = ImageLoaderClient.EXTENSION_ID; - - url = url.replace('filesystem:chrome-extension://' + sourceId, - 'filesystem:chrome-extension://' + targetId); - - // Try to load from cache, if available. - var cacheKey = ImageLoaderClient.Cache.createKey(url, opt_options); - if (opt_options.cache) { - // Load from cache. - ImageLoaderClient.recordBinary('Cached', 1); - var cachedData = this.cache_.loadImage(cacheKey, opt_options.timestamp); - if (cachedData) { - ImageLoaderClient.recordBinary('Cache.HitMiss', 1); - callback({status: 'success', data: cachedData}); - return null; - } else { - ImageLoaderClient.recordBinary('Cache.HitMiss', 0); - } - } else { - // Remove from cache. - ImageLoaderClient.recordBinary('Cached', 0); - this.cache_.removeImage(cacheKey); - } - - // Not available in cache, performing a request to a remote extension. - var request = opt_options; - this.lastTaskId_++; - var task = {isValid: opt_isValid}; - this.tasks_[this.lastTaskId_] = task; - - request.url = url; - request.taskId = this.lastTaskId_; - request.timestamp = opt_options.timestamp; - - ImageLoaderClient.sendMessage_( - request, - function(result) { - // Save to cache. - if (result.status == 'success' && opt_options.cache) - this.cache_.saveImage(cacheKey, result.data, opt_options.timestamp); - callback(result); - }.bind(this)); - return request.taskId; -}; - -/** - * Cancels the request. - * @param {number} taskId Task id returned by ImageLoaderClient.load(). - */ -ImageLoaderClient.prototype.cancel = function(taskId) { - ImageLoaderClient.sendMessage_({taskId: taskId, cancel: true}); -}; - -/** - * Least Recently Used (LRU) cache implementation to be used by - * Client class. It has memory constraints, so it will never - * exceed specified memory limit defined in MEMORY_LIMIT. - * - * @constructor - */ -ImageLoaderClient.Cache = function() { - this.images_ = []; - this.size_ = 0; -}; - -/** - * Memory limit for images data in bytes. - * - * @const - * @type {number} - */ -ImageLoaderClient.Cache.MEMORY_LIMIT = 20 * 1024 * 1024; // 20 MB. - -/** - * Creates a cache key. - * - * @param {string} url Image url. - * @param {Object=} opt_options Loader options as a hash array. - * @return {string} Cache key. - */ -ImageLoaderClient.Cache.createKey = function(url, opt_options) { - opt_options = opt_options || {}; - return JSON.stringify({url: url, - orientation: opt_options.orientation, - scale: opt_options.scale, - width: opt_options.width, - height: opt_options.height, - maxWidth: opt_options.maxWidth, - maxHeight: opt_options.maxHeight}); -}; - -/** - * Evicts the least used elements in cache to make space for a new image. - * - * @param {number} size Requested size. - * @private - */ -ImageLoaderClient.Cache.prototype.evictCache_ = function(size) { - // Sort from the most recent to the oldest. - this.images_.sort(function(a, b) { - return b.lastLoadTimestamp - a.lastLoadTimestamp; - }); - - while (this.images_.length > 0 && - (ImageLoaderClient.Cache.MEMORY_LIMIT - this.size_ < size)) { - var entry = this.images_.pop(); - this.size_ -= entry.data.length; - } -}; - -/** - * Saves an image in the cache. - * - * @param {string} key Cache key. - * @param {string} data Image data. - * @param {number=} opt_timestamp Last modification timestamp. Used to detect - * if the cache entry becomes out of date. - */ -ImageLoaderClient.Cache.prototype.saveImage = function( - key, data, opt_timestamp) { - // If the image is currently in cache, then remove it. - if (this.images_[key]) - this.removeImage(key); - - if (ImageLoaderClient.Cache.MEMORY_LIMIT - this.size_ < data.length) { - ImageLoaderClient.recordBinary('Evicted', 1); - this.evictCache_(data.length); - } else { - ImageLoaderClient.recordBinary('Evicted', 0); - } - - if (ImageLoaderClient.Cache.MEMORY_LIMIT - this.size_ >= data.length) { - this.images_[key] = {lastLoadTimestamp: Date.now(), - timestamp: opt_timestamp ? opt_timestamp : null, - data: data}; - this.size_ += data.length; - } -}; - -/** - * Loads an image from the cache (if available) or returns null. - * - * @param {string} key Cache key. - * @param {number=} opt_timestamp Last modification timestamp. If different - * that the one in cache, then the entry will be invalidated. - * @return {?string} Data of the loaded image or null. - */ -ImageLoaderClient.Cache.prototype.loadImage = function(key, opt_timestamp) { - if (!(key in this.images_)) - return null; - - var entry = this.images_[key]; - entry.lastLoadTimestamp = Date.now(); - - // Check if the image in cache is up to date. If not, then remove it and - // return null. - if (entry.timestamp != opt_timestamp) { - this.removeImage(key); - return null; - } - - return entry.data; -}; - -/** - * Returns cache usage. - * @return {number} Value in percent points (0..100). - */ -ImageLoaderClient.Cache.prototype.getUsage = function() { - return this.size_ / ImageLoaderClient.Cache.MEMORY_LIMIT * 100.0; -}; - -/** - * Removes the image from the cache. - * @param {string} key Cache key. - */ -ImageLoaderClient.Cache.prototype.removeImage = function(key) { - if (!(key in this.images_)) - return; - - var entry = this.images_[key]; - this.size_ -= entry.data.length; - delete this.images_[key]; -}; - -// Helper functions. - -/** - * Loads and resizes and image. Use opt_isValid to easily cancel requests - * which are not valid anymore, which will reduce cpu consumption. - * - * @param {string} url Url of the requested image. - * @param {Image} image Image node to load the requested picture into. - * @param {Object} options Loader options, such as: orientation, scale, - * maxHeight, width, height and/or cache. - * @param {function=} onSuccess Callback for success. - * @param {function=} onError Callback for failure. - * @param {function=} opt_isValid Function returning false in case - * a request is not valid anymore, eg. parent node has been detached. - * @return {?number} Remote task id or null if loaded from cache. - */ -ImageLoaderClient.loadToImage = function( - url, image, options, onSuccess, onError, opt_isValid) { - var callback = function(result) { - if (result.status == 'error') { - onError(); - return; - } - image.src = result.data; - onSuccess(); - }; - - return ImageLoaderClient.getInstance().load( - url, callback, options, opt_isValid); -}; diff --git a/chrome/browser/resources/image_loader/manifest.json b/chrome/browser/resources/image_loader/manifest.json deleted file mode 100644 index ba4e820..0000000 --- a/chrome/browser/resources/image_loader/manifest.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - // chrome-extension://pmfjbimdmchhbnneeidfognadeopoehp - "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDowC9B4+K6zbl4PnALNyOUgra/MPdD8gZ39Fk/IAJWt03qrN7vz1gd/mmrBg0EEIsyLRmUmfyVEfvcIUOZxFqn4A9D2aaRSvNHy9qkasZMBDEql8Nt2iNZm/kGS7sizidDV6Bc/vyLNiH1gKOXBQ42JIxKjgtrmnhGV2giw2vJGwIDAQAB", - "name": "Image loader", - "version": "0.1", - "description": "Image loader", - "incognito" : "split", - "manifest_version": 2, - "permissions": [ - "fileBrowserHandler", - "fileBrowserPrivate", - "https://www.googledrive.com/" - ], - "content_security_policy": "default-src 'none'; script-src 'self'; style-src 'self'; frame-src 'self'; img-src 'self' data:; media-src 'self'; connect-src 'self' https://www.googledrive.com", - "background": { - "scripts": [ - "image_loader.js", - "cache.js", - "worker.js", - "request.js", - "background.js" - ], - "persistent": false - }, - "web_accessible_resources": ["image_loader_client.js"] -} diff --git a/chrome/browser/resources/image_loader/request.js b/chrome/browser/resources/image_loader/request.js deleted file mode 100644 index 11fc7c1..0000000 --- a/chrome/browser/resources/image_loader/request.js +++ /dev/null @@ -1,438 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Creates and starts downloading and then resizing of the image. Finally, - * returns the image using the callback. - * - * @param {string} id Request ID. - * @param {Cache} cache Cache object. - * @param {Object} request Request message as a hash array. - * @param {function} callback Callback used to send the response. - * @constructor - */ -function Request(id, cache, request, callback) { - /** - * @type {string} - * @private - */ - this.id_ = id; - - /** - * @type {Cache} - * @private - */ - this.cache_ = cache; - - /** - * @type {Object} - * @private - */ - this.request_ = request; - - /** - * @type {function} - * @private - */ - this.sendResponse_ = callback; - - /** - * Temporary image used to download images. - * @type {Image} - * @private - */ - this.image_ = new Image(); - - /** - * MIME type of the fetched image. - * @type {string} - * @private - */ - this.contentType_ = null; - - /** - * Used to download remote images using http:// or https:// protocols. - * @type {AuthorizedXHR} - * @private - */ - this.xhr_ = new AuthorizedXHR(); - - /** - * Temporary canvas used to resize and compress the image. - * @type {HTMLCanvasElement} - * @private - */ - this.canvas_ = document.createElement('canvas'); - - /** - * @type {CanvasRenderingContext2D} - * @private - */ - this.context_ = this.canvas_.getContext('2d'); - - /** - * Callback to be called once downloading is finished. - * @type {function()} - * @private - */ - this.downloadCallback_ = null; -} - -/** - * Returns ID of the request. - * @return {string} Request ID. - */ -Request.prototype.getId = function() { - return this.id_; -}; - -/** - * Returns priority of the request. The higher priority, the faster it will - * be handled. The highest priority is 0. The default one is 2. - * - * @return {number} Priority. - */ -Request.prototype.getPriority = function() { - return (this.request_.priority !== undefined) ? this.request_.priority : 2; -}; - -/** - * Tries to load the image from cache if exists and sends the response. - * - * @param {function()} onSuccess Success callback. - * @param {function()} onFailure Failure callback. - */ -Request.prototype.loadFromCacheAndProcess = function(onSuccess, onFailure) { - this.loadFromCache_( - function(data) { // Found in cache. - this.sendImageData_(data); - onSuccess(); - }.bind(this), - onFailure); // Not found in cache. -}; - -/** - * Tries to download the image, resizes and sends the response. - * @param {function()} callback Completion callback. - */ -Request.prototype.downloadAndProcess = function(callback) { - if (this.downloadCallback_) - throw new Error('Downloading already started.'); - - this.downloadCallback_ = callback; - this.downloadOriginal_(this.onImageLoad_.bind(this), - this.onImageError_.bind(this)); -}; - -/** - * Fetches the image from the persistent cache. - * - * @param {function()} onSuccess Success callback. - * @param {function()} onFailure Failure callback. - * @private - */ -Request.prototype.loadFromCache_ = function(onSuccess, onFailure) { - var cacheKey = Cache.createKey(this.request_); - - if (!this.request_.cache) { - // Cache is disabled for this request; therefore, remove it from cache - // if existed. - this.cache_.removeImage(cacheKey); - onFailure(); - return; - } - - if (!this.request_.timestamp) { - // Persistent cache is available only when a timestamp is provided. - onFailure(); - return; - } - - this.cache_.loadImage(cacheKey, - this.request_.timestamp, - onSuccess, - onFailure); -}; - -/** - * Saves the image to the persistent cache. - * - * @param {string} data The image's data. - * @private - */ -Request.prototype.saveToCache_ = function(data) { - if (!this.request_.cache || !this.request_.timestamp) { - // Persistent cache is available only when a timestamp is provided. - return; - } - - var cacheKey = Cache.createKey(this.request_); - this.cache_.saveImage(cacheKey, - data, - this.request_.timestamp); -}; - -/** - * Downloads an image directly or for remote resources using the XmlHttpRequest. - * - * @param {function()} onSuccess Success callback. - * @param {function()} onFailure Failure callback. - * @private - */ -Request.prototype.downloadOriginal_ = function(onSuccess, onFailure) { - this.image_.onload = onSuccess; - this.image_.onerror = onFailure; - - // Download data urls directly since they are not supported by XmlHttpRequest. - var dataUrlMatches = this.request_.url.match(/^data:([^,;]*)[,;]/); - if (dataUrlMatches) { - this.image_.src = this.request_.url; - this.contentType_ = dataUrlMatches[1]; - return; - } - - // Fetch the image via authorized XHR and parse it. - var parseImage = function(contentType, blob) { - var reader = new FileReader(); - reader.onerror = onFailure; - reader.onload = function(e) { - this.image_.src = e.target.result; - }.bind(this); - - // Load the data to the image as a data url. - reader.readAsDataURL(blob); - }.bind(this); - - // Request raw data via XHR. - this.xhr_.load(this.request_.url, parseImage, onFailure); -}; - -/** - * Creates a XmlHttpRequest wrapper with injected OAuth2 authentication headers. - * @constructor - */ -function AuthorizedXHR() { - this.xhr_ = null; - this.aborted_ = false; -} - -/** - * Aborts the current request (if running). - */ -AuthorizedXHR.prototype.abort = function() { - this.aborted_ = true; - if (this.xhr_) - this.xhr_.abort(); -}; - -/** - * Loads an image using a OAuth2 token. If it fails, then tries to retry with - * a refreshed OAuth2 token. - * - * @param {string} url URL to the resource to be fetched. - * @param {function(string, Blob}) onSuccess Success callback with the content - * type and the fetched data. - * @param {function()} onFailure Failure callback. - */ -AuthorizedXHR.prototype.load = function(url, onSuccess, onFailure) { - this.aborted_ = false; - - // Do not call any callbacks when aborting. - var onMaybeSuccess = function(contentType, response) { - if (!this.aborted_) - onSuccess(contentType, response); - }.bind(this); - var onMaybeFailure = function(opt_code) { - if (!this.aborted_) - onFailure(); - }.bind(this); - - // Fetches the access token and makes an authorized call. If refresh is true, - // then forces refreshing the access token. - var requestTokenAndCall = function(refresh, onInnerSuccess, onInnerFailure) { - chrome.fileBrowserPrivate.requestAccessToken(refresh, function(token) { - if (this.aborted_) - return; - if (!token) { - onInnerFailure(); - return; - } - this.xhr_ = AuthorizedXHR.load_( - token, url, onInnerSuccess, onInnerFailure); - }.bind(this)); - }.bind(this); - - // Refreshes the access token and retries the request. - var maybeRetryCall = function(code) { - if (this.aborted_) - return; - requestTokenAndCall(true, onMaybeSuccess, onMaybeFailure); - }.bind(this); - - // Do not request a token for local resources, since it is not necessary. - if (url.indexOf('filesystem:') === 0) { - this.xhr_ = AuthorizedXHR.load_(null, url, onMaybeSuccess, onMaybeFailure); - return; - } - - // Make the request with reusing the current token. If it fails, then retry. - requestTokenAndCall(false, onMaybeSuccess, maybeRetryCall); -}; - -/** - * Fetches data using authorized XmlHttpRequest with the provided OAuth2 token. - * If the token is invalid, the request will fail. - * - * @param {?string} token OAuth2 token to be injected to the request. Null for - * no token. - * @param {string} url URL to the resource to be fetched. - * @param {function(string, Blob}) onSuccess Success callback with the content - * type and the fetched data. - * @param {function(number=)} onFailure Failure callback with the error code - * if available. - * @return {AuthorizedXHR} XHR instance. - * @private - */ -AuthorizedXHR.load_ = function(token, url, onSuccess, onFailure) { - var xhr = new XMLHttpRequest(); - xhr.responseType = 'blob'; - - xhr.onreadystatechange = function() { - if (xhr.readyState != 4) - return; - if (xhr.status != 200) { - onFailure(xhr.status); - return; - } - var contentType = xhr.getResponseHeader('Content-Type'); - onSuccess(contentType, xhr.response); - }.bind(this); - - // Perform a xhr request. - try { - xhr.open('GET', url, true); - if (token) - xhr.setRequestHeader('Authorization', 'Bearer ' + token); - xhr.send(); - } catch (e) { - onFailure(); - } - - return xhr; -}; - -/** - * Sends the resized image via the callback. If the image has been changed, - * then packs the canvas contents, otherwise sends the raw image data. - * - * @param {boolean} imageChanged Whether the image has been changed. - * @private - */ -Request.prototype.sendImage_ = function(imageChanged) { - var imageData; - if (!imageChanged) { - // The image hasn't been processed, so the raw data can be directly - // forwarded for speed (no need to encode the image again). - imageData = this.image_.src; - } else { - // The image has been resized or rotated, therefore the canvas has to be - // encoded to get the correct compressed image data. - switch (this.contentType_) { - case 'image/gif': - case 'image/png': - case 'image/svg': - case 'image/bmp': - imageData = this.canvas_.toDataURL('image/png'); - break; - case 'image/jpeg': - default: - imageData = this.canvas_.toDataURL('image/jpeg', 0.9); - } - } - - // Send and store in the persistent cache. - this.sendImageData_(imageData); - this.saveToCache_(imageData); -}; - -/** - * Sends the resized image via the callback. - * @param {string} data Compressed image data. - * @private - */ -Request.prototype.sendImageData_ = function(data) { - this.sendResponse_({status: 'success', - data: data, - taskId: this.request_.taskId}); -}; - -/** - * Handler, when contents are loaded into the image element. Performs resizing - * and finalizes the request process. - * - * @param {function()} callback Completion callback. - * @private - */ -Request.prototype.onImageLoad_ = function(callback) { - // Perform processing if the url is not a data url, or if there are some - // operations requested. - if (!this.request_.url.match(/^data/) || - ImageLoader.shouldProcess(this.image_.width, - this.image_.height, - this.request_)) { - ImageLoader.resize(this.image_, this.canvas_, this.request_); - this.sendImage_(true); // Image changed. - } else { - this.sendImage_(false); // Image not changed. - } - this.cleanup_(); - this.downloadCallback_(); -}; - -/** - * Handler, when loading of the image fails. Sends a failure response and - * finalizes the request process. - * - * @param {function()} callback Completion callback. - * @private - */ -Request.prototype.onImageError_ = function(callback) { - this.sendResponse_({status: 'error', - taskId: this.request_.taskId}); - this.cleanup_(); - this.downloadCallback_(); -}; - -/** - * Cancels the request. - */ -Request.prototype.cancel = function() { - this.cleanup_(); - - // If downloading has started, then call the callback. - if (this.downloadCallback_) - this.downloadCallback_(); -}; - -/** - * Cleans up memory used by this request. - * @private - */ -Request.prototype.cleanup_ = function() { - this.image_.onerror = function() {}; - this.image_.onload = function() {}; - - // Transparent 1x1 pixel gif, to force garbage collecting. - this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAA' + - 'ABAAEAAAICTAEAOw=='; - - this.xhr_.onload = function() {}; - this.xhr_.abort(); - - // Dispose memory allocated by Canvas. - this.canvas_.width = 0; - this.canvas_.height = 0; -}; diff --git a/chrome/browser/resources/image_loader/worker.js b/chrome/browser/resources/image_loader/worker.js deleted file mode 100644 index fe1f2f1..0000000 --- a/chrome/browser/resources/image_loader/worker.js +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Worker for requests. Fetches requests from a queue and processes them - * synchronously, taking into account priorities. The highest priority is 0. - */ -function Worker() { - /** - * List of requests waiting to be checked. If these items are available in - * cache, then they are processed immediately after starting the worker. - * However, if they have to be downloaded, then these requests are moved - * to pendingRequests_. - * - * @type {Array.<Request>} - * @private - */ - this.newRequests_ = []; - - /** - * List of pending requests for images to be downloaded. - * @type {Array.<Request>} - * @private - */ - this.pendingRequests_ = []; - - /** - * List of requests being processed. - * @type {Array.<Request>} - * @private - */ - this.activeRequests_ = []; - - /** - * Hash array of requests being added to the queue, but not finalized yet. - * @type {Object} - * @private - */ - this.requests_ = {}; - - /** - * If the worker has been started. - * @type {boolean} - * @private - */ - this.started_ = false; -} - -/** - * Maximum download requests to be run in parallel. - * @type {number} - * @const - */ -Worker.MAXIMUM_IN_PARALLEL = 5; - -/** - * Adds a request to the internal priority queue and executes it when requests - * with higher priorities are finished. If the result is cached, then it is - * processed immediately once the worker is started. - * - * @param {Request} request Request object. - */ -Worker.prototype.add = function(request) { - if (!this.started_) { - this.newRequests_.push(request); - this.requests_[request.getId()] = request; - return; - } - - // Enqueue the request, since already started. - this.pendingRequests_.push(request); - this.sortPendingRequests_(); - - this.continue_(); -}; - -/** - * Removes a request from the worker (if exists). - * @param {string} requestId Unique ID of the request. - */ -Worker.prototype.remove = function(requestId) { - var request = this.requests_[requestId]; - if (!request) - return; - - // Remove from the internal queues with pending tasks. - var newIndex = this.pendingRequests_.indexOf(request); - if (newIndex != -1) - this.newRequests_.splice(newIndex, 1); - var pendingIndex = this.pendingRequests_.indexOf(request); - if (pendingIndex != -1) - this.pendingRequests_.splice(pendingIndex, 1); - - // Cancel the request. - request.cancel(); - delete this.requests_[requestId]; -}; - -/** - * Starts handling requests. - */ -Worker.prototype.start = function() { - this.started_ = true; - - // Process tasks added before worker has been started. - this.pendingRequests_ = this.newRequests_; - this.sortPendingRequests_(); - this.newRequests_ = []; - - // Start serving enqueued requests. - this.continue_(); -}; - -/** - * Sorts pending requests by priorities. - * @private - */ -Worker.prototype.sortPendingRequests_ = function() { - this.pendingRequests_.sort(function(a, b) { - return a.getPriority() - b.getPriority(); - }); -}; - -/** - * Processes pending requests from the queue. There is no guarantee that - * all of the tasks will be processed at once. - * - * @private - */ -Worker.prototype.continue_ = function() { - // Run only up to MAXIMUM_IN_PARALLEL in the same time. - while (this.pendingRequests_.length && - this.activeRequests_.length < Worker.MAXIMUM_IN_PARALLEL) { - var request = this.pendingRequests_.shift(); - this.activeRequests_.push(request); - - // Try to load from cache. If doesn't exist, then download. - request.loadFromCacheAndProcess( - this.finish_.bind(this, request), - function(currentRequest) { - currentRequest.downloadAndProcess( - this.finish_.bind(this, currentRequest)); - }.bind(this, request)); - } -}; - -/** - * Handles finished requests. - * - * @param {Request} request Finished request. - * @private - */ -Worker.prototype.finish_ = function(request) { - var index = this.activeRequests_.indexOf(request); - if (index < 0) - console.warn('Request not found.'); - this.activeRequests_.splice(index, 1); - delete this.requests_[request.getId()]; - - // Continue handling the most important requests (if started). - if (this.started_) - this.continue_(); -}; |