rebrowser-playwright-core
Version:
A drop-in replacement for playwright-core patched with rebrowser-patches. It allows to pass modern automation detection tests.
161 lines (158 loc) • 5.77 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Snapshotter = void 0;
var _browserContext = require("../../browserContext");
var _page = require("../../page");
var _eventsHelper = require("../../../utils/eventsHelper");
var _debugLogger = require("../../../utils/debugLogger");
var _snapshotterInjected = require("./snapshotterInjected");
var _utils = require("../../../utils");
var _utilsBundle = require("../../../utilsBundle");
/**
* Copyright (c) Microsoft Corporation.
*
* 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.
*/
class Snapshotter {
constructor(context, delegate) {
this._context = void 0;
this._delegate = void 0;
this._eventListeners = [];
this._snapshotStreamer = void 0;
this._initialized = false;
this._started = false;
this._context = context;
this._delegate = delegate;
const guid = (0, _utils.createGuid)();
this._snapshotStreamer = '__playwright_snapshot_streamer_' + guid;
}
started() {
return this._started;
}
async start() {
this._started = true;
if (!this._initialized) {
this._initialized = true;
await this._initialize();
}
await this.reset();
}
async reset() {
if (this._started) await this._runInAllFrames(`window["${this._snapshotStreamer}"].reset()`);
}
async stop() {
this._started = false;
}
resetForReuse() {
// Next time we start recording, we will call addInitScript again.
this._initialized = false;
}
async _initialize() {
for (const page of this._context.pages()) this._onPage(page);
this._eventListeners = [_eventsHelper.eventsHelper.addEventListener(this._context, _browserContext.BrowserContext.Events.Page, this._onPage.bind(this))];
const {
javaScriptEnabled
} = this._context._options;
const initScript = `(${_snapshotterInjected.frameSnapshotStreamer})("${this._snapshotStreamer}", ${javaScriptEnabled || javaScriptEnabled === undefined})`;
await this._context.addInitScript(initScript);
await this._runInAllFrames(initScript);
}
async _runInAllFrames(expression) {
const frames = [];
for (const page of this._context.pages()) frames.push(...page.frames());
await Promise.all(frames.map(frame => {
return frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => _debugLogger.debugLogger.log('error', e));
}));
}
dispose() {
_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
}
async captureSnapshot(page, callId, snapshotName) {
// Prepare expression synchronously.
const expression = `window["${this._snapshotStreamer}"].captureSnapshot(${JSON.stringify(snapshotName)})`;
// In each frame, in a non-stalling manner, capture the snapshots.
const snapshots = page.frames().map(async frame => {
const data = await frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => _debugLogger.debugLogger.log('error', e));
// Something went wrong -> bail out, our snapshots are best-efforty.
if (!data || !this._started) return;
const snapshot = {
callId,
snapshotName,
pageId: page.guid,
frameId: frame.guid,
frameUrl: data.url,
doctype: data.doctype,
html: data.html,
viewport: data.viewport,
timestamp: (0, _utils.monotonicTime)(),
wallTime: data.wallTime,
collectionTime: data.collectionTime,
resourceOverrides: [],
isMainFrame: page.mainFrame() === frame
};
for (const {
url,
content,
contentType
} of data.resourceOverrides) {
if (typeof content === 'string') {
const buffer = Buffer.from(content);
const sha1 = (0, _utils.calculateSha1)(buffer) + '.' + (_utilsBundle.mime.getExtension(contentType) || 'dat');
this._delegate.onSnapshotterBlob({
sha1,
buffer
});
snapshot.resourceOverrides.push({
url,
sha1
});
} else {
snapshot.resourceOverrides.push({
url,
ref: content
});
}
}
this._delegate.onFrameSnapshot(snapshot);
});
await Promise.all(snapshots);
}
_onPage(page) {
// Annotate frame hierarchy so that snapshots could include frame ids.
for (const frame of page.frames()) this._annotateFrameHierarchy(frame);
this._eventListeners.push(_eventsHelper.eventsHelper.addEventListener(page, _page.Page.Events.FrameAttached, frame => this._annotateFrameHierarchy(frame)));
}
async _annotateFrameHierarchy(frame) {
try {
const frameElement = await frame.frameElement();
const parent = frame.parentFrame();
if (!parent) return;
const context = await parent._mainContext();
await (context === null || context === void 0 ? void 0 : context.evaluate(({
snapshotStreamer,
frameElement,
frameId
}) => {
window[snapshotStreamer].markIframe(frameElement, frameId);
}, {
snapshotStreamer: this._snapshotStreamer,
frameElement,
frameId: frame.guid
}));
frameElement.dispose();
} catch (e) {}
}
}
exports.Snapshotter = Snapshotter;