123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- // 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.
- import {assert} from 'chrome://resources/js/assert.m.js';
- import {ImageCache} from './cache.js';
- import {ImageOrientation} from './image_orientation.js';
- import {ImageRequestTask} from './image_request_task.js';
- import {LoadImageRequest, LoadImageResponse} from './load_image_request.js';
- import {Scheduler} from './scheduler.js';
- /**
- * Loads and resizes an image.
- * @constructor
- */
- export function ImageLoader() {
- /**
- * Persistent cache object.
- * @type {ImageCache}
- * @private
- */
- this.cache_ = new ImageCache();
- /**
- * Manages pending requests and runs them in order of priorities.
- * @type {Scheduler}
- * @private
- */
- this.scheduler_ = new Scheduler();
- // Grant permissions to all volumes, initialize the cache and then start the
- // scheduler.
- chrome.fileManagerPrivate.getVolumeMetadataList(function(volumeMetadataList) {
- // Listen for mount events, and grant permissions to volumes being mounted.
- chrome.fileManagerPrivate.onMountCompleted.addListener(
- function(event) {
- if (event.eventType === 'mount' && event.status === 'success') {
- chrome.fileSystem.requestFileSystem(
- {volumeId: event.volumeMetadata.volumeId}, function() {});
- }
- });
- const initPromises = volumeMetadataList.map(function(volumeMetadata) {
- const requestPromise = new Promise(function(callback) {
- chrome.fileSystem.requestFileSystem(
- {volumeId: volumeMetadata.volumeId},
- /** @type {function(FileSystem=)} */(callback));
- });
- return requestPromise;
- });
- initPromises.push(new Promise(function(resolve, reject) {
- this.cache_.initialize(resolve);
- }.bind(this)));
- // After all initialization promises are done, start the scheduler.
- Promise.all(initPromises).then(this.scheduler_.start.bind(this.scheduler_));
- }.bind(this));
- // Listen for incoming requests.
- chrome.runtime.onMessageExternal.addListener((msg, sender, sendResponse) => {
- if (!sender.origin || !msg) {
- return;
- }
- if (ImageLoader.ALLOWED_CLIENT_ORIGINS.indexOf(sender.origin) === -1) {
- return;
- }
- this.onIncomingRequest_(msg, sender.origin, sendResponse);
- });
- chrome.runtime['onConnectNative'].addListener((port) => {
- if (port.sender.nativeApplication != 'com.google.ash_thumbnail_loader') {
- port.disconnect();
- return;
- }
- port.onMessage.addListener((msg) => {
- // Each connection is expected to handle a single request only.
- const started = this.onIncomingRequest_(
- msg, port.sender.nativeApplication, response => {
- port.postMessage(response);
- port.disconnect();
- });
- if (!started) {
- port.disconnect();
- }
- });
- });
- }
- /**
- * List of extensions allowed to perform image requests.
- *
- * @const
- * @type {Array<string>}
- */
- ImageLoader.ALLOWED_CLIENT_ORIGINS = [
- 'chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj', // File Manager
- 'chrome://file-manager', // File Manager SWA
- ];
- /**
- * Handler for incoming requests.
- *
- * @param {*} request_data A LoadImageRequest (received untyped).
- * @param {!string} senderOrigin
- * @param {function(*): void} sendResponse
- */
- ImageLoader.prototype.onIncomingRequest_ = function(
- request_data, senderOrigin, sendResponse) {
- const request = /** @type {!LoadImageRequest} */ (request_data);
- // Sending a response may fail if the receiver already went offline.
- // This is not an error, but a normal and quite common situation.
- const failSafeSendResponse = function(response) {
- try {
- sendResponse(response);
- } catch (e) {
- // Ignore the error.
- }
- };
- // Incoming requests won't have the full type.
- assert(!(request.orientation instanceof ImageOrientation));
- assert(!(typeof request.orientation === 'number'));
- if (request.orientation) {
- request.orientation =
- ImageOrientation.fromRotationAndScale(request.orientation);
- } else {
- request.orientation = new ImageOrientation(1, 0, 0, 1);
- }
- return this.onMessage_(senderOrigin, request, failSafeSendResponse);
- };
- /**
- * Handles a request. Depending on type of the request, starts or stops
- * an image task.
- *
- * @param {string} senderOrigin Sender's origin.
- * @param {!LoadImageRequest} request Pre-processed request.
- * @param {function(!LoadImageResponse)} 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(senderOrigin, request, callback) {
- const requestId = senderOrigin + ':' + request.taskId;
- if (request.cancel) {
- // Cancel a task.
- this.scheduler_.remove(requestId);
- return false; // No callback calls.
- } else {
- // Create a request task and add it to the scheduler (queue).
- const requestTask =
- new ImageRequestTask(requestId, this.cache_, request, callback);
- this.scheduler_.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_;
- };
|