Skip to content

Commit

Permalink
feat(timetravel): ability to attach existing page
Browse files Browse the repository at this point in the history
  • Loading branch information
blakebyrnes committed Mar 18, 2022
1 parent 01b78f5 commit 419e6d1
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 53 deletions.
19 changes: 12 additions & 7 deletions core/lib/Tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,7 @@ export default class Tab
headersFilter: ['set-cookie'],
loadResourceDetails: MirrorNetwork.loadResourceFromDb.bind(MirrorNetwork, this.session.db),
});
this.mirrorPage = new MirrorPage(this.mirrorNetwork, {
paintEvents: [],
mainFrameIds: new Set([this.mainFrameId]),
documents: [],
domNodePathByFrameId: this.session.db.frames.frameDomNodePathsById,
});
this.mirrorPage.subscribe(this);
this.mirrorPage = this.createMirrorPage();

this.listen();
this.isReady = this.waitForReady();
Expand Down Expand Up @@ -201,6 +195,17 @@ export default class Tab
]);
}

public createMirrorPage(): MirrorPage {
const mirrorPage = new MirrorPage(this.mirrorNetwork, {
paintEvents: [],
mainFrameIds: new Set([this.mainFrameId]),
documents: [],
domNodePathByFrameId: this.session.db.frames.frameDomNodePathsById,
});
mirrorPage.subscribe(this);
return mirrorPage;
}

public getFrameEnvironment(frameId?: number): FrameEnvironment {
return frameId ? this.frameEnvironmentsById.get(frameId) : this.mainFrameEnvironment;
}
Expand Down
1 change: 1 addition & 0 deletions interfaces/IPuppetContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default interface IPuppetContext extends ITypedEventEmitter<IPuppetContex
browserId: string;
isIncognito: boolean;
domStorage: IDomStorage;
pagesById: Map<string, IPuppetPage>;
workersById: Map<string, IPuppetWorker>;
defaultPageInitializationFn: (page: IPuppetPage) => Promise<any>;
sendWithBrowserDevtoolsSession<T extends keyof ProtocolMapping.Commands>(
Expand Down
12 changes: 11 additions & 1 deletion timetravel/injected-scripts/domReplayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ declare global {
interface Window {
loadPaintEvents(paintEvents: IFrontendDomChangeEvent[][]);
applyDomChanges(changes: IFrontendDomChangeEvent[]);
domReplayer: DomReplayer;
setPaintIndex(index: number);
repositionInteractElements();
debugLogs: any[];
Expand Down Expand Up @@ -41,6 +42,15 @@ class DomReplayer {
}
}

public reset(): void {
this.paintEvents.length =0;
this.loadedIndex = 0;
this.pendingDelegatedEventsByChildNodeId = {};
this.pendingDomChanges = [];
this.frameContentWindows = new WeakMap();
location.href = 'about:blank';
}

public loadPaintEvents(newPaintEvents: IFrontendDomChangeEvent[][]): void {
this.pendingDomChanges.length = 0;
for (let i = 0; i < newPaintEvents.length; i += 1) {
Expand Down Expand Up @@ -175,7 +185,7 @@ class DomReplayer {

static load() {
const replayer = new DomReplayer();
(window as any).domReplayer = replayer;
window.domReplayer = replayer;
window.loadPaintEvents = replayer.loadPaintEvents.bind(replayer);
window.setPaintIndex = replayer.setPaintIndex.bind(replayer);
window.applyDomChanges = replayer.applyDomChanges.bind(replayer);
Expand Down
96 changes: 51 additions & 45 deletions timetravel/lib/MirrorPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import IViewport from '@ulixee/hero-interfaces/IViewport';
import IPuppetContext from '@ulixee/hero-interfaces/IPuppetContext';
import { TypedEventEmitter } from '@ulixee/commons/lib/eventUtils';
import Resolvable from '@ulixee/commons/lib/Resolvable';
import InjectedScripts, {
CorePageInjectedScript,
showInteractionScript,
} from '@ulixee/hero-core/lib/InjectedScripts';
import InjectedScripts, { CorePageInjectedScript } from '@ulixee/hero-core/lib/InjectedScripts';
import { IMouseEventRecord } from '@ulixee/hero-core/models/MouseEventsTable';
import { IScrollRecord } from '@ulixee/hero-core/models/ScrollEventsTable';
import DomChangesTable, {
Expand All @@ -28,6 +25,8 @@ import { ITabEventParams } from '@ulixee/hero-core/lib/Tab';

const { log } = Log(module);

const installedScriptsSymbol = Symbol.for('MirrorPageScripts');

export default class MirrorPage extends TypedEventEmitter<{
close: void;
open: void;
Expand Down Expand Up @@ -62,66 +61,73 @@ export default class MirrorPage extends TypedEventEmitter<{
this.onPageEvents = this.onPageEvents.bind(this);
}

public async open(
context: IPuppetContext,
sessionId: string,
viewport?: IViewport,
onPage?: (page: IPuppetPage) => Promise<void>,
): Promise<void> {
if (this.isReady) return await this.isReady;

this.sessionId = sessionId;
const ready = new Resolvable<void>();
this.isReady = ready.promise;
public async attachToPage(page: IPuppetPage, sessionId: string, setReady = true): Promise<void> {
this.page = page;
let readyResolvable: Resolvable<void>;
if (setReady) {
readyResolvable = new Resolvable<void>();
this.isReady = readyResolvable.promise;
}
try {
this.page = await context.newPage({ runPageScripts: false, enableDomStorageTracker: false });
this.events.once(this.page, 'close', this.close.bind(this));
this.events.once(page, 'close', this.close.bind(this));
if (this.debugLogging) {
this.events.on(this.page, 'console', msg => {
this.events.on(page, 'console', msg => {
log.info('MirrorPage.console', {
...msg,
sessionId,
});
});
this.events.on(this.page, 'crashed', msg => {
this.events.on(page, 'crashed', msg => {
log.info('MirrorPage.crashed', {
...msg,
sessionId,
});
});
}

const promises = [];
if (onPage) promises.push(onPage(this.page));
promises.push(
this.page.setNetworkRequestInterceptor(this.network.mirrorNetworkRequests),
this.page.addNewDocumentScript(injectedScript, true),
this.showBrowserInteractions ? InjectedScripts.installInteractionScript(this.page) : null,
this.page.setJavaScriptEnabled(false),
);
if (this.showBrowserInteractions) {
const promises: Promise<any>[] = [
page.setNetworkRequestInterceptor(this.network.mirrorNetworkRequests),
];

if (page[installedScriptsSymbol]) {
await page.mainFrame.evaluate(`window.domReplayer.reset()`, true);
} else {
promises.push(
this.page.mainFrame.evaluate(
`(() => {
window.isMainFrame = true;
${injectedScript};
${showInteractionScript};
})()`,
true,
),
this.showBrowserInteractions ? InjectedScripts.installInteractionScript(page) : null,
page.addNewDocumentScript(injectedScript, true).then(() => page.reload()),
page.setJavaScriptEnabled(false),
);
}
await Promise.all(promises);
} finally {
if (readyResolvable) readyResolvable.resolve();
}
}

public async open(
context: IPuppetContext,
sessionId: string,
viewport?: IViewport,
onPage?: (page: IPuppetPage) => Promise<void>,
): Promise<void> {
if (this.isReady) return await this.isReady;

this.sessionId = sessionId;
const ready = new Resolvable<void>();
this.isReady = ready.promise;
try {
this.page = await context.newPage({ runPageScripts: false, enableDomStorageTracker: false });
await this.attachToPage(this.page, sessionId, false);

if (onPage) await onPage(this.page);
if (viewport) {
promises.push(
this.page.devtoolsSession.send('Emulation.setDeviceMetricsOverride', {
height: viewport.height,
width: viewport.width,
deviceScaleFactor: viewport.deviceScaleFactor,
mobile: false,
}),
);
await this.page.devtoolsSession.send('Emulation.setDeviceMetricsOverride', {
height: viewport.height,
width: viewport.width,
deviceScaleFactor: viewport.deviceScaleFactor,
mobile: false,
});
}
await Promise.all(promises);
} finally {
ready.resolve();
this.emit('open');
Expand Down

0 comments on commit 419e6d1

Please sign in to comment.