mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 13:45:36 +03:00
chore: various cleanup (#266)
This commit is contained in:
parent
f9f7d5c55a
commit
03e2336d49
@ -310,7 +310,7 @@
|
||||
- [class: TimeoutError](#class-timeouterror)
|
||||
- [class: Selector](#class-selector)
|
||||
* [selector.selector](#selectorselector)
|
||||
* [selector.visible](#selectorvisible)
|
||||
* [selector.visibility](#selectorvisibility)
|
||||
<!-- GEN:stop -->
|
||||
|
||||
### Overview
|
||||
|
@ -15,11 +15,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import * as dom from '../dom';
|
||||
import * as frames from '../frames';
|
||||
import { assert, debugError } from '../helper';
|
||||
import * as js from '../javascript';
|
||||
import { debugError, helper, RegisteredListener } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { CDPSession } from './Connection';
|
||||
import { EVALUATION_SCRIPT_URL, ExecutionContextDelegate } from './ExecutionContext';
|
||||
@ -45,17 +43,17 @@ import { ConsoleMessage } from '../console';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
|
||||
export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
export class FrameManager implements PageDelegate {
|
||||
_client: CDPSession;
|
||||
private _page: Page;
|
||||
private _networkManager: NetworkManager;
|
||||
private _contextIdToContext = new Map<number, js.ExecutionContext>();
|
||||
private _contextIdToContext = new Map<number, dom.FrameExecutionContext>();
|
||||
private _isolatedWorlds = new Set<string>();
|
||||
private _eventListeners: RegisteredListener[];
|
||||
rawMouse: RawMouseImpl;
|
||||
rawKeyboard: RawKeyboardImpl;
|
||||
|
||||
constructor(client: CDPSession, browserContext: BrowserContext, ignoreHTTPSErrors: boolean) {
|
||||
super();
|
||||
this._client = client;
|
||||
this.rawKeyboard = new RawKeyboardImpl(client);
|
||||
this.rawMouse = new RawMouseImpl(client);
|
||||
@ -68,22 +66,24 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
(this._page as any).overrides = new Overrides(client);
|
||||
(this._page as any).interception = new Interception(this._networkManager);
|
||||
|
||||
this._client.on('Inspector.targetCrashed', event => this._onTargetCrashed());
|
||||
this._client.on('Log.entryAdded', event => this._onLogEntryAdded(event));
|
||||
this._client.on('Page.fileChooserOpened', event => this._onFileChooserOpened(event));
|
||||
this._client.on('Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId));
|
||||
this._client.on('Page.frameDetached', event => this._onFrameDetached(event.frameId));
|
||||
this._client.on('Page.frameNavigated', event => this._onFrameNavigated(event.frame, false));
|
||||
this._client.on('Page.frameStoppedLoading', event => this._onFrameStoppedLoading(event.frameId));
|
||||
this._client.on('Page.javascriptDialogOpening', event => this._onDialog(event));
|
||||
this._client.on('Page.lifecycleEvent', event => this._onLifecycleEvent(event));
|
||||
this._client.on('Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url));
|
||||
this._client.on('Runtime.bindingCalled', event => this._onBindingCalled(event));
|
||||
this._client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
||||
this._client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails));
|
||||
this._client.on('Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context));
|
||||
this._client.on('Runtime.executionContextDestroyed', event => this._onExecutionContextDestroyed(event.executionContextId));
|
||||
this._client.on('Runtime.executionContextsCleared', event => this._onExecutionContextsCleared());
|
||||
this._eventListeners = [
|
||||
helper.addEventListener(client, 'Inspector.targetCrashed', event => this._onTargetCrashed()),
|
||||
helper.addEventListener(client, 'Log.entryAdded', event => this._onLogEntryAdded(event)),
|
||||
helper.addEventListener(client, 'Page.fileChooserOpened', event => this._onFileChooserOpened(event)),
|
||||
helper.addEventListener(client, 'Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId)),
|
||||
helper.addEventListener(client, 'Page.frameDetached', event => this._onFrameDetached(event.frameId)),
|
||||
helper.addEventListener(client, 'Page.frameNavigated', event => this._onFrameNavigated(event.frame, false)),
|
||||
helper.addEventListener(client, 'Page.frameStoppedLoading', event => this._onFrameStoppedLoading(event.frameId)),
|
||||
helper.addEventListener(client, 'Page.javascriptDialogOpening', event => this._onDialog(event)),
|
||||
helper.addEventListener(client, 'Page.lifecycleEvent', event => this._onLifecycleEvent(event)),
|
||||
helper.addEventListener(client, 'Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url)),
|
||||
helper.addEventListener(client, 'Runtime.bindingCalled', event => this._onBindingCalled(event)),
|
||||
helper.addEventListener(client, 'Runtime.consoleAPICalled', event => this._onConsoleAPI(event)),
|
||||
helper.addEventListener(client, 'Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails)),
|
||||
helper.addEventListener(client, 'Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)),
|
||||
helper.addEventListener(client, 'Runtime.executionContextDestroyed', event => this._onExecutionContextDestroyed(event.executionContextId)),
|
||||
helper.addEventListener(client, 'Runtime.executionContextsCleared', event => this._onExecutionContextsCleared()),
|
||||
];
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
@ -102,11 +102,9 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
}
|
||||
|
||||
didClose() {
|
||||
// TODO: remove listeners.
|
||||
}
|
||||
|
||||
networkManager(): NetworkManager {
|
||||
return this._networkManager;
|
||||
helper.removeEventListeners(this._eventListeners);
|
||||
this._networkManager.dispose();
|
||||
this._page._didClose();
|
||||
}
|
||||
|
||||
async navigateFrame(frame: frames.Frame, url: string, options: frames.GotoOptions = {}): Promise<network.Response | null> {
|
||||
@ -243,21 +241,18 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
}
|
||||
|
||||
_onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription) {
|
||||
const frameId = contextPayload.auxData ? contextPayload.auxData.frameId : null;
|
||||
const frame = this._page._frameManager.frame(frameId);
|
||||
const frame = this._page._frameManager.frame(contextPayload.auxData ? contextPayload.auxData.frameId : null);
|
||||
if (!frame)
|
||||
return;
|
||||
if (contextPayload.auxData && contextPayload.auxData.type === 'isolated')
|
||||
this._isolatedWorlds.add(contextPayload.name);
|
||||
const delegate = new ExecutionContextDelegate(this._client, contextPayload);
|
||||
if (frame) {
|
||||
const context = new dom.FrameExecutionContext(delegate, frame);
|
||||
if (contextPayload.auxData && !!contextPayload.auxData.isDefault)
|
||||
frame._contextCreated('main', context);
|
||||
else if (contextPayload.name === UTILITY_WORLD_NAME)
|
||||
frame._contextCreated('utility', context);
|
||||
this._contextIdToContext.set(contextPayload.id, context);
|
||||
} else {
|
||||
this._contextIdToContext.set(contextPayload.id, new js.ExecutionContext(delegate));
|
||||
}
|
||||
}
|
||||
|
||||
_onExecutionContextDestroyed(executionContextId: number) {
|
||||
@ -265,8 +260,7 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
if (!context)
|
||||
return;
|
||||
this._contextIdToContext.delete(executionContextId);
|
||||
if (context.frame())
|
||||
context.frame()._contextDestroyed(context as dom.FrameExecutionContext);
|
||||
context.frame()._contextDestroyed(context);
|
||||
}
|
||||
|
||||
_onExecutionContextsCleared() {
|
||||
@ -274,12 +268,6 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
this._onExecutionContextDestroyed(contextId);
|
||||
}
|
||||
|
||||
executionContextById(contextId: number): js.ExecutionContext {
|
||||
const context = this._contextIdToContext.get(contextId);
|
||||
assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
|
||||
return context;
|
||||
}
|
||||
|
||||
async _onConsoleAPI(event: Protocol.Runtime.consoleAPICalledPayload) {
|
||||
if (event.executionContextId === 0) {
|
||||
// DevTools protocol stores the last 1000 console messages. These
|
||||
@ -297,7 +285,7 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/3865
|
||||
return;
|
||||
}
|
||||
const context = this.executionContextById(event.executionContextId);
|
||||
const context = this._contextIdToContext.get(event.executionContextId);
|
||||
const values = event.args.map(arg => context._createHandle(arg));
|
||||
this._page._addConsoleMessage(event.type, values, toConsoleMessageLocation(event.stackTrace));
|
||||
}
|
||||
@ -309,7 +297,7 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
}
|
||||
|
||||
_onBindingCalled(event: Protocol.Runtime.bindingCalledPayload) {
|
||||
const context = this.executionContextById(event.executionContextId);
|
||||
const context = this._contextIdToContext.get(event.executionContextId);
|
||||
this._page._onBindingCalled(event.payload, context);
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
import { CDPSession } from './Connection';
|
||||
import { Page } from '../page';
|
||||
import { assert, debugError, helper } from '../helper';
|
||||
import { assert, debugError, helper, RegisteredListener } from '../helper';
|
||||
import { Protocol } from './protocol';
|
||||
import * as network from '../network';
|
||||
import * as frames from '../frames';
|
||||
@ -36,18 +36,21 @@ export class NetworkManager {
|
||||
private _protocolRequestInterceptionEnabled = false;
|
||||
private _userCacheDisabled = false;
|
||||
private _requestIdToInterceptionId = new Map<string, string>();
|
||||
private _eventListeners: RegisteredListener[];
|
||||
|
||||
constructor(client: CDPSession, ignoreHTTPSErrors: boolean, page: Page) {
|
||||
this._client = client;
|
||||
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
this._page = page;
|
||||
|
||||
this._client.on('Fetch.requestPaused', this._onRequestPaused.bind(this));
|
||||
this._client.on('Fetch.authRequired', this._onAuthRequired.bind(this));
|
||||
this._client.on('Network.requestWillBeSent', this._onRequestWillBeSent.bind(this));
|
||||
this._client.on('Network.responseReceived', this._onResponseReceived.bind(this));
|
||||
this._client.on('Network.loadingFinished', this._onLoadingFinished.bind(this));
|
||||
this._client.on('Network.loadingFailed', this._onLoadingFailed.bind(this));
|
||||
this._eventListeners = [
|
||||
helper.addEventListener(client, 'Fetch.requestPaused', this._onRequestPaused.bind(this)),
|
||||
helper.addEventListener(client, 'Fetch.authRequired', this._onAuthRequired.bind(this)),
|
||||
helper.addEventListener(client, 'Network.requestWillBeSent', this._onRequestWillBeSent.bind(this)),
|
||||
helper.addEventListener(client, 'Network.responseReceived', this._onResponseReceived.bind(this)),
|
||||
helper.addEventListener(client, 'Network.loadingFinished', this._onLoadingFinished.bind(this)),
|
||||
helper.addEventListener(client, 'Network.loadingFailed', this._onLoadingFailed.bind(this)),
|
||||
];
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
@ -56,6 +59,10 @@ export class NetworkManager {
|
||||
await this._client.send('Security.setIgnoreCertificateErrors', {ignore: true});
|
||||
}
|
||||
|
||||
dispose() {
|
||||
helper.removeEventListeners(this._eventListeners);
|
||||
}
|
||||
|
||||
async authenticate(credentials: { username: string; password: string; } | null) {
|
||||
this._credentials = credentials;
|
||||
await this._updateProtocolRequestInterception();
|
||||
|
@ -36,7 +36,7 @@ export class Target {
|
||||
private _ignoreHTTPSErrors: boolean;
|
||||
private _defaultViewport: types.Viewport;
|
||||
private _pagePromise: Promise<Page> | null = null;
|
||||
private _page: Page | null = null;
|
||||
private _frameManager: FrameManager | null = null;
|
||||
private _workerPromise: Promise<Worker> | null = null;
|
||||
_initializedPromise: Promise<boolean>;
|
||||
_initializedCallback: (value?: unknown) => void;
|
||||
@ -77,16 +77,15 @@ export class Target {
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
if (this._page)
|
||||
this._page._didClose();
|
||||
if (this._frameManager)
|
||||
this._frameManager.didClose();
|
||||
}
|
||||
|
||||
async page(): Promise<Page | null> {
|
||||
if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) {
|
||||
this._pagePromise = this._sessionFactory().then(async client => {
|
||||
const frameManager = new FrameManager(client, this._browserContext, this._ignoreHTTPSErrors);
|
||||
const page = frameManager.page();
|
||||
this._page = page;
|
||||
this._frameManager = new FrameManager(client, this._browserContext, this._ignoreHTTPSErrors);
|
||||
const page = this._frameManager.page();
|
||||
(page as any)[targetSymbol] = this;
|
||||
client.once(CDPSessionEvents.Disconnected, () => page._didDisconnect());
|
||||
client.on('Target.attachedToTarget', event => {
|
||||
@ -95,7 +94,7 @@ export class Target {
|
||||
client.send('Target.detachFromTarget', { sessionId: event.sessionId }).catch(debugError);
|
||||
}
|
||||
});
|
||||
await frameManager.initialize();
|
||||
await this._frameManager.initialize();
|
||||
await client.send('Target.setAutoAttach', {autoAttach: true, waitForDebuggerOnStart: false, flatten: true});
|
||||
if (this._defaultViewport)
|
||||
await page.setViewport(this._defaultViewport);
|
||||
|
@ -219,7 +219,7 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||
|
||||
export class Target {
|
||||
_pagePromise?: Promise<Page>;
|
||||
private _page: Page | null = null;
|
||||
private _frameManager: FrameManager | null = null;
|
||||
private _browser: Browser;
|
||||
_context: BrowserContext;
|
||||
private _connection: Connection;
|
||||
@ -239,8 +239,8 @@ export class Target {
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
if (this._page)
|
||||
this._page._didClose();
|
||||
if (this._frameManager)
|
||||
this._frameManager.didClose();
|
||||
}
|
||||
|
||||
opener(): Target | null {
|
||||
@ -263,11 +263,10 @@ export class Target {
|
||||
if (this._type === 'page' && !this._pagePromise) {
|
||||
this._pagePromise = new Promise(async f => {
|
||||
const session = await this._connection.createSession(this._targetId);
|
||||
const frameManager = new FrameManager(session, this._context);
|
||||
const page = frameManager._page;
|
||||
this._page = page;
|
||||
this._frameManager = new FrameManager(session, this._context);
|
||||
const page = this._frameManager._page;
|
||||
session.once(JugglerSessionEvents.Disconnected, () => page._didDisconnect());
|
||||
await frameManager._initialize();
|
||||
await this._frameManager._initialize();
|
||||
if (this._browser._defaultViewport)
|
||||
await page.setViewport(this._browser._defaultViewport);
|
||||
f(page);
|
||||
|
@ -15,10 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import * as frames from '../frames';
|
||||
import { assert, helper, RegisteredListener, debugError } from '../helper';
|
||||
import * as js from '../javascript';
|
||||
import * as dom from '../dom';
|
||||
import { JugglerSession } from './Connection';
|
||||
import { ExecutionContextDelegate } from './ExecutionContext';
|
||||
@ -35,17 +33,16 @@ import { Accessibility } from './features/accessibility';
|
||||
import * as network from '../network';
|
||||
import * as types from '../types';
|
||||
|
||||
export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
export class FrameManager implements PageDelegate {
|
||||
readonly rawMouse: RawMouseImpl;
|
||||
readonly rawKeyboard: RawKeyboardImpl;
|
||||
readonly _session: JugglerSession;
|
||||
readonly _page: Page;
|
||||
private readonly _networkManager: NetworkManager;
|
||||
private readonly _contextIdToContext: Map<string, js.ExecutionContext>;
|
||||
private readonly _contextIdToContext: Map<string, dom.FrameExecutionContext>;
|
||||
private _eventListeners: RegisteredListener[];
|
||||
|
||||
constructor(session: JugglerSession, browserContext: BrowserContext) {
|
||||
super();
|
||||
this._session = session;
|
||||
this.rawKeyboard = new RawKeyboardImpl(session);
|
||||
this.rawMouse = new RawMouseImpl(session);
|
||||
@ -81,22 +78,15 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
]);
|
||||
}
|
||||
|
||||
executionContextById(executionContextId) {
|
||||
return this._contextIdToContext.get(executionContextId) || null;
|
||||
}
|
||||
|
||||
_onExecutionContextCreated({executionContextId, auxData}) {
|
||||
const frameId = auxData ? auxData.frameId : null;
|
||||
const frame = this._page._frameManager.frame(frameId);
|
||||
const frame = this._page._frameManager.frame(auxData ? auxData.frameId : null);
|
||||
if (!frame)
|
||||
return;
|
||||
const delegate = new ExecutionContextDelegate(this._session, executionContextId);
|
||||
if (frame) {
|
||||
const context = new dom.FrameExecutionContext(delegate, frame);
|
||||
frame._contextCreated('main', context);
|
||||
frame._contextCreated('utility', context);
|
||||
this._contextIdToContext.set(executionContextId, context);
|
||||
} else {
|
||||
this._contextIdToContext.set(executionContextId, new js.ExecutionContext(delegate));
|
||||
}
|
||||
}
|
||||
|
||||
_onExecutionContextDestroyed({executionContextId}) {
|
||||
@ -104,14 +94,13 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
if (!context)
|
||||
return;
|
||||
this._contextIdToContext.delete(executionContextId);
|
||||
if (context.frame())
|
||||
context.frame()._contextDestroyed(context as dom.FrameExecutionContext);
|
||||
}
|
||||
|
||||
_onNavigationStarted(params) {
|
||||
_onNavigationStarted() {
|
||||
}
|
||||
|
||||
_onNavigationAborted(params) {
|
||||
_onNavigationAborted(params: Protocol.Page.navigationAbortedPayload) {
|
||||
const frame = this._page._frameManager.frame(params.frameId);
|
||||
for (const watcher of this._page._frameManager._lifecycleWatchers)
|
||||
watcher._onAbortedNewDocumentNavigation(frame, params.navigationId, params.errorText);
|
||||
@ -140,18 +129,18 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
this._page._frameManager.frameLifecycleEvent(frameId, 'domcontentloaded');
|
||||
}
|
||||
|
||||
_onUncaughtError(params) {
|
||||
_onUncaughtError(params: Protocol.Page.uncaughtErrorPayload) {
|
||||
const error = new Error(params.message);
|
||||
error.stack = params.stack;
|
||||
this._page.emit(Events.Page.PageError, error);
|
||||
}
|
||||
|
||||
_onConsole({type, args, executionContextId, location}) {
|
||||
const context = this.executionContextById(executionContextId);
|
||||
const context = this._contextIdToContext.get(executionContextId);
|
||||
this._page._addConsoleMessage(type, args.map(arg => context._createHandle(arg)), location);
|
||||
}
|
||||
|
||||
_onDialogOpened(params) {
|
||||
_onDialogOpened(params: Protocol.Page.dialogOpenedPayload) {
|
||||
this._page.emit(Events.Page.Dialog, new dialog.Dialog(
|
||||
params.type as dialog.DialogType,
|
||||
params.message,
|
||||
@ -162,12 +151,12 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
}
|
||||
|
||||
_onBindingCalled(event: Protocol.Page.bindingCalledPayload) {
|
||||
const context = this.executionContextById(event.executionContextId);
|
||||
const context = this._contextIdToContext.get(event.executionContextId);
|
||||
this._page._onBindingCalled(event.payload, context);
|
||||
}
|
||||
|
||||
async _onFileChooserOpened({executionContextId, element}) {
|
||||
const context = this.executionContextById(executionContextId);
|
||||
const context = this._contextIdToContext.get(executionContextId);
|
||||
const handle = context._createHandle(element).asElement()!;
|
||||
this._page._onFileChooserOpened(handle);
|
||||
}
|
||||
@ -181,6 +170,7 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
didClose() {
|
||||
helper.removeEventListeners(this._eventListeners);
|
||||
this._networkManager.dispose();
|
||||
this._page._didClose();
|
||||
}
|
||||
|
||||
async waitForFrameNavigation(frame: frames.Frame, options: frames.NavigateOptions = {}) {
|
||||
|
@ -30,8 +30,6 @@ import { launchProcess, waitForLine } from '../processLauncher';
|
||||
const mkdtempAsync = util.promisify(fs.mkdtemp);
|
||||
const writeFileAsync = util.promisify(fs.writeFile);
|
||||
|
||||
const FIREFOX_PROFILE_PATH = path.join(os.tmpdir(), 'playwright_firefox_profile-');
|
||||
|
||||
const DEFAULT_ARGS = [
|
||||
'-no-remote',
|
||||
'-foreground',
|
||||
|
@ -40,8 +40,6 @@ export interface PageDelegate {
|
||||
exposeBinding(name: string, bindingFunction: string): Promise<void>;
|
||||
evaluateOnNewDocument(source: string): Promise<void>;
|
||||
closePage(runBeforeUnload: boolean): Promise<void>;
|
||||
// TODO: reverse didClose call sequence.
|
||||
didClose(): void;
|
||||
|
||||
navigateFrame(frame: frames.Frame, url: string, options?: frames.GotoOptions): Promise<network.Response | null>;
|
||||
waitForFrameNavigation(frame: frames.Frame, options?: frames.NavigateOptions): Promise<network.Response | null>;
|
||||
@ -130,7 +128,6 @@ export class Page extends EventEmitter {
|
||||
_didClose() {
|
||||
assert(!this._closed, 'Page closed twice');
|
||||
this._closed = true;
|
||||
this._delegate.didClose();
|
||||
this.emit(Events.Page.Close);
|
||||
this._closedCallback();
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||
const opener = this._targets.get(targetInfo.openerId);
|
||||
if (!opener)
|
||||
return;
|
||||
const openerPage = opener._page;
|
||||
const openerPage = opener._frameManager ? opener._frameManager._page : null;
|
||||
if (!openerPage || !openerPage.listenerCount(Events.Page.Popup))
|
||||
return;
|
||||
target.page().then(page => openerPage.emit(Events.Page.Popup, page));
|
||||
|
@ -15,10 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as EventEmitter from 'events';
|
||||
import * as frames from '../frames';
|
||||
import { assert, debugError, helper, RegisteredListener } from '../helper';
|
||||
import * as js from '../javascript';
|
||||
import { debugError, helper, RegisteredListener } from '../helper';
|
||||
import * as dom from '../dom';
|
||||
import * as network from '../network';
|
||||
import { TargetSession } from './Connection';
|
||||
@ -39,19 +37,18 @@ import { PNG } from 'pngjs';
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
const BINDING_CALL_MESSAGE = '__playwright_binding_call__';
|
||||
|
||||
export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
export class FrameManager implements PageDelegate {
|
||||
readonly rawMouse: RawMouseImpl;
|
||||
readonly rawKeyboard: RawKeyboardImpl;
|
||||
_session: TargetSession;
|
||||
readonly _page: Page;
|
||||
private readonly _networkManager: NetworkManager;
|
||||
private readonly _contextIdToContext: Map<number, js.ExecutionContext>;
|
||||
private readonly _contextIdToContext: Map<number, dom.FrameExecutionContext>;
|
||||
private _isolatedWorlds: Set<string>;
|
||||
private _sessionListeners: RegisteredListener[] = [];
|
||||
private readonly _bootstrapScripts: string[] = [];
|
||||
|
||||
constructor(browserContext: BrowserContext) {
|
||||
super();
|
||||
this.rawKeyboard = new RawKeyboardImpl();
|
||||
this.rawMouse = new RawMouseImpl();
|
||||
this._contextIdToContext = new Map();
|
||||
@ -102,7 +99,9 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
|
||||
didClose() {
|
||||
helper.removeEventListeners(this._sessionListeners);
|
||||
this._networkManager.dispose();
|
||||
this.disconnectFromTarget();
|
||||
this._page._didClose();
|
||||
}
|
||||
|
||||
_addSessionListeners() {
|
||||
@ -124,14 +123,9 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
disconnectFromTarget() {
|
||||
for (const context of this._contextIdToContext.values()) {
|
||||
(context._delegate as ExecutionContextDelegate)._dispose();
|
||||
if (context.frame())
|
||||
context.frame()._contextDestroyed(context as dom.FrameExecutionContext);
|
||||
context.frame()._contextDestroyed(context);
|
||||
}
|
||||
// this._mainFrame = null;
|
||||
}
|
||||
|
||||
networkManager(): NetworkManager {
|
||||
return this._networkManager;
|
||||
this._contextIdToContext.clear();
|
||||
}
|
||||
|
||||
_onFrameStoppedLoading(frameId: string) {
|
||||
@ -158,12 +152,11 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
|
||||
_onFrameNavigated(framePayload: Protocol.Page.Frame, initial: boolean) {
|
||||
const frame = this._page._frameManager.frame(framePayload.id);
|
||||
for (const context of this._contextIdToContext.values()) {
|
||||
for (const [contextId, context] of this._contextIdToContext) {
|
||||
if (context.frame() === frame) {
|
||||
const delegate = context._delegate as ExecutionContextDelegate;
|
||||
delegate._dispose();
|
||||
this._contextIdToContext.delete(delegate._contextId);
|
||||
frame._contextDestroyed(context as dom.FrameExecutionContext);
|
||||
(context._delegate as ExecutionContextDelegate)._dispose();
|
||||
this._contextIdToContext.delete(contextId);
|
||||
frame._contextDestroyed(context);
|
||||
}
|
||||
}
|
||||
// Append session id to avoid cross-process loaderId clash.
|
||||
@ -182,29 +175,16 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
_onExecutionContextCreated(contextPayload : Protocol.Runtime.ExecutionContextDescription) {
|
||||
if (this._contextIdToContext.has(contextPayload.id))
|
||||
return;
|
||||
const frameId = contextPayload.frameId;
|
||||
// If the frame was attached manually there is no navigation event.
|
||||
// FIXME: support frameAttached event in WebKit protocol.
|
||||
const frame = this._page._frameManager.frame(frameId);
|
||||
const frame = this._page._frameManager.frame(contextPayload.frameId);
|
||||
if (!frame)
|
||||
return;
|
||||
const delegate = new ExecutionContextDelegate(this._session, contextPayload);
|
||||
if (frame) {
|
||||
const context = new dom.FrameExecutionContext(delegate, frame);
|
||||
if (contextPayload.isPageContext)
|
||||
frame._contextCreated('main', context);
|
||||
else if (contextPayload.name === UTILITY_WORLD_NAME)
|
||||
frame._contextCreated('utility', context);
|
||||
this._contextIdToContext.set(contextPayload.id, context);
|
||||
} else {
|
||||
this._contextIdToContext.set(contextPayload.id, new js.ExecutionContext(delegate));
|
||||
}
|
||||
}
|
||||
|
||||
executionContextById(contextId: number): js.ExecutionContext {
|
||||
const context = this._contextIdToContext.get(contextId);
|
||||
assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
|
||||
return context;
|
||||
}
|
||||
|
||||
async navigateFrame(frame: frames.Frame, url: string, options: frames.GotoOptions = {}): Promise<network.Response | null> {
|
||||
@ -279,7 +259,7 @@ export class FrameManager extends EventEmitter implements PageDelegate {
|
||||
|
||||
const mainFrameContext = await this._page.mainFrame().executionContext();
|
||||
const handles = (parameters || []).map(p => {
|
||||
let context: js.ExecutionContext | null = null;
|
||||
let context: dom.FrameExecutionContext | null = null;
|
||||
if (p.objectId) {
|
||||
const objectId = JSON.parse(p.objectId);
|
||||
context = this._contextIdToContext.get(objectId.injectedScriptId);
|
||||
|
@ -53,6 +53,10 @@ export class NetworkManager {
|
||||
]);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
helper.removeEventListeners(this._sessionListeners);
|
||||
}
|
||||
|
||||
async setExtraHTTPHeaders(extraHTTPHeaders: { [s: string]: string; }) {
|
||||
this._extraHTTPHeaders = {};
|
||||
for (const key of Object.keys(extraHTTPHeaders)) {
|
||||
|
@ -30,7 +30,7 @@ export class Target {
|
||||
readonly _type: 'page' | 'service-worker' | 'worker';
|
||||
private readonly _session: TargetSession;
|
||||
private _pagePromise: Promise<Page> | null = null;
|
||||
_page: Page | null = null;
|
||||
_frameManager: FrameManager | null = null;
|
||||
|
||||
static fromPage(page: Page): Target {
|
||||
return (page as any)[targetSymbol];
|
||||
@ -47,17 +47,17 @@ export class Target {
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
if (this._page)
|
||||
this._page._didClose();
|
||||
if (this._frameManager)
|
||||
this._frameManager.didClose();
|
||||
}
|
||||
|
||||
async _initializeSession(session: TargetSession) {
|
||||
if (!this._page)
|
||||
if (!this._frameManager)
|
||||
return;
|
||||
await (this._page._delegate as FrameManager)._initializeSession(session).catch(e => {
|
||||
await this._frameManager._initializeSession(session).catch(e => {
|
||||
// Swallow initialization errors due to newer target swap in,
|
||||
// since we will reinitialize again.
|
||||
if (this._page)
|
||||
if (this._frameManager)
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
@ -66,32 +66,31 @@ export class Target {
|
||||
if (!oldTarget._pagePromise)
|
||||
return;
|
||||
this._pagePromise = oldTarget._pagePromise;
|
||||
this._page = oldTarget._page;
|
||||
this._frameManager = oldTarget._frameManager;
|
||||
// Swapped out target should not be accessed by anyone. Reset page promise so that
|
||||
// old target does not close the page on connection reset.
|
||||
oldTarget._pagePromise = null;
|
||||
oldTarget._page = null;
|
||||
oldTarget._frameManager = null;
|
||||
this._adoptPage();
|
||||
}
|
||||
|
||||
private _adoptPage() {
|
||||
(this._page as any)[targetSymbol] = this;
|
||||
(this._frameManager._page as any)[targetSymbol] = this;
|
||||
this._session.once(TargetSessionEvents.Disconnected, () => {
|
||||
// Once swapped out, we reset _page and won't call _didDisconnect for old session.
|
||||
if (this._page)
|
||||
this._page._didDisconnect();
|
||||
if (this._frameManager)
|
||||
this._frameManager._page._didDisconnect();
|
||||
});
|
||||
(this._page._delegate as FrameManager).setSession(this._session);
|
||||
this._frameManager.setSession(this._session);
|
||||
}
|
||||
|
||||
async page(): Promise<Page> {
|
||||
if (this._type === 'page' && !this._pagePromise) {
|
||||
const browser = this._browserContext.browser() as Browser;
|
||||
// Reference local page variable as _page may be
|
||||
this._frameManager = new FrameManager(this._browserContext);
|
||||
// Reference local page variable as |this._frameManager| may be
|
||||
// cleared on swap.
|
||||
const frameManager = new FrameManager(this._browserContext);
|
||||
const page = frameManager._page;
|
||||
this._page = page;
|
||||
const page = this._frameManager._page;
|
||||
this._pagePromise = new Promise(async f => {
|
||||
this._adoptPage();
|
||||
await this._initializeSession(this._session);
|
||||
|
Loading…
Reference in New Issue
Block a user