Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
UnsupportedError: "chrome://remote/content/cdp/Error.sys.mjs",
});
export class Stream {
#path;
#offset;
#length;
constructor(path) {
this.#path = path;
this.#offset = 0;
this.#length = null;
}
async destroy() {
await IOUtils.remove(this.#path);
}
async seek(seekTo) {
// To keep compatibility with Chrome clip invalid offsets
this.#offset = Math.max(0, Math.min(seekTo, await this.length()));
}
async readBytes(count) {
const bytes = await IOUtils.read(this.#path, {
offset: this.#offset,
maxBytes: count,
});
this.#offset += bytes.length;
return bytes;
}
async available() {
const length = await this.length();
return length - this.#offset;
}
async length() {
if (this.#length === null) {
const info = await IOUtils.stat(this.#path);
this.#length = info.size;
}
return this.#length;
}
get path() {
return this.#path;
}
}
export class StreamRegistry {
constructor() {
// handle => stream
this.streams = new Map();
// Register an async shutdown blocker to ensure all open IO streams are
// closed, and remaining temporary files removed. Needs to happen before
// IOUtils has been shutdown.
IOUtils.profileBeforeChange.addBlocker(
"Remote Agent: Clean-up of open streams",
async () => {
await this.destructor();
}
);
}
async destructor() {
for (const stream of this.streams.values()) {
await stream.destroy();
}
this.streams.clear();
}
/**
* Add a new stream to the registry.
*
* @param {Stream} stream
* The stream to use.
*
* @returns {string}
* Stream handle (uuid)
*/
add(stream) {
if (!(stream instanceof Stream)) {
// Bug 1602731 - Implement support for blob
throw new lazy.UnsupportedError(`Unknown stream type for ${stream}`);
}
const handle = lazy.generateUUID();
this.streams.set(handle, stream);
return handle;
}
/**
* Get a stream from the registry.
*
* @param {string} handle
* Handle of the stream to retrieve.
*
* @returns {Stream}
* The requested stream.
*/
get(handle) {
const stream = this.streams.get(handle);
if (!stream) {
throw new TypeError(`Invalid stream handle`);
}
return stream;
}
/**
* Remove a stream from the registry.
*
* @param {string} handle
* Handle of the stream to remove.
*
* @returns {boolean}
* true if successfully removed
*/
async remove(handle) {
const stream = this.get(handle);
await stream.destroy();
return this.streams.delete(handle);
}
}