puppeteer/lib/FrameManager.js

246 lines
5.9 KiB
JavaScript
Raw Normal View History

/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let EventEmitter = require('events');
class FrameManager extends EventEmitter {
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @param {!Connection} client
* @return {!FrameManager}
*/
2017-06-21 20:51:06 +00:00
static async create(client) {
let mainFramePayload = await client.send('Page.getResourceTree');
2017-06-21 20:51:06 +00:00
return new FrameManager(client, mainFramePayload.frameTree);
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @param {!Connection} client
* @param {!Object} frameTree
*/
2017-06-21 20:51:06 +00:00
constructor(client, frameTree) {
super();
this._client = client;
/** @type {!Map<string, !Frame>} */
this._frames = new Map();
this._mainFrame = this._addFramesRecursively(null, frameTree);
this._client.on('Page.frameAttached', event => this._frameAttached(event.frameId, event.parentFrameId));
this._client.on('Page.frameNavigated', event => this._frameNavigated(event.frame));
this._client.on('Page.frameDetached', event => this._frameDetached(event.frameId));
}
/**
2017-06-21 20:58:49 +00:00
* @return {!Frame}
*/
2017-06-21 20:51:06 +00:00
mainFrame() {
return this._mainFrame;
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @return {!Array<!Frame>}
*/
2017-06-21 20:51:06 +00:00
frames() {
return Array.from(this._frames.values());
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @param {string} frameId
* @param {?string} parentFrameId
* @return {?Frame}
*/
2017-06-21 20:51:06 +00:00
_frameAttached(frameId, parentFrameId) {
if (this._frames.has(frameId))
return;
if (!parentFrameId) {
// Navigation to the new backend process.
this._navigateFrame(this._mainFrame, frameId, null);
return;
}
let parentFrame = this._frames.get(parentFrameId);
let frame = new Frame(parentFrame, frameId, null);
2017-06-21 20:51:06 +00:00
this._frames.set(frame._id, frame);
this.emit(FrameManager.Events.FrameAttached, frame);
}
/**
2017-06-21 20:58:49 +00:00
* @param {!Object} framePayload
*/
2017-06-21 20:51:06 +00:00
_frameNavigated(framePayload) {
let frame = this._frames.get(framePayload.id);
2017-06-21 20:51:06 +00:00
if (!frame) {
// Simulate missed "frameAttached" for a main frame navigation to the new backend process.
console.assert(!framePayload.parentId, 'Main frame shouldn\'t have parent frame id.');
frame = this._mainFrame;
}
2017-06-21 20:51:06 +00:00
this._navigateFrame(frame, framePayload.id, framePayload);
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @param {string} frameId
*/
2017-06-21 20:51:06 +00:00
_frameDetached(frameId) {
let frame = this._frames.get(frameId);
2017-06-21 20:51:06 +00:00
if (frame)
this._removeFramesRecursively(frame);
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @param {!Frame} frame
* @param {string} newFrameId
* @param {?Object} newFramePayload
*/
2017-06-21 20:51:06 +00:00
_navigateFrame(frame, newFrameId, newFramePayload) {
// Detach all child frames first.
for (let child of frame.childFrames())
2017-06-21 20:51:06 +00:00
this._removeFramesRecursively(child);
this._frames.delete(frame._id, frame);
frame._id = newFrameId;
frame._adoptPayload(newFramePayload);
this._frames.set(newFrameId, frame);
this.emit(FrameManager.Events.FrameNavigated, frame);
}
/**
2017-06-21 20:58:49 +00:00
* @param {?Frame} parentFrame
* @param {!Object} frameTreePayload
* @return {!Frame}
*/
2017-06-21 20:51:06 +00:00
_addFramesRecursively(parentFrame, frameTreePayload) {
let framePayload = frameTreePayload.frame;
let frame = new Frame(parentFrame, framePayload.id, framePayload);
2017-06-21 20:51:06 +00:00
this._frames.set(frame._id, frame);
for (let i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i)
2017-06-21 20:51:06 +00:00
this._addFramesRecursively(frame, frameTreePayload.childFrames[i]);
return frame;
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @param {!Frame} frame
*/
2017-06-21 20:51:06 +00:00
_removeFramesRecursively(frame) {
for (let child of frame.childFrames())
2017-06-21 20:51:06 +00:00
this._removeFramesRecursively(child);
frame._detach();
this._frames.delete(frame._id);
this.emit(FrameManager.Events.FrameDetached, frame);
}
}
/** @enum {string} */
FrameManager.Events = {
2017-06-21 20:51:06 +00:00
FrameAttached: 'frameattached',
FrameNavigated: 'framenavigated',
FrameDetached: 'framedetached'
};
/**
* @unrestricted
*/
class Frame {
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @param {?Frame} parentFrame
* @param {string} frameId
* @param {?Object} payload
*/
2017-06-21 20:51:06 +00:00
constructor(parentFrame, frameId, payload) {
this._parentFrame = parentFrame;
this._url = '';
this._id = frameId;
this._adoptPayload(payload);
/** @type {!Set<!Frame>} */
this._childFrames = new Set();
if (this._parentFrame)
this._parentFrame._childFrames.add(this);
}
/**
2017-06-21 20:58:49 +00:00
* @return {string}
*/
2017-06-21 20:51:06 +00:00
name() {
return this._name || '';
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @return {string}
*/
2017-06-21 20:51:06 +00:00
url() {
return this._url;
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @return {string}
*/
2017-06-21 20:51:06 +00:00
securityOrigin() {
return this._securityOrigin;
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @return {?Frame}
*/
2017-06-21 20:51:06 +00:00
parentFrame() {
return this._parentFrame;
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @return {!Array.<!Frame>}
*/
2017-06-21 20:51:06 +00:00
childFrames() {
return Array.from(this._childFrames);
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @return {boolean}
*/
2017-06-21 20:51:06 +00:00
isMainFrame() {
return !this._detached && !this._parentFrame;
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @return {boolean}
*/
2017-06-21 20:51:06 +00:00
isDetached() {
return this._detached;
}
2017-06-21 20:51:06 +00:00
/**
2017-06-21 20:58:49 +00:00
* @param {?Object} framePayload
*/
2017-06-21 20:51:06 +00:00
_adoptPayload(framePayload) {
framePayload = framePayload || {
name: '',
url: '',
securityOrigin: '',
mimeType: ''
};
this._name = framePayload.name;
this._url = framePayload.url;
this._securityOrigin = framePayload.securityOrigin;
this._mimeType = framePayload.mimeType;
}
_detach() {
this._detached = true;
if (this._parentFrame)
this._parentFrame._childFrames.delete(this);
this._parentFrame = null;
}
}
module.exports = FrameManager;