Commit 95a98a7d authored by Bo Majewski's avatar Bo Majewski Committed by Commit Bot

Renames ImageRequest to ImageRequestTask

During a cleanup https://codereview.chromium.org/1148563004 the image
Request class was renamed to ImageRequest. However, the file name was
kept as request.js. Make the class and its file name the same.

Bug: 903742
Change-Id: I6f2d7bbfb6f3f94001d0cd1e629fc27f83061c45
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2245429Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Commit-Queue: Bo Majewski <majewski@chromium.org>
Cr-Commit-Position: refs/heads/master@{#779705}
parent c18c02f9
......@@ -22,3 +22,7 @@ IN_PROC_BROWSER_TEST_F(ImageLoaderJsTest, CacheTest) {
IN_PROC_BROWSER_TEST_F(ImageLoaderJsTest, ImageLoaderTest) {
RunTestURL("image_loader_unittest_gen.html");
}
IN_PROC_BROWSER_TEST_F(ImageLoaderJsTest, SchedulerTest) {
RunTestURL("scheduler_unittest_gen.html");
}
......@@ -14,9 +14,9 @@ js_type_check("closure_compile_module") {
":image_loader",
":image_loader_client",
":image_loader_util",
":image_request_task",
":load_image_request",
":piex_loader",
":request",
":scheduler",
]
}
......@@ -39,9 +39,9 @@ js_unittest("cache_unittest") {
js_library("image_loader") {
deps = [
":cache",
":image_request_task",
":load_image_request",
":piex_loader",
":request",
":scheduler",
"//ui/file_manager/externs:file_manager_private",
]
......@@ -96,7 +96,7 @@ js_library("piex_loader") {
externs_list = [ "//ui/file_manager/externs/platform.js" ]
}
js_library("request") {
js_library("image_request_task") {
deps = [
":cache",
":image_loader_util",
......@@ -108,7 +108,14 @@ js_library("request") {
}
js_library("scheduler") {
deps = [ ":request" ]
deps = [ ":image_request_task" ]
}
js_unittest("scheduler_unittest") {
deps = [
":scheduler",
"//ui/webui/resources/js:webui_resource_test",
]
}
js_test_gen_html("js_test_gen_html") {
......@@ -117,6 +124,7 @@ js_test_gen_html("js_test_gen_html") {
":cache_unittest",
":image_loader_client_unittest",
":image_loader_unittest",
":scheduler_unittest",
]
}
......
......@@ -9,7 +9,7 @@
// <include src="image_loader.js">
// <include src="image_loader_util.js">
// <include src="piex_loader.js">
// <include src="request.js">
// <include src="image_request_task.js">
// <include src="scheduler.js">
// Entry point.
// <include src="background.js">
......@@ -131,7 +131,7 @@ ImageLoader.prototype.onMessage_ = function(senderOrigin, request, callback) {
return false; // No callback calls.
} else {
// Create a request task and add it to the scheduler (queue).
const requestTask = new ImageRequest(
const requestTask = new ImageRequestTask(
requestId, this.cache_, this.piexLoader_, request, callback);
this.scheduler_.add(requestTask);
return true; // Request will call the callback.
......
......@@ -13,7 +13,7 @@
* @param {function(!LoadImageResponse)} callback Response handler.
* @constructor
*/
function ImageRequest(id, cache, piexLoader, request, callback) {
function ImageRequestTask(id, cache, piexLoader, request, callback) {
/**
* Global ID (concatenated client ID and client request ID).
* @type {string}
......@@ -111,7 +111,7 @@ function ImageRequest(id, cache, piexLoader, request, callback) {
* @const
* @type {number}
*/
ImageRequest.VIDEO_THUMBNAIL_POSITION = 3; // [sec]
ImageRequestTask.VIDEO_THUMBNAIL_POSITION = 3; // [sec]
/**
* The maximum milliseconds to load video. If loading video exceeds the limit,
......@@ -119,13 +119,13 @@ ImageRequest.VIDEO_THUMBNAIL_POSITION = 3; // [sec]
* @const
* @type {number}
*/
ImageRequest.MAX_MILLISECONDS_TO_LOAD_VIDEO = 3000;
ImageRequestTask.MAX_MILLISECONDS_TO_LOAD_VIDEO = 3000;
/**
* A map which is used to estimate content type from extension.
* @enum {string}
*/
ImageRequest.ExtensionContentTypeMap = {
ImageRequestTask.ExtensionContentTypeMap = {
gif: 'image/gif',
png: 'image/png',
svg: 'image/svg',
......@@ -138,7 +138,7 @@ ImageRequest.ExtensionContentTypeMap = {
* Returns ID of the request.
* @return {string} Request ID.
*/
ImageRequest.prototype.getId = function() {
ImageRequestTask.prototype.getId = function() {
return this.id_;
};
......@@ -146,7 +146,7 @@ ImageRequest.prototype.getId = function() {
* Returns the client's task ID for the request.
* @return {number}
*/
ImageRequest.prototype.getClientTaskId = function() {
ImageRequestTask.prototype.getClientTaskId = function() {
// Every incoming request should have been given a taskId.
assert(this.request_.taskId);
return this.request_.taskId;
......@@ -158,7 +158,7 @@ ImageRequest.prototype.getClientTaskId = function() {
*
* @return {number} Priority.
*/
ImageRequest.prototype.getPriority = function() {
ImageRequestTask.prototype.getPriority = function() {
return (this.request_.priority !== undefined) ? this.request_.priority : 2;
};
......@@ -169,7 +169,7 @@ ImageRequest.prototype.getPriority = function() {
* @param {function()} onSuccess Success callback.
* @param {function()} onFailure Failure callback.
*/
ImageRequest.prototype.loadFromCacheAndProcess = function(
ImageRequestTask.prototype.loadFromCacheAndProcess = function(
onSuccess, onFailure) {
this.loadFromCache_(
function(width, height, ifd, data) { // Found in cache.
......@@ -185,14 +185,14 @@ ImageRequest.prototype.loadFromCacheAndProcess = function(
*
* @param {function()} callback Completion callback.
*/
ImageRequest.prototype.downloadAndProcess = function(callback) {
ImageRequestTask.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));
this.downloadOriginal_(
this.onImageLoad_.bind(this), this.onImageError_.bind(this));
};
/**
......@@ -203,7 +203,7 @@ ImageRequest.prototype.downloadAndProcess = function(callback) {
* @param {function()} onFailure Failure callback.
* @private
*/
ImageRequest.prototype.loadFromCache_ = function(onSuccess, onFailure) {
ImageRequestTask.prototype.loadFromCache_ = function(onSuccess, onFailure) {
const cacheKey = LoadImageRequest.cacheKey(this.request_);
if (!cacheKey) {
......@@ -238,7 +238,7 @@ ImageRequest.prototype.loadFromCache_ = function(onSuccess, onFailure) {
* @param {string} data Image data.
* @private
*/
ImageRequest.prototype.saveToCache_ = function(width, height, data) {
ImageRequestTask.prototype.saveToCache_ = function(width, height, data) {
const timestamp = this.request_.timestamp;
if (!this.request_.cache || !timestamp) {
......@@ -262,7 +262,7 @@ ImageRequest.prototype.saveToCache_ = function(width, height, data) {
* @param {function()} onFailure Failure callback.
* @private
*/
ImageRequest.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
ImageRequestTask.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
this.image_.onload = function() {
URL.revokeObjectURL(this.image_.src);
onSuccess();
......@@ -324,12 +324,14 @@ ImageRequest.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
// Load video thumbnails by using video tag instead of XHR.
if (fileType.type === 'video') {
this.createVideoThumbnailUrl_(this.request_.url).then(function(url) {
this.image_.src = url;
}.bind(this)).catch(function(error) {
console.error('Video thumbnail error: ', error);
onFailure();
});
this.createVideoThumbnailUrl_(this.request_.url)
.then(function(url) {
this.image_.src = url;
}.bind(this))
.catch(function(error) {
console.error('Video thumbnail error: ', error);
onFailure();
});
return;
}
......@@ -353,7 +355,7 @@ ImageRequest.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
* thumbnail.
* @private
*/
ImageRequest.prototype.createVideoThumbnailUrl_ = function(url) {
ImageRequestTask.prototype.createVideoThumbnailUrl_ = function(url) {
const video =
assertInstanceof(document.createElement('video'), HTMLVideoElement);
return Promise
......@@ -361,13 +363,13 @@ ImageRequest.prototype.createVideoThumbnailUrl_ = function(url) {
new Promise((resolve, reject) => {
video.addEventListener('canplay', resolve);
video.addEventListener('error', reject);
video.currentTime = ImageRequest.VIDEO_THUMBNAIL_POSITION;
video.currentTime = ImageRequestTask.VIDEO_THUMBNAIL_POSITION;
video.preload = 'auto';
video.src = url;
video.load();
}),
new Promise((resolve) => {
setTimeout(resolve, ImageRequest.MAX_MILLISECONDS_TO_LOAD_VIDEO);
setTimeout(resolve, ImageRequestTask.MAX_MILLISECONDS_TO_LOAD_VIDEO);
}).then(() => {
// If we don't receive 'canplay' event after 3 seconds have passed for
// some reason (e.g. unseekable video), we give up generating
......@@ -396,7 +398,7 @@ ImageRequest.prototype.createVideoThumbnailUrl_ = function(url) {
* type and the fetched data.
* @param {function()} onFailure Failure callback.
*/
ImageRequest.prototype.load = function(url, onSuccess, onFailure) {
ImageRequestTask.prototype.load = function(url, onSuccess, onFailure) {
this.aborted_ = false;
// Do not call any callbacks when aborting.
......@@ -405,7 +407,8 @@ ImageRequest.prototype.load = function(url, onSuccess, onFailure) {
// When content type is not available, try to estimate it from url.
if (!contentType) {
contentType =
ImageRequest.ExtensionContentTypeMap[this.extractExtension_(url)];
ImageRequestTask
.ExtensionContentTypeMap[this.extractExtension_(url)];
}
if (!this.aborted_) {
......@@ -422,7 +425,8 @@ ImageRequest.prototype.load = function(url, onSuccess, onFailure) {
// The query parameter is workaround for crbug.com/379678, which forces the
// browser to obtain the latest contents of the image.
const noCacheUrl = url + '?nocache=' + Date.now();
this.xhr_ = ImageRequest.load_(noCacheUrl, onMaybeSuccess, onMaybeFailure);
this.xhr_ =
ImageRequestTask.load_(noCacheUrl, onMaybeSuccess, onMaybeFailure);
};
/**
......@@ -430,7 +434,7 @@ ImageRequest.prototype.load = function(url, onSuccess, onFailure) {
* @param {string} url Url.
* @return {string} Extracted extension, e.g. png.
*/
ImageRequest.prototype.extractExtension_ = function(url) {
ImageRequestTask.prototype.extractExtension_ = function(url) {
const result = (/\.([a-zA-Z]+)$/i).exec(url);
return result ? result[1] : '';
};
......@@ -446,7 +450,7 @@ ImageRequest.prototype.extractExtension_ = function(url) {
* @return {XMLHttpRequest} XHR instance.
* @private
*/
ImageRequest.load_ = function(url, onSuccess, onFailure) {
ImageRequestTask.load_ = function(url, onSuccess, onFailure) {
const xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
......@@ -481,7 +485,7 @@ ImageRequest.load_ = function(url, onSuccess, onFailure) {
* @param {boolean} imageChanged Whether the image has been changed.
* @private
*/
ImageRequest.prototype.sendImage_ = function(imageChanged) {
ImageRequestTask.prototype.sendImage_ = function(imageChanged) {
let width;
let height;
let data;
......@@ -525,7 +529,7 @@ ImageRequest.prototype.sendImage_ = function(imageChanged) {
* @param {string} data Image data.
* @private
*/
ImageRequest.prototype.sendImageData_ = function(width, height, data) {
ImageRequestTask.prototype.sendImageData_ = function(width, height, data) {
const result = {width, height, ifd: this.ifd_, data};
this.sendResponse_(new LoadImageResponse(
LoadImageResponseStatus.SUCCESS, this.getClientTaskId(), result));
......@@ -536,7 +540,7 @@ ImageRequest.prototype.sendImageData_ = function(width, height, data) {
* and finalizes the request process.
* @private
*/
ImageRequest.prototype.onImageLoad_ = function() {
ImageRequestTask.prototype.onImageLoad_ = function() {
// Perform processing if the url is not a data url, or if there are some
// operations requested.
if (!(this.request_.url.match(/^data/) ||
......@@ -559,7 +563,7 @@ ImageRequest.prototype.onImageLoad_ = function() {
* finalizes the request process.
* @private
*/
ImageRequest.prototype.onImageError_ = function() {
ImageRequestTask.prototype.onImageError_ = function() {
this.sendResponse_(new LoadImageResponse(
LoadImageResponseStatus.ERROR, this.getClientTaskId()));
this.cleanup_();
......@@ -569,7 +573,7 @@ ImageRequest.prototype.onImageError_ = function() {
/**
* Cancels the request.
*/
ImageRequest.prototype.cancel = function() {
ImageRequestTask.prototype.cancel = function() {
this.cleanup_();
// If downloading has started, then call the callback.
......@@ -582,7 +586,7 @@ ImageRequest.prototype.cancel = function() {
* Cleans up memory used by this request.
* @private
*/
ImageRequest.prototype.cleanup_ = function() {
ImageRequestTask.prototype.cleanup_ = function() {
this.image_.onerror = function() {};
this.image_.onload = function() {};
......
......@@ -3,43 +3,44 @@
// found in the LICENSE file.
/**
* Scheduler for requests. Fetches requests from a queue and processes them
* synchronously, taking into account priorities. The highest priority is 0.
* Scheduler for ImageRequestTask objects. Fetches tasks from a queue and
* processes them synchronously, taking into account priorities. The highest
* priority is 0.
* @constructor
*/
function Scheduler() {
/**
* List of requests waiting to be checked. If these items are available in
* List of tasks waiting to be checked. If these items are available in
* cache, then they are processed immediately after starting the scheduler.
* However, if they have to be downloaded, then these requests are moved
* to pendingRequests_.
* However, if they have to be downloaded, then these tasks are moved
* to pendingTasks_.
*
* @type {Array<ImageRequest>}
* @type {Array<ImageRequestTask>}
* @private
*/
this.newRequests_ = [];
this.newTasks_ = [];
/**
* List of pending requests for images to be downloaded.
* @type {Array<ImageRequest>}
* List of pending tasks for images to be downloaded.
* @type {Array<ImageRequestTask>}
* @private
*/
this.pendingRequests_ = [];
this.pendingTasks_ = [];
/**
* List of requests being processed.
* @type {Array<ImageRequest>}
* List of tasks being processed.
* @type {Array<ImageRequestTask>}
* @private
*/
this.activeRequests_ = [];
this.activeTasks_ = [];
/**
* Map of requests being added to the queue, but not finalized yet. Keyed by
* the ImageRequest id.
* @type {Object<string, ImageRequest>}>
* Map of tasks being added to the queue, but not finalized yet. Keyed by
* the ImageRequestTask id.
* @type {Object<string, ImageRequestTask>}>
* @private
*/
this.requests_ = {};
this.tasks_ = {};
/**
* If the scheduler has been started.
......@@ -50,121 +51,119 @@ function Scheduler() {
}
/**
* Maximum download requests to be run in parallel.
* Maximum download tasks to be run in parallel.
* @type {number}
* @const
*/
Scheduler.MAXIMUM_IN_PARALLEL = 5;
/**
* Adds a request to the internal priority queue and executes it when requests
* Adds a task to the internal priority queue and executes it when tasks
* with higher priorities are finished. If the result is cached, then it is
* processed immediately once the scheduler is started.
*
* @param {ImageRequest} request Request object.
* @param {ImageRequestTask} task A task to be run
*/
Scheduler.prototype.add = function(request) {
Scheduler.prototype.add = function(task) {
if (!this.started_) {
this.newRequests_.push(request);
this.requests_[request.getId()] = request;
this.newTasks_.push(task);
this.tasks_[task.getId()] = task;
return;
}
// Enqueue the request, since already started.
this.pendingRequests_.push(request);
this.sortPendingRequests_();
// Enqueue the tasks, since already started.
this.pendingTasks_.push(task);
this.sortPendingTasks_();
this.continue_();
};
/**
* Removes a request from the scheduler (if exists).
* @param {string} requestId Unique ID of the request.
* Removes a task from the scheduler (if exists).
* @param {string} taskId Unique ID of the task.
*/
Scheduler.prototype.remove = function(requestId) {
const request = this.requests_[requestId];
if (!request) {
Scheduler.prototype.remove = function(taskId) {
const task = this.tasks_[taskId];
if (!task) {
return;
}
// Remove from the internal queues with pending tasks.
const newIndex = this.pendingRequests_.indexOf(request);
const newIndex = this.newTasks_.indexOf(task);
if (newIndex != -1) {
this.newRequests_.splice(newIndex, 1);
this.newTasks_.splice(newIndex, 1);
}
const pendingIndex = this.pendingRequests_.indexOf(request);
const pendingIndex = this.pendingTasks_.indexOf(task);
if (pendingIndex != -1) {
this.pendingRequests_.splice(pendingIndex, 1);
this.pendingTasks_.splice(pendingIndex, 1);
}
// Cancel the request.
request.cancel();
delete this.requests_[requestId];
// Cancel the task.
task.cancel();
delete this.tasks_[taskId];
};
/**
* Starts handling requests.
* Starts handling tasks.
*/
Scheduler.prototype.start = function() {
this.started_ = true;
// Process tasks added before scheduler has been started.
this.pendingRequests_ = this.newRequests_;
this.sortPendingRequests_();
this.newRequests_ = [];
this.pendingTasks_ = this.newTasks_;
this.sortPendingTasks_();
this.newTasks_ = [];
// Start serving enqueued requests.
// Start serving enqueued tasks.
this.continue_();
};
/**
* Sorts pending requests by priorities.
* Sorts pending tasks by priorities.
* @private
*/
Scheduler.prototype.sortPendingRequests_ = function() {
this.pendingRequests_.sort(function(a, b) {
Scheduler.prototype.sortPendingTasks_ = function() {
this.pendingTasks_.sort(function(a, b) {
return a.getPriority() - b.getPriority();
});
};
/**
* Processes pending requests from the queue. There is no guarantee that
* Processes pending tasks from the queue. There is no guarantee that
* all of the tasks will be processed at once.
*
* @private
*/
Scheduler.prototype.continue_ = function() {
// Run only up to MAXIMUM_IN_PARALLEL in the same time.
while (this.pendingRequests_.length &&
this.activeRequests_.length < Scheduler.MAXIMUM_IN_PARALLEL) {
const request = this.pendingRequests_.shift();
this.activeRequests_.push(request);
while (this.pendingTasks_.length &&
this.activeTasks_.length < Scheduler.MAXIMUM_IN_PARALLEL) {
const task = this.pendingTasks_.shift();
this.activeTasks_.push(task);
// 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));
task.loadFromCacheAndProcess(
this.finish_.bind(this, task), function(currentTask) {
currentTask.downloadAndProcess(this.finish_.bind(this, currentTask));
}.bind(this, task));
}
};
/**
* Handles finished requests.
* Handles finished tasks.
*
* @param {ImageRequest} request Finished request.
* @param {ImageRequestTask} task Finished task.
* @private
*/
Scheduler.prototype.finish_ = function(request) {
const index = this.activeRequests_.indexOf(request);
Scheduler.prototype.finish_ = function(task) {
const index = this.activeTasks_.indexOf(task);
if (index < 0) {
console.warn('Request not found.');
console.warn('ImageRequestTask not found.');
}
this.activeRequests_.splice(index, 1);
delete this.requests_[request.getId()];
this.activeTasks_.splice(index, 1);
delete this.tasks_[task.getId()];
// Continue handling the most important requests (if started).
// Continue handling the most important tasks (if started).
if (this.started_) {
this.continue_();
}
......
// Copyright 2020 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';
/**
* Fake global clock used to record the "time" at which a task was run.
*/
let globalTime = 0;
function setUp() {
globalTime = 0;
}
/**
* @typedef{{
* cancelCallCount: number,
* runTime: number,
* }}
*/
let FakeImageRequestTask;
/**
* @param {string} taskId
* @return {!FakeImageRequestTask}
*/
function newTask(taskId, priority) {
return /** @type !FakeImageRequestTask */ ({
// Counts how many times cancel method was called.
// Used to test multiple cancellation of the same task.
cancelCallCount: 0,
// Records value of globalTime variable at the time the main method,
// loadFromCacheAndProcess is called. Used to test if the task was
// executed and in what orders tasks were executed.
runTime: 0,
getId() {
return taskId;
},
getPriority() {
return priority;
},
cancel() {
++this.cancelCallCount;
},
loadFromCacheAndProcess(resolve, reject) {
this.runTime = ++globalTime;
setTimeout(resolve);
},
});
}
/**
* Checks that adding and removing tasks before the scheduler is started works.
*/
function testIdleSchedulerAddRemove() {
const scheduler = new Scheduler();
const fakeTask = newTask('task-1', 0);
scheduler.add(/** @type {!ImageRequestTask} */ (fakeTask));
assertEquals(0, fakeTask.cancelCallCount);
scheduler.remove('task-1');
assertEquals(1, fakeTask.cancelCallCount);
scheduler.remove('task-1');
assertEquals(1, fakeTask.cancelCallCount);
}
/**
* Checks that tasks that were in newTasks are correctly copied to pending
* tasks when scheduler is started. They also should be executed in the
* order of their priorities.
*/
function testNewTasksMovedAndRunInPriorityOrder() {
const fakeTask1 = newTask('task-1', 1);
const fakeTask2 = newTask('task-2', 0);
const scheduler = new Scheduler();
scheduler.add(/** @type {!ImageRequestTask} */ (fakeTask1));
scheduler.add(/** @type {!ImageRequestTask} */ (fakeTask2));
scheduler.start();
assertEquals(2, fakeTask1.runTime);
assertEquals(1, fakeTask2.runTime);
}
/**
* Checks that the scheduler only launches MAXIMUM_IN_PARALLEL tasks.
*/
function testParallelTasks() {
const scheduler = new Scheduler();
const taskList = [];
for (let i = 0; i <= Scheduler.MAXIMUM_IN_PARALLEL; ++i) {
taskList.push(newTask(`task-${i}`, 0));
scheduler.add(/** @type {!ImageRequestTask} */ (taskList[i]));
}
scheduler.start();
for (let i = 0; i < Scheduler.MAXIMUM_IN_PARALLEL; ++i) {
assertEquals(i + 1, taskList[i].runTime, `task ${i} did not run`);
}
assertEquals(0, taskList[Scheduler.MAXIMUM_IN_PARALLEL].runTime);
}
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