Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
META-INF.dirigible.dev-tools.sdk.ResourceTreeModel.js Maven / Gradle / Ivy
/*
* 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 ProtocolClient from '../protocol_client/protocol_client.js';
import {DOMModel} from './DOMModel.js';
import {Events as NetworkManagerEvents, NetworkManager} from './NetworkManager.js';
import {NetworkRequest} from './NetworkRequest.js'; // eslint-disable-line no-unused-vars
import {Resource} from './Resource.js';
import {ExecutionContext, RuntimeModel} from './RuntimeModel.js';
import {Capability, SDKModel, Target, TargetManager} from './SDKModel.js'; // eslint-disable-line no-unused-vars
import {SecurityOriginManager} from './SecurityOriginManager.js';
export class ResourceTreeModel extends SDKModel {
/**
* @param {!Target} target
*/
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.enable();
this._securityOriginManager = target.model(SecurityOriginManager);
target.registerPageDispatcher(new PageDispatcher(this));
/** @type {!Map} */
this._frames = new Map();
this._cachedResourcesProcessed = false;
this._pendingReloadOptions = null;
this._reloadSuspensionCount = 0;
this._isInterstitialShowing = false;
/** @type {?ResourceTreeFrame} */
this.mainFrame = null;
this._agent.getResourceTree().then(this._processCachedResources.bind(this));
}
/**
* @param {!NetworkRequest} request
* @return {?ResourceTreeFrame}
*/
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);
}
/**
* @return {!Array.}
*/
static frames() {
const result = [];
for (const resourceTreeModel of TargetManager.instance().models(ResourceTreeModel)) {
result.push(...resourceTreeModel._frames.values());
}
return result;
}
/**
* @param {string} url
* @return {?Resource}
*/
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;
}
/**
* @param {boolean=} bypassCache
* @param {string=} scriptToEvaluateOnLoad
*/
static reloadAllPages(bypassCache, scriptToEvaluateOnLoad) {
for (const resourceTreeModel of TargetManager.instance().models(ResourceTreeModel)) {
if (!resourceTreeModel.target().parentTarget()) {
resourceTreeModel.reloadPage(bypassCache, scriptToEvaluateOnLoad);
}
}
}
/**
* @return {!DOMModel}
*/
domModel() {
return /** @type {!DOMModel} */ (this.target().model(DOMModel));
}
/**
* @param {?Protocol.Page.FrameResourceTree} mainFramePayload
*/
_processCachedResources(mainFramePayload) {
if (mainFramePayload) {
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);
}
/**
* @return {boolean}
*/
cachedResourcesLoaded() {
return this._cachedResourcesProcessed;
}
/**
* @return {boolean}
*/
isInterstitialShowing() {
return this._isInterstitialShowing;
}
/**
* @param {!ResourceTreeFrame} frame
* @param {boolean=} aboutToNavigate
*/
_addFrame(frame, aboutToNavigate) {
this._frames.set(frame.id, frame);
if (frame.isMainFrame()) {
this.mainFrame = frame;
}
this.dispatchEventToListeners(Events.FrameAdded, frame);
this._updateSecurityOrigins();
}
/**
* @param {!Protocol.Page.FrameId} frameId
* @param {?Protocol.Page.FrameId} parentFrameId
* @param {!Protocol.Runtime.StackTrace=} stackTrace
* @return {?ResourceTreeFrame}
*/
_frameAttached(frameId, parentFrameId, stackTrace) {
const parentFrame = parentFrameId ? (this._frames.get(parentFrameId) || null) : null;
// Do nothing unless cached resource tree is processed - it will overwrite everything.
if (!this._cachedResourcesProcessed && parentFrame) {
return null;
}
if (this._frames.has(frameId)) {
return null;
}
const frame = new ResourceTreeFrame(this, parentFrame, frameId, null, stackTrace || null);
if (parentFrameId && !parentFrame) {
frame._crossTargetParentFrameId = parentFrameId;
}
if (frame.isMainFrame() && this.mainFrame) {
// Navigation to the new backend process.
this._frameDetached(this.mainFrame.id);
}
this._addFrame(frame, true);
return frame;
}
/**
* @param {!Protocol.Page.Frame} framePayload
*/
_frameNavigated(framePayload) {
const parentFrame = framePayload.parentId ? (this._frames.get(framePayload.parentId) || null) : null;
// Do nothing unless cached resource tree is processed - it will overwrite everything.
if (!this._cachedResourcesProcessed && parentFrame) {
return;
}
let frame = this._frames.get(framePayload.id);
if (!frame) {
// Simulate missed "frameAttached" for a main frame navigation to the new backend process.
frame = this._frameAttached(framePayload.id, framePayload.parentId || '');
console.assert(frame);
}
this.dispatchEventToListeners(Events.FrameWillNavigate, frame);
frame._navigate(framePayload);
this.dispatchEventToListeners(Events.FrameNavigated, frame);
if (frame.isMainFrame()) {
this.dispatchEventToListeners(Events.MainFrameNavigated, frame);
}
// 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();
}
/**
* @param {!Protocol.Page.FrameId} frameId
*/
_frameDetached(frameId) {
// 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;
}
if (frame.parentFrame) {
frame.parentFrame._removeChildFrame(frame);
} else {
frame._remove();
}
this._updateSecurityOrigins();
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_onRequestFinished(event) {
if (!this._cachedResourcesProcessed) {
return;
}
const request = /** @type {!NetworkRequest} */ (event.data);
if (request.failed || request.resourceType() === Common.ResourceType.resourceTypes.XHR) {
return;
}
const frame = this._frames.get(request.frameId);
if (frame) {
frame._addRequest(request);
}
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_onRequestUpdateDropped(event) {
if (!this._cachedResourcesProcessed) {
return;
}
const frameId = event.data.frameId;
const frame = this._frames.get(frameId);
if (!frame) {
return;
}
const url = event.data.url;
if (frame._resourcesMap[url]) {
return;
}
const resource = new Resource(
this, null, url, frame.url, frameId, event.data.loaderId,
Common.ResourceType.resourceTypes[event.data.resourceType], event.data.mimeType, event.data.lastModified, null);
frame.addResource(resource);
}
/**
* @param {!Protocol.Page.FrameId} frameId
* @return {!ResourceTreeFrame}
*/
frameForId(frameId) {
return this._frames.get(frameId);
}
/**
* @param {function(!Resource)} callback
* @return {boolean}
*/
forAllResources(callback) {
if (this.mainFrame) {
return this.mainFrame._callForFrameResources(callback);
}
return false;
}
/**
* @return {!Array}
*/
frames() {
return [...this._frames.values()];
}
/**
* @param {string} url
* @return {?Resource}
*/
resourceForURL(url) {
// Workers call into this with no frames available.
return this.mainFrame ? this.mainFrame.resourceForURL(url) : null;
}
/**
* @param {?ResourceTreeFrame} parentFrame
* @param {!Protocol.Page.FrameResourceTree} frameTreePayload
*/
_addFramesRecursively(parentFrame, frameTreePayload) {
const framePayload = frameTreePayload.frame;
const frame = new ResourceTreeFrame(this, parentFrame, framePayload.id, framePayload, null);
if (!parentFrame && 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[framePayload.url]) {
const frameResource = this._createResourceFromFramePayload(
framePayload, framePayload.url, Common.ResourceType.resourceTypes.Document, framePayload.mimeType, null,
null);
frame.addResource(frameResource);
}
}
/**
* @param {!Protocol.Page.Frame} frame
* @param {string} url
* @param {!Common.ResourceType.ResourceType} type
* @param {string} mimeType
* @param {?number} lastModifiedTime
* @param {?number} contentSize
* @return {!Resource}
*/
_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) {
this.reloadPage.apply(this, this._pendingReloadOptions);
}
}
/**
* @param {boolean=} bypassCache
* @param {string=} scriptToEvaluateOnLoad
*/
reloadPage(bypassCache, 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 = [bypassCache, scriptToEvaluateOnLoad];
return;
}
this._pendingReloadOptions = null;
this.dispatchEventToListeners(Events.WillReloadPage);
this._agent.reload(bypassCache, scriptToEvaluateOnLoad);
}
/**
* @param {string} url
* @return {!Promise}
*/
navigate(url) {
return this._agent.navigate(url);
}
/**
* @return {!Promise{currentIndex: number, entries: !Array}>}
*/
async navigationHistory() {
const response = await this._agent.invoke_getNavigationHistory({});
if (response[ProtocolClient.InspectorBackend.ProtocolError]) {
return null;
}
return {currentIndex: response.currentIndex, entries: response.entries};
}
/**
* @param {!Protocol.Page.NavigationEntry} entry
*/
navigateToHistoryEntry(entry) {
this._agent.navigateToHistoryEntry(entry.id);
}
/**
* @return {!Promise<{url: string, data: ?string, errors: !Array}>}
*/
async fetchAppManifest() {
const response = await this._agent.invoke_getAppManifest({});
if (response[ProtocolClient.InspectorBackend.ProtocolError]) {
return {url: response.url, data: null, errors: []};
}
return {url: response.url, data: response.data || null, errors: response.errors};
}
/**
* @return {!Promise>}
*/
async getInstallabilityErrors() {
const response = await this._agent.invoke_getInstallabilityErrors({});
return response.installabilityErrors || [];
}
/**
* @return {!Promise<{primaryIcon: ?string}>}
*/
async getManifestIcons() {
const response = await this._agent.invoke_getManifestIcons({});
return {primaryIcon: response.primaryIcon || null};
}
/**
* @param {!ExecutionContext} a
* @param {!ExecutionContext} b
* @return {number}
*/
_executionContextComparator(a, b) {
/**
* @param {!ResourceTreeFrame} frame
* @return {!Array}
*/
function framePath(frame) {
let currentFrame = frame;
const parents = [];
while (currentFrame) {
parents.push(currentFrame);
currentFrame = currentFrame.parentFrame;
}
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);
}
/**
* @return {!SecurityOriginData}
*/
_getSecurityOriginData() {
/** @type {!Set} */
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);
}
/**
* @return {?string}
*/
getMainSecurityOrigin() {
const data = this._getSecurityOriginData();
return data.mainSecurityOrigin || data.unreachableMainSecurityOrigin;
}
}
/** @enum {symbol} */
export const Events = {
FrameAdded: Symbol('FrameAdded'),
FrameNavigated: Symbol('FrameNavigated'),
FrameDetached: Symbol('FrameDetached'),
FrameResized: Symbol('FrameResized'),
FrameWillNavigate: Symbol('FrameWillNavigate'),
MainFrameNavigated: Symbol('MainFrameNavigated'),
ResourceAdded: Symbol('ResourceAdded'),
WillLoadCachedResources: Symbol('WillLoadCachedResources'),
CachedResourcesLoaded: Symbol('CachedResourcesLoaded'),
DOMContentLoaded: Symbol('DOMContentLoaded'),
LifecycleEvent: Symbol('LifecycleEvent'),
Load: Symbol('Load'),
PageReloadRequested: Symbol('PageReloadRequested'),
WillReloadPage: Symbol('WillReloadPage'),
InterstitialShown: Symbol('InterstitialShown'),
InterstitialHidden: Symbol('InterstitialHidden')
};
/**
* @unrestricted
*/
export class ResourceTreeFrame {
/**
* @param {!ResourceTreeModel} model
* @param {?ResourceTreeFrame} parentFrame
* @param {!Protocol.Page.FrameId} frameId
* @param {?Protocol.Page.Frame} payload
* @param {?Protocol.Runtime.StackTrace} creationStackTrace
*/
constructor(model, parentFrame, frameId, payload, creationStackTrace) {
this._model = model;
this._parentFrame = parentFrame;
this._id = frameId;
this._url = '';
this._crossTargetParentFrameId = null;
if (payload) {
this._loaderId = payload.loaderId;
this._name = payload.name;
this._url = payload.url;
this._securityOrigin = payload.securityOrigin;
this._mimeType = payload.mimeType;
this._unreachableUrl = payload.unreachableUrl || '';
}
this._creationStackTrace = creationStackTrace;
/**
* @type {!Set.}
*/
this._childFrames = new Set();
/**
* @type {!Object.}
*/
this._resourcesMap = {};
if (this._parentFrame) {
this._parentFrame._childFrames.add(this);
}
}
/**
* @param {!Protocol.Page.Frame} framePayload
*/
_navigate(framePayload) {
this._loaderId = framePayload.loaderId;
this._name = framePayload.name;
this._url = framePayload.url;
this._securityOrigin = framePayload.securityOrigin;
this._mimeType = framePayload.mimeType;
this._unreachableUrl = framePayload.unreachableUrl || '';
const mainResource = this._resourcesMap[this._url];
this._resourcesMap = {};
this._removeChildFrames();
if (mainResource && mainResource.loaderId === this._loaderId) {
this.addResource(mainResource);
}
}
/**
* @return {!ResourceTreeModel}
*/
resourceTreeModel() {
return this._model;
}
/**
* @return {string}
*/
get id() {
return this._id;
}
/**
* @return {string}
*/
get name() {
return this._name || '';
}
/**
* @return {string}
*/
get url() {
return this._url;
}
/**
* @return {string}
*/
get securityOrigin() {
return this._securityOrigin;
}
/**
* @return {string}
*/
unreachableUrl() {
return this._unreachableUrl;
}
/**
* @return {string}
*/
get loaderId() {
return this._loaderId;
}
/**
* @return {?ResourceTreeFrame}
*/
get parentFrame() {
return this._parentFrame;
}
/**
* @return {!Array.}
*/
get childFrames() {
return [...this._childFrames];
}
/**
* @return {?ResourceTreeFrame}
*/
crossTargetParentFrame() {
if (!this._crossTargetParentFrameId) {
return null;
}
if (!this._model.target().parentTarget()) {
return null;
}
const parentModel = this._model.target().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;
}
/**
* @param {function(!Protocol.Runtime.CallFrame):boolean} searchFn
* @return {?Protocol.Runtime.CallFrame}
*/
findCreationCallFrame(searchFn) {
let stackTrace = this._creationStackTrace;
while (stackTrace) {
const foundEntry = stackTrace.callFrames.find(searchFn);
if (foundEntry) {
return foundEntry;
}
stackTrace = this.parent;
}
return null;
}
/**
* @return {boolean}
*/
isMainFrame() {
return !this._parentFrame;
}
isTopFrame() {
return !this._parentFrame && !this._crossTargetParentFrameId;
}
/**
* @return {!Resource}
*/
get mainResource() {
return this._resourcesMap[this._url];
}
/**
* @param {!ResourceTreeFrame} frame
*/
_removeChildFrame(frame) {
this._childFrames.delete(frame);
frame._remove();
}
_removeChildFrames() {
const frames = this._childFrames;
this._childFrames = new Set();
for (const frame of frames) {
frame._remove();
}
}
_remove() {
this._removeChildFrames();
this._model._frames.delete(this.id);
this._model.dispatchEventToListeners(Events.FrameDetached, this);
}
/**
* @param {!Resource} resource
*/
addResource(resource) {
if (this._resourcesMap[resource.url] === resource) {
// Already in the tree, we just got an extra update.
return;
}
this._resourcesMap[resource.url] = resource;
this._model.dispatchEventToListeners(Events.ResourceAdded, resource);
}
/**
* @param {!NetworkRequest} request
*/
_addRequest(request) {
let resource = this._resourcesMap[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[resource.url] = resource;
this._model.dispatchEventToListeners(Events.ResourceAdded, resource);
}
/**
* @return {!Array.}
*/
resources() {
const result = [];
for (const url in this._resourcesMap) {
result.push(this._resourcesMap[url]);
}
return result;
}
/**
* @param {string} url
* @return {?Resource}
*/
resourceForURL(url) {
const resource = this._resourcesMap[url];
if (resource) {
return resource;
}
for (const frame of this._childFrames) {
const resource = frame.resourceForURL(url);
if (resource) {
return resource;
}
}
return null;
}
/**
* @param {function(!Resource)} callback
* @return {boolean}
*/
_callForFrameResources(callback) {
for (const url in this._resourcesMap) {
if (callback(this._resourcesMap[url])) {
return true;
}
}
for (const frame of this._childFrames) {
if (frame._callForFrameResources(callback)) {
return true;
}
}
return false;
}
/**
* @return {string}
*/
displayName() {
if (this.isTopFrame()) {
return Common.UIString.UIString('top');
}
const subtitle = new Common.ParsedURL.ParsedURL(this._url).displayName;
if (subtitle) {
if (!this._name) {
return subtitle;
}
return this._name + ' (' + subtitle + ')';
}
return Common.UIString.UIString('