| // Copyright 2013 The Chromium Authors |
| // 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.js'; |
| |
| import {ImageCache} from './cache.js'; |
| import type {ImageTransformParam} from './image_orientation.js'; |
| import {ImageOrientation} from './image_orientation.js'; |
| import {ImageRequestTask} from './image_request_task.js'; |
| import type {LoadImageResponse} from './load_image_request.js'; |
| import {type LoadImageRequest} from './load_image_request.js'; |
| import {Scheduler} from './scheduler.js'; |
| |
| let instance: ImageLoader|null = null; |
| |
| /** |
| * Loads and resizes an image. |
| */ |
| export class ImageLoader { |
| /** |
| * Persistent cache object. |
| */ |
| private cache_: ImageCache = new ImageCache(); |
| |
| /** |
| * Manages pending requests and runs them in order of priorities. |
| */ |
| private scheduler_: Scheduler = new Scheduler(); |
| |
| constructor() { |
| // Initialize the cache and then start the scheduler. |
| this.cache_.initialize(() => this.scheduler_.start()); |
| |
| // Listen for incoming requests. |
| chrome.runtime.onMessageExternal.addListener( |
| (msg, sender, sendResponse) => { |
| if (!sender.origin || !msg) { |
| return; |
| } |
| |
| if (ALLOWED_CLIENT_ORIGINS.indexOf(sender.origin) === -1) { |
| return; |
| } |
| |
| this.onIncomingRequest_(msg, sender.origin, sendResponse); |
| }); |
| |
| chrome.runtime.onConnectNative.addListener(port => { |
| assert(port.sender); |
| 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(); |
| } |
| }); |
| }); |
| } |
| |
| /** |
| * Handler for incoming requests. |
| */ |
| private onIncomingRequest_( |
| request: LoadImageRequest, senderOrigin: string, |
| sendResponse: (r: unknown) => void) { |
| // 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: unknown) { |
| 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 as ImageTransformParam); |
| } 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. |
| * @return True if the message channel should stay alive until the |
| * callback is called. |
| */ |
| private onMessage_( |
| senderOrigin: string, request: LoadImageRequest, |
| callback: (r: LoadImageResponse) => void): boolean { |
| const requestId = senderOrigin + ':' + request.taskId; |
| if (request.cancel) { |
| // Cancel a task. |
| this.scheduler_.remove(requestId); |
| return false; // No callback calls. |
| } |
| // 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 a singleton instance. |
| */ |
| static getInstance(): ImageLoader { |
| if (!instance) { |
| instance = new ImageLoader(); |
| } |
| return instance; |
| } |
| } |
| |
| /** |
| * List of extensions allowed to perform image requests. |
| */ |
| const ALLOWED_CLIENT_ORIGINS = [ |
| 'chrome://file-manager', // File Manager SWA |
| ]; |