// Copyright 2021 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.
/*
 * Copyright (C) 2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the #name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
import * as Common from '../common/common.js';
import * as i18n from '../i18n/i18n.js';
import * as Platform from '../platform/platform.js';
import { DOMModel } from './DOMModel.js';
import { Events as NetworkManagerEvents, NetworkManager } from './NetworkManager.js';
import { Resource } from './Resource.js';
import { ExecutionContext, RuntimeModel } from './RuntimeModel.js';
import { Capability, Type } from './Target.js';
import { SDKModel } from './SDKModel.js';
import { TargetManager } from './TargetManager.js';
import { SecurityOriginManager } from './SecurityOriginManager.js';
import { StorageKeyManager } from './StorageKeyManager.js';
import { FrameManager } from './FrameManager.js';
export class ResourceTreeModel extends SDKModel {
    agent;
    storageAgent;
    #securityOriginManager;
    #storageKeyManager;
    framesInternal;
    #cachedResourcesProcessed;
    #pendingReloadOptions;
    #reloadSuspensionCount;
    isInterstitialShowing;
    mainFrame;
    #pendingBackForwardCacheNotUsedEvents;
    #pendingPrerenderAttemptCompletedEvents;
    constructor(target) {
        super(target);
        const networkManager = target.model(NetworkManager);
        if (networkManager) {
            networkManager.addEventListener(NetworkManagerEvents.RequestFinished, this.onRequestFinished, this);
            networkManager.addEventListener(NetworkManagerEvents.RequestUpdateDropped, this.onRequestUpdateDropped, this);
        }
        this.agent = target.pageAgent();
        this.storageAgent = target.storageAgent();
        void this.agent.invoke_enable();
        this.#securityOriginManager = target.model(SecurityOriginManager);
        this.#storageKeyManager = target.model(StorageKeyManager);
        this.#pendingBackForwardCacheNotUsedEvents = new Set();
        this.#pendingPrerenderAttemptCompletedEvents = new Set();
        target.registerPageDispatcher(new PageDispatcher(this));
        target.registerPreloadDispatcher(new PreloadDispatcher(this));
        this.framesInternal = new Map();
        this.#cachedResourcesProcessed = false;
        this.#pendingReloadOptions = null;
        this.#reloadSuspensionCount = 0;
        this.isInterstitialShowing = false;
        this.mainFrame = null;
        void this.#buildResourceTree();
    }
    async #buildResourceTree() {
        return this.agent.invoke_getResourceTree().then(event => {
            this.processCachedResources(event.getError() ? null : event.frameTree);
            if (this.mainFrame) {
                this.processPendingEvents(this.mainFrame);
            }
        });
    }
    static frameForRequest(request) {
        const networkManager = NetworkManager.forRequest(request);
        const resourceTreeModel = networkManager ? networkManager.target().model(ResourceTreeModel) : null;
        if (!resourceTreeModel) {
            return null;
        }
        return request.frameId ? resourceTreeModel.frameForId(request.frameId) : null;
    }
    static frames() {
        const result = [];
        for (const resourceTreeModel of TargetManager.instance().models(ResourceTreeModel)) {
            result.push(...resourceTreeModel.frames());
        }
        return result;
    }
    static resourceForURL(url) {
        for (const resourceTreeModel of TargetManager.instance().models(ResourceTreeModel)) {
            const mainFrame = resourceTreeModel.mainFrame;
            const result = mainFrame ? mainFrame.resourceForURL(url) : null;
            if (result) {
                return result;
            }
        }
        return null;
    }
    static reloadAllPages(bypassCache, scriptToEvaluateOnLoad) {
        for (const resourceTreeModel of TargetManager.instance().models(ResourceTreeModel)) {
            if (resourceTreeModel.target().parentTarget()?.type() !== Type.Frame) {
                resourceTreeModel.reloadPage(bypassCache, scriptToEvaluateOnLoad);
            }
        }
    }
    async storageKeyForFrame(frameId) {
        if (!this.framesInternal.has(frameId)) {
            return null;
        }
        const response = await this.storageAgent.invoke_getStorageKeyForFrame({ frameId: frameId });
        if (response.getError() === 'Frame tree node for given frame not found') {
            return null;
        }
        return response.storageKey;
    }
    domModel() {
        return this.target().model(DOMModel);
    }
    processCachedResources(mainFramePayload) {
        // TODO(caseq): the url check below is a mergeable, conservative
        // workaround for a problem caused by us requesting resources from a
        // subtarget frame before it has committed. The proper fix is likely
        // to be too complicated to be safely merged.
        // See https://crbug.com/1081270 for details.
        if (mainFramePayload && mainFramePayload.frame.url !== ':') {
            this.dispatchEventToListeners(Events.WillLoadCachedResources);
            this.addFramesRecursively(null, mainFramePayload);
            this.target().setInspectedURL(mainFramePayload.frame.url);
        }
        this.#cachedResourcesProcessed = true;
        const runtimeModel = this.target().model(RuntimeModel);
        if (runtimeModel) {
            runtimeModel.setExecutionContextComparator(this.executionContextComparator.bind(this));
            runtimeModel.fireExecutionContextOrderChanged();
        }
        this.dispatchEventToListeners(Events.CachedResourcesLoaded, this);
    }
    cachedResourcesLoaded() {
        return this.#cachedResourcesProcessed;
    }
    addFrame(frame, _aboutToNavigate) {
        this.framesInternal.set(frame.id, frame);
        if (frame.isMainFrame()) {
            this.mainFrame = frame;
        }
        this.dispatchEventToListeners(Events.FrameAdded, frame);
        this.updateSecurityOrigins();
        void this.updateStorageKeys();
    }
    frameAttached(frameId, parentFrameId, stackTrace) {
        const sameTargetParentFrame = parentFrameId ? (this.framesInternal.get(parentFrameId) || null) : null;
        // Do nothing unless cached resource tree is processed - it will overwrite everything.
        if (!this.#cachedResourcesProcessed && sameTargetParentFrame) {
            return null;
        }
        if (this.framesInternal.has(frameId)) {
            return null;
        }
        const frame = new ResourceTreeFrame(this, sameTargetParentFrame, frameId, null, stackTrace || null);
        if (parentFrameId && !sameTargetParentFrame) {
            frame.crossTargetParentFrameId = parentFrameId;
        }
        if (frame.isMainFrame() && this.mainFrame) {
            // Navigation to the new backend process.
            this.frameDetached(this.mainFrame.id, false);
        }
        this.addFrame(frame, true);
        return frame;
    }
    frameNavigated(framePayload, type) {
        const sameTargetParentFrame = framePayload.parentId ? (this.framesInternal.get(framePayload.parentId) || null) : null;
        // Do nothing unless cached resource tree is processed - it will overwrite everything.
        if (!this.#cachedResourcesProcessed && sameTargetParentFrame) {
            return;
        }
        let frame = this.framesInternal.get(framePayload.id) || null;
        if (!frame) {
            // Simulate missed "frameAttached" for a main frame navigation to the new backend process.
            frame = this.frameAttached(framePayload.id, framePayload.parentId || null);
            console.assert(Boolean(frame));
            if (!frame) {
                return;
            }
        }
        this.dispatchEventToListeners(Events.FrameWillNavigate, frame);
        frame.navigate(framePayload);
        if (type) {
            frame.backForwardCacheDetails.restoredFromCache = type === "BackForwardCacheRestore" /* Protocol.Page.NavigationType.BackForwardCacheRestore */;
        }
        this.dispatchEventToListeners(Events.FrameNavigated, frame);
        if (frame.isPrimaryFrame()) {
            this.primaryPageChanged(frame, "Navigation" /* PrimaryPageChangeType.Navigation */);
        }
        // Fill frame with retained resources (the ones loaded using new loader).
        const resources = frame.resources();
        for (let i = 0; i < resources.length; ++i) {
            this.dispatchEventToListeners(Events.ResourceAdded, resources[i]);
        }
        if (frame.isMainFrame()) {
            this.target().setInspectedURL(frame.url);
        }
        this.updateSecurityOrigins();
        void this.updateStorageKeys();
        if (frame.backForwardCacheDetails.restoredFromCache) {
            FrameManager.instance().modelRemoved(this);
            FrameManager.instance().modelAdded(this);
            void this.#buildResourceTree();
        }
    }
    primaryPageChanged(frame, type) {
        this.processPendingEvents(frame);
        this.dispatchEventToListeners(Events.PrimaryPageChanged, { frame, type });
        const networkManager = this.target().model(NetworkManager);
        if (networkManager && frame.isOutermostFrame()) {
            networkManager.clearRequests();
        }
    }
    documentOpened(framePayload) {
        this.frameNavigated(framePayload, undefined);
        const frame = this.framesInternal.get(framePayload.id);
        if (frame && !frame.getResourcesMap().get(framePayload.url)) {
            const frameResource = this.createResourceFromFramePayload(framePayload, framePayload.url, Common.ResourceType.resourceTypes.Document, framePayload.mimeType, null, null);
            frameResource.isGenerated = true;
            frame.addResource(frameResource);
        }
    }
    frameDetached(frameId, isSwap) {
        // Do nothing unless cached resource tree is processed - it will overwrite everything.
        if (!this.#cachedResourcesProcessed) {
            return;
        }
        const frame = this.framesInternal.get(frameId);
        if (!frame) {
            return;
        }
        const sameTargetParentFrame = frame.sameTargetParentFrame();
        if (sameTargetParentFrame) {
            sameTargetParentFrame.removeChildFrame(frame, isSwap);
        }
        else {
            frame.remove(isSwap);
        }
        this.updateSecurityOrigins();
        void this.updateStorageKeys();
    }
    onRequestFinished(event) {
        if (!this.#cachedResourcesProcessed) {
            return;
        }
        const request = event.data;
        if (request.failed) {
            return;
        }
        const frame = request.frameId ? this.framesInternal.get(request.frameId) : null;
        if (frame) {
            frame.addRequest(request);
        }
    }
    onRequestUpdateDropped(event) {
        if (!this.#cachedResourcesProcessed) {
            return;
        }
        const data = event.data;
        const frameId = data.frameId;
        if (!frameId) {
            return;
        }
        const frame = this.framesInternal.get(frameId);
        if (!frame) {
            return;
        }
        const url = data.url;
        if (frame.getResourcesMap().get(url)) {
            return;
        }
        const resource = new Resource(this, null, url, frame.url, frameId, data.loaderId, Common.ResourceType.resourceTypes[data.resourceType], data.mimeType, data.lastModified, null);
        frame.addResource(resource);
    }
    frameForId(frameId) {
        return this.framesInternal.get(frameId) || null;
    }
    forAllResources(callback) {
        if (this.mainFrame) {
            return this.mainFrame.callForFrameResources(callback);
        }
        return false;
    }
    frames() {
        return [...this.framesInternal.values()];
    }
    resourceForURL(url) {
        // Workers call into this with no #frames available.
        return this.mainFrame ? this.mainFrame.resourceForURL(url) : null;
    }
    addFramesRecursively(sameTargetParentFrame, frameTreePayload) {
        const framePayload = frameTreePayload.frame;
        let frame = this.framesInternal.get(framePayload.id);
        if (!frame) {
            frame = new ResourceTreeFrame(this, sameTargetParentFrame, framePayload.id, framePayload, null);
        }
        if (!sameTargetParentFrame && framePayload.parentId) {
            frame.crossTargetParentFrameId = framePayload.parentId;
        }
        this.addFrame(frame);
        for (const childFrame of frameTreePayload.childFrames || []) {
            this.addFramesRecursively(frame, childFrame);
        }
        for (let i = 0; i < frameTreePayload.resources.length; ++i) {
            const subresource = frameTreePayload.resources[i];
            const resource = this.createResourceFromFramePayload(framePayload, subresource.url, Common.ResourceType.resourceTypes[subresource.type], subresource.mimeType, subresource.lastModified || null, subresource.contentSize || null);
            frame.addResource(resource);
        }
        if (!frame.getResourcesMap().get(framePayload.url)) {
            const frameResource = this.createResourceFromFramePayload(framePayload, framePayload.url, Common.ResourceType.resourceTypes.Document, framePayload.mimeType, null, null);
            frame.addResource(frameResource);
        }
    }
    createResourceFromFramePayload(frame, url, type, mimeType, lastModifiedTime, contentSize) {
        const lastModified = typeof lastModifiedTime === 'number' ? new Date(lastModifiedTime * 1000) : null;
        return new Resource(this, null, url, frame.url, frame.id, frame.loaderId, type, mimeType, lastModified, contentSize);
    }
    suspendReload() {
        this.#reloadSuspensionCount++;
    }
    resumeReload() {
        this.#reloadSuspensionCount--;
        console.assert(this.#reloadSuspensionCount >= 0, 'Unbalanced call to ResourceTreeModel.resumeReload()');
        if (!this.#reloadSuspensionCount && this.#pendingReloadOptions) {
            const { ignoreCache, scriptToEvaluateOnLoad } = this.#pendingReloadOptions;
            this.reloadPage(ignoreCache, scriptToEvaluateOnLoad);
        }
    }
    reloadPage(ignoreCache, scriptToEvaluateOnLoad) {
        // Only dispatch PageReloadRequested upon first reload request to simplify client logic.
        if (!this.#pendingReloadOptions) {
            this.dispatchEventToListeners(Events.PageReloadRequested, this);
        }
        if (this.#reloadSuspensionCount) {
            this.#pendingReloadOptions = { ignoreCache, scriptToEvaluateOnLoad };
            return;
        }
        this.#pendingReloadOptions = null;
        const networkManager = this.target().model(NetworkManager);
        if (networkManager) {
            networkManager.clearRequests();
        }
        this.dispatchEventToListeners(Events.WillReloadPage);
        void this.agent.invoke_reload({ ignoreCache, scriptToEvaluateOnLoad });
    }
    // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    navigate(url) {
        return this.agent.invoke_navigate({ url });
    }
    async navigationHistory() {
        const response = await this.agent.invoke_getNavigationHistory();
        if (response.getError()) {
            return null;
        }
        return { currentIndex: response.currentIndex, entries: response.entries };
    }
    navigateToHistoryEntry(entry) {
        void this.agent.invoke_navigateToHistoryEntry({ entryId: entry.id });
    }
    setLifecycleEventsEnabled(enabled) {
        return this.agent.invoke_setLifecycleEventsEnabled({ enabled });
    }
    async fetchAppManifest() {
        const response = await this.agent.invoke_getAppManifest();
        if (response.getError()) {
            return { url: response.url, data: null, errors: [] };
        }
        return { url: response.url, data: response.data || null, errors: response.errors };
    }
    async getInstallabilityErrors() {
        const response = await this.agent.invoke_getInstallabilityErrors();
        return response.installabilityErrors || [];
    }
    async getAppId() {
        return this.agent.invoke_getAppId();
    }
    executionContextComparator(a, b) {
        function framePath(frame) {
            let currentFrame = frame;
            const parents = [];
            while (currentFrame) {
                parents.push(currentFrame);
                currentFrame = currentFrame.sameTargetParentFrame();
            }
            return parents.reverse();
        }
        if (a.target() !== b.target()) {
            return ExecutionContext.comparator(a, b);
        }
        const framesA = a.frameId ? framePath(this.frameForId(a.frameId)) : [];
        const framesB = b.frameId ? framePath(this.frameForId(b.frameId)) : [];
        let frameA;
        let frameB;
        for (let i = 0;; i++) {
            if (!framesA[i] || !framesB[i] || (framesA[i] !== framesB[i])) {
                frameA = framesA[i];
                frameB = framesB[i];
                break;
            }
        }
        if (!frameA && frameB) {
            return -1;
        }
        if (!frameB && frameA) {
            return 1;
        }
        if (frameA && frameB) {
            return frameA.id.localeCompare(frameB.id);
        }
        return ExecutionContext.comparator(a, b);
    }
    getSecurityOriginData() {
        const securityOrigins = new Set();
        let mainSecurityOrigin = null;
        let unreachableMainSecurityOrigin = null;
        for (const frame of this.framesInternal.values()) {
            const origin = frame.securityOrigin;
            if (!origin) {
                continue;
            }
            securityOrigins.add(origin);
            if (frame.isMainFrame()) {
                mainSecurityOrigin = origin;
                if (frame.unreachableUrl()) {
                    const unreachableParsed = new Common.ParsedURL.ParsedURL(frame.unreachableUrl());
                    unreachableMainSecurityOrigin = unreachableParsed.securityOrigin();
                }
            }
        }
        return {
            securityOrigins: securityOrigins,
            mainSecurityOrigin: mainSecurityOrigin,
            unreachableMainSecurityOrigin: unreachableMainSecurityOrigin,
        };
    }
    async getStorageKeyData() {
        const storageKeys = new Set();
        let mainStorageKey = null;
        for (const { isMainFrame, storageKey } of await Promise.all([...this.framesInternal.values()].map(f => f.getStorageKey(/* forceFetch */ false).then(k => ({
            isMainFrame: f.isMainFrame(),
            storageKey: k,
        }))))) {
            if (isMainFrame) {
                mainStorageKey = storageKey;
            }
            if (storageKey) {
                storageKeys.add(storageKey);
            }
        }
        return { storageKeys: storageKeys, mainStorageKey: mainStorageKey };
    }
    updateSecurityOrigins() {
        const data = this.getSecurityOriginData();
        this.#securityOriginManager.setMainSecurityOrigin(data.mainSecurityOrigin || '', data.unreachableMainSecurityOrigin || '');
        this.#securityOriginManager.updateSecurityOrigins(data.securityOrigins);
    }
    async updateStorageKeys() {
        const data = await this.getStorageKeyData();
        this.#storageKeyManager.setMainStorageKey(data.mainStorageKey || '');
        this.#storageKeyManager.updateStorageKeys(data.storageKeys);
    }
    async getMainStorageKey() {
        return this.mainFrame ? this.mainFrame.getStorageKey(/* forceFetch */ false) : null;
    }
    getMainSecurityOrigin() {
        const data = this.getSecurityOriginData();
        return data.mainSecurityOrigin || data.unreachableMainSecurityOrigin;
    }
    onBackForwardCacheNotUsed(event) {
        if (this.mainFrame && this.mainFrame.id === event.frameId && this.mainFrame.loaderId === event.loaderId) {
            this.mainFrame.setBackForwardCacheDetails(event);
            this.dispatchEventToListeners(Events.BackForwardCacheDetailsUpdated, this.mainFrame);
        }
        else {
            this.#pendingBackForwardCacheNotUsedEvents.add(event);
        }
    }
    onPrerenderAttemptCompleted(event) {
        if (this.mainFrame && this.mainFrame.id === event.initiatingFrameId) {
            this.mainFrame.setPrerenderFinalStatus(event.finalStatus);
            this.dispatchEventToListeners(Events.PrerenderingStatusUpdated, this.mainFrame);
            if (event.disallowedApiMethod) {
                this.mainFrame.setPrerenderDisallowedApiMethod(event.disallowedApiMethod);
            }
        }
        else {
            this.#pendingPrerenderAttemptCompletedEvents.add(event);
        }
        this.dispatchEventToListeners(Events.PrerenderAttemptCompleted, event);
    }
    processPendingEvents(frame) {
        if (!frame.isMainFrame()) {
            return;
        }
        for (const event of this.#pendingBackForwardCacheNotUsedEvents) {
            if (frame.id === event.frameId && frame.loaderId === event.loaderId) {
                frame.setBackForwardCacheDetails(event);
                this.#pendingBackForwardCacheNotUsedEvents.delete(event);
                break;
            }
        }
        for (const event of this.#pendingPrerenderAttemptCompletedEvents) {
            if (frame.id === event.initiatingFrameId) {
                frame.setPrerenderFinalStatus(event.finalStatus);
                if (event.disallowedApiMethod) {
                    frame.setPrerenderDisallowedApiMethod(event.disallowedApiMethod);
                }
                this.#pendingPrerenderAttemptCompletedEvents.delete(event);
                break;
            }
        }
        // No need to dispatch events here as this method call is followed by a `PrimaryPageChanged` event.
    }
}
// TODO(crbug.com/1167717): Make this a const enum again
// eslint-disable-next-line rulesdir/const_enum
export var Events;
(function (Events) {
    Events["FrameAdded"] = "FrameAdded";
    Events["FrameNavigated"] = "FrameNavigated";
    Events["FrameDetached"] = "FrameDetached";
    Events["FrameResized"] = "FrameResized";
    Events["FrameWillNavigate"] = "FrameWillNavigate";
    // Primary page changes can be either main frame navigations or activations of a background frame.
    // TODO(crbug.com/1393057): Let frame activations trigger this event.
    Events["PrimaryPageChanged"] = "PrimaryPageChanged";
    Events["ResourceAdded"] = "ResourceAdded";
    Events["WillLoadCachedResources"] = "WillLoadCachedResources";
    Events["CachedResourcesLoaded"] = "CachedResourcesLoaded";
    Events["DOMContentLoaded"] = "DOMContentLoaded";
    Events["LifecycleEvent"] = "LifecycleEvent";
    Events["Load"] = "Load";
    Events["PageReloadRequested"] = "PageReloadRequested";
    Events["WillReloadPage"] = "WillReloadPage";
    Events["InterstitialShown"] = "InterstitialShown";
    Events["InterstitialHidden"] = "InterstitialHidden";
    Events["BackForwardCacheDetailsUpdated"] = "BackForwardCacheDetailsUpdated";
    Events["PrerenderingStatusUpdated"] = "PrerenderingStatusUpdated";
    Events["PrerenderAttemptCompleted"] = "PrerenderAttemptCompleted";
    Events["JavaScriptDialogOpening"] = "JavaScriptDialogOpening";
})(Events || (Events = {}));
export class ResourceTreeFrame {
    #model;
    #sameTargetParentFrameInternal;
    #idInternal;
    crossTargetParentFrameId;
    #loaderIdInternal;
    #nameInternal;
    #urlInternal;
    #domainAndRegistryInternal;
    #securityOriginInternal;
    #storageKeyInternal;
    #unreachableUrlInternal;
    #adFrameStatusInternal;
    #secureContextType;
    #crossOriginIsolatedContextType;
    #gatedAPIFeatures;
    #creationStackTrace;
    #creationStackTraceTarget;
    #childFramesInternal;
    resourcesMap;
    backForwardCacheDetails = {
        restoredFromCache: undefined,
        explanations: [],
        explanationsTree: undefined,
    };
    prerenderFinalStatus;
    prerenderDisallowedApiMethod;
    constructor(model, parentFrame, frameId, payload, creationStackTrace) {
        this.#model = model;
        this.#sameTargetParentFrameInternal = parentFrame;
        this.#idInternal = frameId;
        this.crossTargetParentFrameId = null;
        this.#loaderIdInternal = (payload && payload.loaderId) || '';
        this.#nameInternal = payload && payload.name;
        this.#urlInternal =
            payload && payload.url || Platform.DevToolsPath.EmptyUrlString;
        this.#domainAndRegistryInternal = (payload && payload.domainAndRegistry) || '';
        this.#securityOriginInternal = payload && payload.securityOrigin;
        this.#unreachableUrlInternal =
            (payload && payload.unreachableUrl) || Platform.DevToolsPath.EmptyUrlString;
        this.#adFrameStatusInternal = payload?.adFrameStatus;
        this.#secureContextType = payload && payload.secureContextType;
        this.#crossOriginIsolatedContextType = payload && payload.crossOriginIsolatedContextType;
        this.#gatedAPIFeatures = payload && payload.gatedAPIFeatures;
        this.#creationStackTrace = creationStackTrace;
        this.#creationStackTraceTarget = null;
        this.#childFramesInternal = new Set();
        this.resourcesMap = new Map();
        this.prerenderFinalStatus = null;
        this.prerenderDisallowedApiMethod = null;
        if (this.#sameTargetParentFrameInternal) {
            this.#sameTargetParentFrameInternal.#childFramesInternal.add(this);
        }
    }
    isSecureContext() {
        return this.#secureContextType !== null && this.#secureContextType.startsWith('Secure');
    }
    getSecureContextType() {
        return this.#secureContextType;
    }
    isCrossOriginIsolated() {
        return this.#crossOriginIsolatedContextType !== null && this.#crossOriginIsolatedContextType.startsWith('Isolated');
    }
    getCrossOriginIsolatedContextType() {
        return this.#crossOriginIsolatedContextType;
    }
    getGatedAPIFeatures() {
        return this.#gatedAPIFeatures;
    }
    getCreationStackTraceData() {
        return {
            creationStackTrace: this.#creationStackTrace,
            creationStackTraceTarget: this.#creationStackTraceTarget || this.resourceTreeModel().target(),
        };
    }
    navigate(framePayload) {
        this.#loaderIdInternal = framePayload.loaderId;
        this.#nameInternal = framePayload.name;
        this.#urlInternal = framePayload.url;
        this.#domainAndRegistryInternal = framePayload.domainAndRegistry;
        this.#securityOriginInternal = framePayload.securityOrigin;
        void this.getStorageKey(/* forceFetch */ true);
        this.#unreachableUrlInternal =
            framePayload.unreachableUrl || Platform.DevToolsPath.EmptyUrlString;
        this.#adFrameStatusInternal = framePayload?.adFrameStatus;
        this.#secureContextType = framePayload.secureContextType;
        this.#crossOriginIsolatedContextType = framePayload.crossOriginIsolatedContextType;
        this.#gatedAPIFeatures = framePayload.gatedAPIFeatures;
        this.backForwardCacheDetails = {
            restoredFromCache: undefined,
            explanations: [],
            explanationsTree: undefined,
        };
        const mainResource = this.resourcesMap.get(this.#urlInternal);
        this.resourcesMap.clear();
        this.removeChildFrames();
        if (mainResource && mainResource.loaderId === this.#loaderIdInternal) {
            this.addResource(mainResource);
        }
    }
    resourceTreeModel() {
        return this.#model;
    }
    get id() {
        return this.#idInternal;
    }
    get name() {
        return this.#nameInternal || '';
    }
    get url() {
        return this.#urlInternal;
    }
    domainAndRegistry() {
        return this.#domainAndRegistryInternal;
    }
    async getAdScriptId(frameId) {
        const res = await this.#model.agent.invoke_getAdScriptId({ frameId });
        return res.adScriptId || null;
    }
    get securityOrigin() {
        return this.#securityOriginInternal;
    }
    getStorageKey(forceFetch) {
        if (!this.#storageKeyInternal || forceFetch) {
            this.#storageKeyInternal = this.#model.storageKeyForFrame(this.#idInternal);
        }
        return this.#storageKeyInternal;
    }
    unreachableUrl() {
        return this.#unreachableUrlInternal;
    }
    get loaderId() {
        return this.#loaderIdInternal;
    }
    adFrameType() {
        return this.#adFrameStatusInternal?.adFrameType || "none" /* Protocol.Page.AdFrameType.None */;
    }
    adFrameStatus() {
        return this.#adFrameStatusInternal;
    }
    get childFrames() {
        return [...this.#childFramesInternal];
    }
    /**
     * Returns the parent frame if both #frames are part of the same process/target.
     */
    sameTargetParentFrame() {
        return this.#sameTargetParentFrameInternal;
    }
    /**
     * Returns the parent frame if both #frames are part of different processes/targets (child is an OOPIF).
     */
    crossTargetParentFrame() {
        if (!this.crossTargetParentFrameId) {
            return null;
        }
        const parentTarget = this.#model.target().parentTarget();
        if (parentTarget?.type() !== Type.Frame) {
            return null;
        }
        const parentModel = parentTarget.model(ResourceTreeModel);
        if (!parentModel) {
            return null;
        }
        // Note that parent #model has already processed cached resources:
        // - when parent target was created, we issued getResourceTree call;
        // - strictly after we issued setAutoAttach call;
        // - both of them were handled in renderer in the same order;
        // - cached resource tree got processed on parent #model;
        // - child target was created as a result of setAutoAttach call.
        return parentModel.framesInternal.get(this.crossTargetParentFrameId) || null;
    }
    /**
     * Returns the parent frame. There is only 1 parent and it's either in the
     * same target or it's cross-target.
     */
    parentFrame() {
        return this.sameTargetParentFrame() || this.crossTargetParentFrame();
    }
    /**
     * Returns true if this is the main frame of its target. A main frame is the root of the frame tree i.e. a frame without
     * a parent, but the whole frame tree could be embedded in another frame tree (e.g. OOPIFs, fenced frames, portals).
     * https://chromium.googlesource.com/chromium/src/+/HEAD/docs/frame_trees.md
     */
    isMainFrame() {
        return !this.#sameTargetParentFrameInternal;
    }
    /**
     * Returns true if this is a main frame which is not embedded in another frame tree. With MPArch features such as
     * back/forward cache or prerender there can be multiple outermost frames.
     * https://chromium.googlesource.com/chromium/src/+/HEAD/docs/frame_trees.md
     */
    isOutermostFrame() {
        return this.#model.target().parentTarget()?.type() !== Type.Frame && !this.#sameTargetParentFrameInternal &&
            !this.crossTargetParentFrameId;
    }
    /**
     * Returns true is this is the primary frame of the browser tab. There can only be one primary frame for each
     * browser tab. It is the outermost frame being actively displayed in the browser tab.
     * https://chromium.googlesource.com/chromium/src/+/HEAD/docs/frame_trees.md
     */
    isPrimaryFrame() {
        return !this.#sameTargetParentFrameInternal &&
            this.#model.target() === TargetManager.instance().primaryPageTarget();
    }
    removeChildFrame(frame, isSwap) {
        this.#childFramesInternal.delete(frame);
        frame.remove(isSwap);
    }
    removeChildFrames() {
        const frames = this.#childFramesInternal;
        this.#childFramesInternal = new Set();
        for (const frame of frames) {
            frame.remove(false);
        }
    }
    remove(isSwap) {
        this.removeChildFrames();
        this.#model.framesInternal.delete(this.id);
        this.#model.dispatchEventToListeners(Events.FrameDetached, { frame: this, isSwap });
    }
    addResource(resource) {
        if (this.resourcesMap.get(resource.url) === resource) {
            // Already in the tree, we just got an extra update.
            return;
        }
        this.resourcesMap.set(resource.url, resource);
        this.#model.dispatchEventToListeners(Events.ResourceAdded, resource);
    }
    addRequest(request) {
        let resource = this.resourcesMap.get(request.url());
        if (resource && resource.request === request) {
            // Already in the tree, we just got an extra update.
            return;
        }
        resource = new Resource(this.#model, request, request.url(), request.documentURL, request.frameId, request.loaderId, request.resourceType(), request.mimeType, null, null);
        this.resourcesMap.set(resource.url, resource);
        this.#model.dispatchEventToListeners(Events.ResourceAdded, resource);
    }
    resources() {
        return Array.from(this.resourcesMap.values());
    }
    resourceForURL(url) {
        const resource = this.resourcesMap.get(url);
        if (resource) {
            return resource;
        }
        for (const frame of this.#childFramesInternal) {
            const resource = frame.resourceForURL(url);
            if (resource) {
                return resource;
            }
        }
        return null;
    }
    callForFrameResources(callback) {
        for (const resource of this.resourcesMap.values()) {
            if (callback(resource)) {
                return true;
            }
        }
        for (const frame of this.#childFramesInternal) {
            if (frame.callForFrameResources(callback)) {
                return true;
            }
        }
        return false;
    }
    displayName() {
        if (this.isOutermostFrame()) {
            return i18n.i18n.lockedString('top');
        }
        const subtitle = new Common.ParsedURL.ParsedURL(this.#urlInternal).displayName;
        if (subtitle) {
            if (!this.#nameInternal) {
                return subtitle;
            }
            return this.#nameInternal + ' (' + subtitle + ')';
        }
        return i18n.i18n.lockedString('iframe');
    }
    async getOwnerDeferredDOMNode() {
        const parentFrame = this.parentFrame();
        if (!parentFrame) {
            return null;
        }
        return parentFrame.resourceTreeModel().domModel().getOwnerNodeForFrame(this.#idInternal);
    }
    async getOwnerDOMNodeOrDocument() {
        const deferredNode = await this.getOwnerDeferredDOMNode();
        if (deferredNode) {
            return deferredNode.resolvePromise();
        }
        if (this.isOutermostFrame()) {
            return this.resourceTreeModel().domModel().requestDocument();
        }
        return null;
    }
    async highlight() {
        const parentFrame = this.parentFrame();
        const parentTarget = this.resourceTreeModel().target().parentTarget();
        const highlightFrameOwner = async (domModel) => {
            const deferredNode = await domModel.getOwnerNodeForFrame(this.#idInternal);
            if (deferredNode) {
                domModel.overlayModel().highlightInOverlay({ deferredNode, selectorList: '' }, 'all', true);
            }
        };
        if (parentFrame) {
            return highlightFrameOwner(parentFrame.resourceTreeModel().domModel());
        }
        // Portals.
        if (parentTarget?.type() === Type.Frame) {
            const domModel = parentTarget.model(DOMModel);
            if (domModel) {
                return highlightFrameOwner(domModel);
            }
        }
        // For the outermost frame there is no owner node. Highlight the whole #document instead.
        const document = await this.resourceTreeModel().domModel().requestDocument();
        if (document) {
            this.resourceTreeModel().domModel().overlayModel().highlightInOverlay({ node: document, selectorList: '' }, 'all', true);
        }
    }
    async getPermissionsPolicyState() {
        const response = await this.resourceTreeModel().target().pageAgent().invoke_getPermissionsPolicyState({ frameId: this.#idInternal });
        if (response.getError()) {
            return null;
        }
        return response.states;
    }
    async getOriginTrials() {
        const response = await this.resourceTreeModel().target().pageAgent().invoke_getOriginTrials({ frameId: this.#idInternal });
        if (response.getError()) {
            return [];
        }
        return response.originTrials;
    }
    setCreationStackTrace(creationStackTraceData) {
        this.#creationStackTrace = creationStackTraceData.creationStackTrace;
        this.#creationStackTraceTarget = creationStackTraceData.creationStackTraceTarget;
    }
    setBackForwardCacheDetails(event) {
        this.backForwardCacheDetails.restoredFromCache = false;
        this.backForwardCacheDetails.explanations = event.notRestoredExplanations;
        this.backForwardCacheDetails.explanationsTree = event.notRestoredExplanationsTree;
    }
    getResourcesMap() {
        return this.resourcesMap;
    }
    setPrerenderFinalStatus(status) {
        this.prerenderFinalStatus = status;
    }
    setPrerenderDisallowedApiMethod(disallowedApiMethod) {
        this.prerenderDisallowedApiMethod = disallowedApiMethod;
    }
}
export class PageDispatcher {
    #resourceTreeModel;
    constructor(resourceTreeModel) {
        this.#resourceTreeModel = resourceTreeModel;
    }
    backForwardCacheNotUsed(params) {
        this.#resourceTreeModel.onBackForwardCacheNotUsed(params);
    }
    domContentEventFired({ timestamp }) {
        this.#resourceTreeModel.dispatchEventToListeners(Events.DOMContentLoaded, timestamp);
    }
    loadEventFired({ timestamp }) {
        this.#resourceTreeModel.dispatchEventToListeners(Events.Load, { resourceTreeModel: this.#resourceTreeModel, loadTime: timestamp });
    }
    lifecycleEvent({ frameId, name }) {
        this.#resourceTreeModel.dispatchEventToListeners(Events.LifecycleEvent, { frameId, name });
    }
    frameAttached({ frameId, parentFrameId, stack }) {
        this.#resourceTreeModel.frameAttached(frameId, parentFrameId, stack);
    }
    frameNavigated({ frame, type }) {
        this.#resourceTreeModel.frameNavigated(frame, type);
    }
    documentOpened({ frame }) {
        this.#resourceTreeModel.documentOpened(frame);
    }
    frameDetached({ frameId, reason }) {
        this.#resourceTreeModel.frameDetached(frameId, reason === "swap" /* Protocol.Page.FrameDetachedEventReason.Swap */);
    }
    frameStartedLoading({}) {
    }
    frameStoppedLoading({}) {
    }
    frameRequestedNavigation({}) {
    }
    frameScheduledNavigation({}) {
    }
    frameClearedScheduledNavigation({}) {
    }
    navigatedWithinDocument({}) {
    }
    frameResized() {
        this.#resourceTreeModel.dispatchEventToListeners(Events.FrameResized);
    }
    javascriptDialogOpening(event) {
        this.#resourceTreeModel.dispatchEventToListeners(Events.JavaScriptDialogOpening, event);
        if (!event.hasBrowserHandler) {
            void this.#resourceTreeModel.agent.invoke_handleJavaScriptDialog({ accept: false });
        }
    }
    javascriptDialogClosed({}) {
    }
    screencastFrame({}) {
    }
    screencastVisibilityChanged({}) {
    }
    interstitialShown() {
        this.#resourceTreeModel.isInterstitialShowing = true;
        this.#resourceTreeModel.dispatchEventToListeners(Events.InterstitialShown);
    }
    interstitialHidden() {
        this.#resourceTreeModel.isInterstitialShowing = false;
        this.#resourceTreeModel.dispatchEventToListeners(Events.InterstitialHidden);
    }
    windowOpen({}) {
    }
    compilationCacheProduced({}) {
    }
    fileChooserOpened({}) {
    }
    downloadWillBegin({}) {
    }
    downloadProgress() {
    }
}
class PreloadDispatcher {
    #resourceTreeModel;
    constructor(resourceTreeModel) {
        this.#resourceTreeModel = resourceTreeModel;
    }
    ruleSetUpdated(_event) {
    }
    ruleSetRemoved(_event) {
    }
    prerenderAttemptCompleted(params) {
        this.#resourceTreeModel.onPrerenderAttemptCompleted(params);
    }
    prefetchStatusUpdated(_event) {
    }
    prerenderStatusUpdated(_event) {
    }
    preloadEnabledStateUpdated(_event) {
    }
    preloadingAttemptSourcesUpdated() {
    }
}
SDKModel.register(ResourceTreeModel, { capabilities: Capability.DOM, autostart: true, early: true });
//# sourceMappingURL=ResourceTreeModel.js.map