debug-server-next
Version:
Dev server for hippy-core.
856 lines (855 loc) • 34.7 kB
JavaScript
/*
* 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.
*/
/* eslint-disable rulesdir/no_underscored_properties */
import * as Common from '../common/common.js';
import * as i18n from '../i18n/i18n.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 } from './Target.js';
import { SDKModel } from './SDKModel.js';
import { TargetManager } from './TargetManager.js';
import { SecurityOriginManager } from './SecurityOriginManager.js';
export class ResourceTreeModel extends SDKModel {
_agent;
_securityOriginManager;
_frames;
_cachedResourcesProcessed;
_pendingReloadOptions;
_reloadSuspensionCount;
_isInterstitialShowing;
mainFrame;
pendingBackForwardCacheNotUsedEvents;
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._agent.invoke_enable();
this._securityOriginManager = target.model(SecurityOriginManager);
this.pendingBackForwardCacheNotUsedEvents = new Set();
target.registerPageDispatcher(new PageDispatcher(this));
this._frames = new Map();
this._cachedResourcesProcessed = false;
this._pendingReloadOptions = null;
this._reloadSuspensionCount = 0;
this._isInterstitialShowing = false;
this.mainFrame = null;
this._agent.invoke_getResourceTree().then(event => {
this._processCachedResources(event.getError() ? null : event.frameTree);
});
}
static frameForRequest(request) {
const networkManager = NetworkManager.forRequest(request);
const resourceTreeModel = networkManager ? networkManager.target().model(ResourceTreeModel) : null;
if (!resourceTreeModel) {
return null;
}
return resourceTreeModel.frameForId(request.frameId);
}
static frames() {
const result = [];
for (const resourceTreeModel of TargetManager.instance().models(ResourceTreeModel)) {
result.push(...resourceTreeModel._frames.values());
}
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()) {
resourceTreeModel.reloadPage(bypassCache, scriptToEvaluateOnLoad);
}
}
}
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;
}
isInterstitialShowing() {
return this._isInterstitialShowing;
}
_addFrame(frame, _aboutToNavigate) {
this._frames.set(frame.id, frame);
if (frame.isMainFrame()) {
this.mainFrame = frame;
}
this.dispatchEventToListeners(Events.FrameAdded, frame);
this._updateSecurityOrigins();
}
_frameAttached(frameId, parentFrameId, stackTrace) {
const sameTargetParentFrame = parentFrameId ? (this._frames.get(parentFrameId) || null) : null;
// Do nothing unless cached resource tree is processed - it will overwrite everything.
if (!this._cachedResourcesProcessed && sameTargetParentFrame) {
return null;
}
if (this._frames.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._frames.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._frames.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 || '');
console.assert(Boolean(frame));
if (!frame) {
return;
}
}
this.dispatchEventToListeners(Events.FrameWillNavigate, frame);
frame._navigate(framePayload);
if (type) {
frame.backForwardCacheDetails.restoredFromCache = type === "BackForwardCacheRestore" /* BackForwardCacheRestore */;
}
this.dispatchEventToListeners(Events.FrameNavigated, frame);
if (frame.isMainFrame()) {
this.processPendingBackForwardCacheNotUsedEvents(frame);
this.dispatchEventToListeners(Events.MainFrameNavigated, frame);
const networkManager = this.target().model(NetworkManager);
if (networkManager) {
networkManager.clearRequests();
}
}
// 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();
}
_documentOpened(framePayload) {
this._frameNavigated(framePayload, undefined);
const frame = this._frames.get(framePayload.id);
if (frame && !frame._resourcesMap.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._frames.get(frameId);
if (!frame) {
return;
}
const sameTargetParentFrame = frame.sameTargetParentFrame();
if (sameTargetParentFrame) {
sameTargetParentFrame._removeChildFrame(frame, isSwap);
}
else {
frame._remove(isSwap);
}
this._updateSecurityOrigins();
}
_onRequestFinished(event) {
if (!this._cachedResourcesProcessed) {
return;
}
const request = event.data;
if (request.failed || request.resourceType() === Common.ResourceType.resourceTypes.XHR) {
return;
}
const frame = this._frames.get(request.frameId);
if (frame) {
frame._addRequest(request);
}
}
_onRequestUpdateDropped(event) {
if (!this._cachedResourcesProcessed) {
return;
}
const data = event.data;
const frameId = data.frameId;
const frame = this._frames.get(frameId);
if (!frame) {
return;
}
const url = data.url;
if (frame._resourcesMap.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._frames.get(frameId) || null;
}
forAllResources(callback) {
if (this.mainFrame) {
return this.mainFrame._callForFrameResources(callback);
}
return false;
}
frames() {
return [...this._frames.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;
const 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._resourcesMap.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);
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) {
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 getManifestIcons() {
const response = await this._agent.invoke_getManifestIcons();
return { primaryIcon: response.primaryIcon || null };
}
_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._frames.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,
};
}
_updateSecurityOrigins() {
const data = this._getSecurityOriginData();
this._securityOriginManager.setMainSecurityOrigin(data.mainSecurityOrigin || '', data.unreachableMainSecurityOrigin || '');
this._securityOriginManager.updateSecurityOrigins(data.securityOrigins);
}
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);
}
}
processPendingBackForwardCacheNotUsedEvents(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);
// No need to dispatch the `BackForwardCacheDetailsUpdated` event here,
// as this method call is followed by a `MainFrameNavigated` event.
return;
}
}
}
}
// 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";
Events["MainFrameNavigated"] = "MainFrameNavigated";
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 || (Events = {}));
export class ResourceTreeFrame {
_model;
_sameTargetParentFrame;
_id;
_crossTargetParentFrameId;
_loaderId;
_name;
_url;
_domainAndRegistry;
_securityOrigin;
_mimeType;
_unreachableUrl;
_adFrameStatus;
_secureContextType;
_crossOriginIsolatedContextType;
_gatedAPIFeatures;
originTrials;
creationStackTrace;
creationStackTraceTarget;
_childFrames;
_resourcesMap;
backForwardCacheDetails = { restoredFromCache: undefined, explanations: [] };
constructor(model, parentFrame, frameId, payload, creationStackTrace) {
this._model = model;
this._sameTargetParentFrame = parentFrame;
this._id = frameId;
this._crossTargetParentFrameId = null;
this._loaderId = (payload && payload.loaderId) || '';
this._name = payload && payload.name;
this._url = (payload && payload.url) || '';
this._domainAndRegistry = (payload && payload.domainAndRegistry) || '';
this._securityOrigin = payload && payload.securityOrigin;
this._mimeType = payload && payload.mimeType;
this._unreachableUrl = (payload && payload.unreachableUrl) || '';
this._adFrameStatus = payload?.adFrameStatus;
this._secureContextType = payload && payload.secureContextType;
this._crossOriginIsolatedContextType = payload && payload.crossOriginIsolatedContextType;
this._gatedAPIFeatures = payload && payload.gatedAPIFeatures;
this.originTrials = (payload && payload.originTrials) || null;
this.creationStackTrace = creationStackTrace;
this.creationStackTraceTarget = null;
this._childFrames = new Set();
this._resourcesMap = new Map();
if (this._sameTargetParentFrame) {
this._sameTargetParentFrame._childFrames.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;
}
getOriginTrials() {
return this.originTrials;
}
getCreationStackTraceData() {
return {
creationStackTrace: this.creationStackTrace,
creationStackTraceTarget: this.creationStackTraceTarget || this.resourceTreeModel().target(),
};
}
_navigate(framePayload) {
this._loaderId = framePayload.loaderId;
this._name = framePayload.name;
this._url = framePayload.url;
this._domainAndRegistry = framePayload.domainAndRegistry;
this._securityOrigin = framePayload.securityOrigin;
this._mimeType = framePayload.mimeType;
this._unreachableUrl = framePayload.unreachableUrl || '';
this._adFrameStatus = framePayload?.adFrameStatus;
this._secureContextType = framePayload.secureContextType;
this._crossOriginIsolatedContextType = framePayload.crossOriginIsolatedContextType;
this._gatedAPIFeatures = framePayload.gatedAPIFeatures;
this.backForwardCacheDetails = { restoredFromCache: undefined, explanations: [] };
this.originTrials = framePayload.originTrials || null;
const mainResource = this._resourcesMap.get(this._url);
this._resourcesMap.clear();
this._removeChildFrames();
if (mainResource && mainResource.loaderId === this._loaderId) {
this.addResource(mainResource);
}
}
resourceTreeModel() {
return this._model;
}
get id() {
return this._id;
}
get name() {
return this._name || '';
}
get url() {
return this._url;
}
domainAndRegistry() {
return this._domainAndRegistry;
}
get securityOrigin() {
return this._securityOrigin;
}
unreachableUrl() {
return this._unreachableUrl;
}
get loaderId() {
return this._loaderId;
}
adFrameType() {
return this._adFrameStatus?.adFrameType || "none" /* None */;
}
adFrameStatus() {
return this._adFrameStatus;
}
get childFrames() {
return [...this._childFrames];
}
/**
* Returns the parent frame if both frames are part of the same process/target.
*/
sameTargetParentFrame() {
return this._sameTargetParentFrame;
}
/**
* 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) {
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._frames.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. For example, this returns true for the main frame
* of an out-of-process iframe (OOPIF).
*/
isMainFrame() {
return !this._sameTargetParentFrame;
}
/**
* Returns true if this is the top frame of the main target, i.e. if this is the top-most frame in the inspected
* tab.
*/
isTopFrame() {
return !this._model.target().parentTarget() && !this._sameTargetParentFrame && !this._crossTargetParentFrameId;
}
_removeChildFrame(frame, isSwap) {
this._childFrames.delete(frame);
frame._remove(isSwap);
}
_removeChildFrames() {
const frames = this._childFrames;
this._childFrames = new Set();
for (const frame of frames) {
frame._remove(false);
}
}
_remove(isSwap) {
this._removeChildFrames();
this._model._frames.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._childFrames) {
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._childFrames) {
if (frame._callForFrameResources(callback)) {
return true;
}
}
return false;
}
displayName() {
if (this.isTopFrame()) {
return i18n.i18n.lockedString('top');
}
const subtitle = new Common.ParsedURL.ParsedURL(this._url).displayName;
if (subtitle) {
if (!this._name) {
return subtitle;
}
return this._name + ' (' + subtitle + ')';
}
return i18n.i18n.lockedString('iframe');
}
async getOwnerDeferredDOMNode() {
const parentFrame = this.parentFrame();
if (!parentFrame) {
return null;
}
return parentFrame.resourceTreeModel().domModel().getOwnerNodeForFrame(this._id);
}
async getOwnerDOMNodeOrDocument() {
const deferredNode = await this.getOwnerDeferredDOMNode();
if (deferredNode) {
return deferredNode.resolvePromise();
}
if (this.isTopFrame()) {
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._id);
if (deferredNode) {
domModel.overlayModel().highlightInOverlay({ deferredNode, selectorList: '' }, 'all', true);
}
};
if (parentFrame) {
return highlightFrameOwner(parentFrame.resourceTreeModel().domModel());
}
// Portals.
if (parentTarget) {
const domModel = parentTarget.model(DOMModel);
if (domModel) {
return highlightFrameOwner(domModel);
}
}
// For the top 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._id });
if (response.getError()) {
return null;
}
return response.states;
}
setCreationStackTrace(creationStackTraceData) {
this.creationStackTrace = creationStackTraceData.creationStackTrace;
this.creationStackTraceTarget = creationStackTraceData.creationStackTraceTarget;
}
setBackForwardCacheDetails(event) {
this.backForwardCacheDetails.restoredFromCache = false;
this.backForwardCacheDetails.explanations = event.notRestoredExplanations;
}
}
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" /* Swap */);
}
frameStartedLoading({}) {
}
frameStoppedLoading({}) {
}
frameRequestedNavigation({}) {
}
frameScheduledNavigation({}) {
}
frameClearedScheduledNavigation({}) {
}
navigatedWithinDocument({}) {
}
frameResized() {
this._resourceTreeModel.dispatchEventToListeners(Events.FrameResized);
}
javascriptDialogOpening({ hasBrowserHandler }) {
if (!hasBrowserHandler) {
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() {
}
}
SDKModel.register(ResourceTreeModel, { capabilities: Capability.DOM, autostart: true, early: true });