2022-09-16 05:35:51 +00:00
|
|
|
/**
|
|
|
|
* Copyright 2022 Google Inc. All rights reserved.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2023-05-15 14:39:47 +00:00
|
|
|
import {Frame as BaseFrame} from '../api/Frame.js';
|
2023-05-31 21:36:19 +00:00
|
|
|
import {Deferred} from '../util/Deferred.js';
|
2023-02-15 23:09:31 +00:00
|
|
|
|
2022-09-16 05:35:51 +00:00
|
|
|
/**
|
|
|
|
* Keeps track of the page frame tree and it's is managed by
|
|
|
|
* {@link FrameManager}. FrameTree uses frame IDs to reference frame and it
|
|
|
|
* means that referenced frames might not be in the tree anymore. Thus, the tree
|
|
|
|
* structure is eventually consistent.
|
|
|
|
* @internal
|
|
|
|
*/
|
2023-05-15 14:39:47 +00:00
|
|
|
export class FrameTree<Frame extends BaseFrame> {
|
2022-09-16 05:35:51 +00:00
|
|
|
#frames = new Map<string, Frame>();
|
|
|
|
// frameID -> parentFrameID
|
|
|
|
#parentIds = new Map<string, string>();
|
|
|
|
// frameID -> childFrameIDs
|
|
|
|
#childIds = new Map<string, Set<string>>();
|
|
|
|
#mainFrame?: Frame;
|
2023-05-26 09:42:22 +00:00
|
|
|
#waitRequests = new Map<string, Set<Deferred<Frame>>>();
|
2022-09-16 05:35:51 +00:00
|
|
|
|
|
|
|
getMainFrame(): Frame | undefined {
|
|
|
|
return this.#mainFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
getById(frameId: string): Frame | undefined {
|
|
|
|
return this.#frames.get(frameId);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a promise that is resolved once the frame with
|
|
|
|
* the given ID is added to the tree.
|
|
|
|
*/
|
|
|
|
waitForFrame(frameId: string): Promise<Frame> {
|
|
|
|
const frame = this.getById(frameId);
|
|
|
|
if (frame) {
|
|
|
|
return Promise.resolve(frame);
|
|
|
|
}
|
2023-05-31 21:36:19 +00:00
|
|
|
const deferred = Deferred.create<Frame>();
|
2022-09-16 05:35:51 +00:00
|
|
|
const callbacks =
|
2023-05-26 09:42:22 +00:00
|
|
|
this.#waitRequests.get(frameId) || new Set<Deferred<Frame>>();
|
2022-09-16 05:35:51 +00:00
|
|
|
callbacks.add(deferred);
|
2023-05-26 06:02:17 +00:00
|
|
|
return deferred.valueOrThrow();
|
2022-09-16 05:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
frames(): Frame[] {
|
|
|
|
return Array.from(this.#frames.values());
|
|
|
|
}
|
|
|
|
|
|
|
|
addFrame(frame: Frame): void {
|
|
|
|
this.#frames.set(frame._id, frame);
|
|
|
|
if (frame._parentId) {
|
|
|
|
this.#parentIds.set(frame._id, frame._parentId);
|
|
|
|
if (!this.#childIds.has(frame._parentId)) {
|
|
|
|
this.#childIds.set(frame._parentId, new Set());
|
|
|
|
}
|
|
|
|
this.#childIds.get(frame._parentId)!.add(frame._id);
|
2023-07-13 20:11:36 +00:00
|
|
|
} else if (!this.#mainFrame) {
|
2022-09-16 05:35:51 +00:00
|
|
|
this.#mainFrame = frame;
|
|
|
|
}
|
|
|
|
this.#waitRequests.get(frame._id)?.forEach(request => {
|
|
|
|
return request.resolve(frame);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
removeFrame(frame: Frame): void {
|
|
|
|
this.#frames.delete(frame._id);
|
|
|
|
this.#parentIds.delete(frame._id);
|
|
|
|
if (frame._parentId) {
|
|
|
|
this.#childIds.get(frame._parentId)?.delete(frame._id);
|
|
|
|
} else {
|
|
|
|
this.#mainFrame = undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
childFrames(frameId: string): Frame[] {
|
|
|
|
const childIds = this.#childIds.get(frameId);
|
|
|
|
if (!childIds) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
return Array.from(childIds)
|
|
|
|
.map(id => {
|
|
|
|
return this.getById(id);
|
|
|
|
})
|
|
|
|
.filter((frame): frame is Frame => {
|
|
|
|
return frame !== undefined;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
parentFrame(frameId: string): Frame | undefined {
|
|
|
|
const parentId = this.#parentIds.get(frameId);
|
|
|
|
return parentId ? this.getById(parentId) : undefined;
|
|
|
|
}
|
|
|
|
}
|