"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.WorkerDispatcher = exports.PageDispatcher = exports.BindingCallDispatcher = void 0;
var _page = require("../page");
var _dispatcher = require("./dispatcher");
var _errors = require("../errors");
var _frameDispatcher = require("./frameDispatcher");
var _networkDispatchers = require("./networkDispatchers");
var _jsHandleDispatcher = require("./jsHandleDispatcher");
var _elementHandlerDispatcher = require("./elementHandlerDispatcher");
var _artifactDispatcher = require("./artifactDispatcher");
var _utils = require("../../utils");
var _webSocketRouteDispatcher = require("./webSocketRouteDispatcher");
/**
 * 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 PageDispatcher extends _dispatcher.Dispatcher {
  static from(parentScope, page) {
    return PageDispatcher.fromNullable(parentScope, page);
  }
  static fromNullable(parentScope, page) {
    if (!page) return undefined;
    const result = (0, _dispatcher.existingDispatcher)(page);
    return result || new PageDispatcher(parentScope, page);
  }
  constructor(parentScope, page) {
    // TODO: theoretically, there could be more than one frame already.
    // If we split pageCreated and pageReady, there should be no main frame during pageCreated.

    // We will reparent it to the page below using adopt.
    const mainFrame = _frameDispatcher.FrameDispatcher.from(parentScope, page.mainFrame());
    super(parentScope, page, 'Page', {
      mainFrame,
      viewportSize: page.viewportSize() || undefined,
      isClosed: page.isClosed(),
      opener: PageDispatcher.fromNullable(parentScope, page.opener())
    });
    this._type_EventTarget = true;
    this._type_Page = true;
    this._page = void 0;
    this._subscriptions = new Set();
    this._webSocketInterceptionPatterns = [];
    this.adopt(mainFrame);
    this._page = page;
    this.addObjectListener(_page.Page.Events.Close, () => {
      this._dispatchEvent('close');
      this._dispose();
    });
    this.addObjectListener(_page.Page.Events.Crash, () => this._dispatchEvent('crash'));
    this.addObjectListener(_page.Page.Events.Download, download => {
      // Artifact can outlive the page, so bind to the context scope.
      this._dispatchEvent('download', {
        url: download.url,
        suggestedFilename: download.suggestedFilename(),
        artifact: _artifactDispatcher.ArtifactDispatcher.from(parentScope, download.artifact)
      });
    });
    this.addObjectListener(_page.Page.Events.FileChooser, fileChooser => this._dispatchEvent('fileChooser', {
      element: _elementHandlerDispatcher.ElementHandleDispatcher.from(mainFrame, fileChooser.element()),
      isMultiple: fileChooser.isMultiple()
    }));
    this.addObjectListener(_page.Page.Events.FrameAttached, frame => this._onFrameAttached(frame));
    this.addObjectListener(_page.Page.Events.FrameDetached, frame => this._onFrameDetached(frame));
    this.addObjectListener(_page.Page.Events.LocatorHandlerTriggered, uid => this._dispatchEvent('locatorHandlerTriggered', {
      uid
    }));
    this.addObjectListener(_page.Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', {
      webSocket: new _networkDispatchers.WebSocketDispatcher(this, webSocket)
    }));
    this.addObjectListener(_page.Page.Events.Worker, worker => this._dispatchEvent('worker', {
      worker: new WorkerDispatcher(this, worker)
    }));
    this.addObjectListener(_page.Page.Events.Video, artifact => this._dispatchEvent('video', {
      artifact: _artifactDispatcher.ArtifactDispatcher.from(parentScope, artifact)
    }));
    if (page._video) this._dispatchEvent('video', {
      artifact: _artifactDispatcher.ArtifactDispatcher.from(this.parentScope(), page._video)
    });
    // Ensure client knows about all frames.
    const frames = page._frameManager.frames();
    for (let i = 1; i < frames.length; i++) this._onFrameAttached(frames[i]);
  }
  page() {
    return this._page;
  }
  async setDefaultNavigationTimeoutNoReply(params, metadata) {
    this._page.setDefaultNavigationTimeout(params.timeout);
  }
  async setDefaultTimeoutNoReply(params, metadata) {
    this._page.setDefaultTimeout(params.timeout);
  }
  async exposeBinding(params, metadata) {
    await this._page.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => {
      // When reusing the context, we might have some bindings called late enough,
      // after context and page dispatchers have been disposed.
      if (this._disposed) return;
      const binding = new BindingCallDispatcher(this, params.name, !!params.needsHandle, source, args);
      this._dispatchEvent('bindingCall', {
        binding
      });
      return binding.promise();
    });
  }
  async setExtraHTTPHeaders(params, metadata) {
    await this._page.setExtraHTTPHeaders(params.headers);
  }
  async reload(params, metadata) {
    return {
      response: _networkDispatchers.ResponseDispatcher.fromNullable(this.parentScope(), await this._page.reload(metadata, params))
    };
  }
  async goBack(params, metadata) {
    return {
      response: _networkDispatchers.ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goBack(metadata, params))
    };
  }
  async goForward(params, metadata) {
    return {
      response: _networkDispatchers.ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goForward(metadata, params))
    };
  }
  async requestGC(params, metadata) {
    await this._page.requestGC();
  }
  async registerLocatorHandler(params, metadata) {
    const uid = this._page.registerLocatorHandler(params.selector, params.noWaitAfter);
    return {
      uid
    };
  }
  async resolveLocatorHandlerNoReply(params, metadata) {
    this._page.resolveLocatorHandler(params.uid, params.remove);
  }
  async unregisterLocatorHandler(params, metadata) {
    this._page.unregisterLocatorHandler(params.uid);
  }
  async emulateMedia(params, metadata) {
    await this._page.emulateMedia({
      media: params.media,
      colorScheme: params.colorScheme,
      reducedMotion: params.reducedMotion,
      forcedColors: params.forcedColors
    });
  }
  async setViewportSize(params, metadata) {
    await this._page.setViewportSize(params.viewportSize);
  }
  async addInitScript(params, metadata) {
    await this._page.addInitScript(params.source);
  }
  async setNetworkInterceptionPatterns(params, metadata) {
    if (!params.patterns.length) {
      await this._page.setClientRequestInterceptor(undefined);
      return;
    }
    const urlMatchers = params.patterns.map(pattern => pattern.regexSource ? new RegExp(pattern.regexSource, pattern.regexFlags) : pattern.glob);
    await this._page.setClientRequestInterceptor((route, request) => {
      const matchesSome = urlMatchers.some(urlMatch => (0, _utils.urlMatches)(this._page._browserContext._options.baseURL, request.url(), urlMatch));
      if (!matchesSome) return false;
      this._dispatchEvent('route', {
        route: _networkDispatchers.RouteDispatcher.from(_networkDispatchers.RequestDispatcher.from(this.parentScope(), request), route)
      });
      return true;
    });
  }
  async setWebSocketInterceptionPatterns(params, metadata) {
    this._webSocketInterceptionPatterns = params.patterns;
    if (params.patterns.length) await _webSocketRouteDispatcher.WebSocketRouteDispatcher.installIfNeeded(this._page);
  }
  async expectScreenshot(params, metadata) {
    const mask = (params.mask || []).map(({
      frame,
      selector
    }) => ({
      frame: frame._object,
      selector
    }));
    const locator = params.locator ? {
      frame: params.locator.frame._object,
      selector: params.locator.selector
    } : undefined;
    return await this._page.expectScreenshot(metadata, {
      ...params,
      locator,
      mask
    });
  }
  async screenshot(params, metadata) {
    const mask = (params.mask || []).map(({
      frame,
      selector
    }) => ({
      frame: frame._object,
      selector
    }));
    return {
      binary: await this._page.screenshot(metadata, {
        ...params,
        mask
      })
    };
  }
  async close(params, metadata) {
    if (!params.runBeforeUnload) metadata.potentiallyClosesScope = true;
    await this._page.close(metadata, params);
  }
  async updateSubscription(params) {
    if (params.event === 'fileChooser') await this._page.setFileChooserIntercepted(params.enabled);
    if (params.enabled) this._subscriptions.add(params.event);else this._subscriptions.delete(params.event);
  }
  async keyboardDown(params, metadata) {
    await this._page.keyboard.down(params.key);
  }
  async keyboardUp(params, metadata) {
    await this._page.keyboard.up(params.key);
  }
  async keyboardInsertText(params, metadata) {
    await this._page.keyboard.insertText(params.text);
  }
  async keyboardType(params, metadata) {
    await this._page.keyboard.type(params.text, params);
  }
  async keyboardPress(params, metadata) {
    await this._page.keyboard.press(params.key, params);
  }
  async mouseMove(params, metadata) {
    await this._page.mouse.move(params.x, params.y, params, metadata);
  }
  async mouseDown(params, metadata) {
    await this._page.mouse.down(params, metadata);
  }
  async mouseUp(params, metadata) {
    await this._page.mouse.up(params, metadata);
  }
  async mouseClick(params, metadata) {
    await this._page.mouse.click(params.x, params.y, params, metadata);
  }
  async mouseWheel(params, metadata) {
    await this._page.mouse.wheel(params.deltaX, params.deltaY);
  }
  async touchscreenTap(params, metadata) {
    await this._page.touchscreen.tap(params.x, params.y, metadata);
  }
  async accessibilitySnapshot(params, metadata) {
    const rootAXNode = await this._page.accessibility.snapshot({
      interestingOnly: params.interestingOnly,
      root: params.root ? params.root._elementHandle : undefined
    });
    return {
      rootAXNode: rootAXNode || undefined
    };
  }
  async pdf(params, metadata) {
    if (!this._page.pdf) throw new Error('PDF generation is only supported for Headless Chromium');
    const buffer = await this._page.pdf(params);
    return {
      pdf: buffer
    };
  }
  async bringToFront(params, metadata) {
    await this._page.bringToFront();
  }
  async startJSCoverage(params, metadata) {
    const coverage = this._page.coverage;
    await coverage.startJSCoverage(params);
  }
  async stopJSCoverage(params, metadata) {
    const coverage = this._page.coverage;
    return await coverage.stopJSCoverage();
  }
  async startCSSCoverage(params, metadata) {
    const coverage = this._page.coverage;
    await coverage.startCSSCoverage(params);
  }
  async stopCSSCoverage(params, metadata) {
    const coverage = this._page.coverage;
    return await coverage.stopCSSCoverage();
  }
  _onFrameAttached(frame) {
    this._dispatchEvent('frameAttached', {
      frame: _frameDispatcher.FrameDispatcher.from(this.parentScope(), frame)
    });
  }
  _onFrameDetached(frame) {
    this._dispatchEvent('frameDetached', {
      frame: _frameDispatcher.FrameDispatcher.from(this.parentScope(), frame)
    });
  }
  _onDispose() {
    // Avoid protocol calls for the closed page.
    if (!this._page.isClosedOrClosingOrCrashed()) this._page.setClientRequestInterceptor(undefined).catch(() => {});
  }
}
exports.PageDispatcher = PageDispatcher;
class WorkerDispatcher extends _dispatcher.Dispatcher {
  static fromNullable(scope, worker) {
    if (!worker) return undefined;
    const result = (0, _dispatcher.existingDispatcher)(worker);
    return result || new WorkerDispatcher(scope, worker);
  }
  constructor(scope, worker) {
    super(scope, worker, 'Worker', {
      url: worker.url()
    });
    this._type_Worker = true;
    this.addObjectListener(_page.Worker.Events.Close, () => this._dispatchEvent('close'));
  }
  async evaluateExpression(params, metadata) {
    return {
      value: (0, _jsHandleDispatcher.serializeResult)(await this._object.evaluateExpression(params.expression, params.isFunction, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
    };
  }
  async evaluateExpressionHandle(params, metadata) {
    return {
      handle: _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
    };
  }
}
exports.WorkerDispatcher = WorkerDispatcher;
class BindingCallDispatcher extends _dispatcher.Dispatcher {
  constructor(scope, name, needsHandle, source, args) {
    super(scope, {
      guid: 'bindingCall@' + (0, _utils.createGuid)()
    }, 'BindingCall', {
      frame: _frameDispatcher.FrameDispatcher.from(scope.parentScope(), source.frame),
      name,
      args: needsHandle ? undefined : args.map(_jsHandleDispatcher.serializeResult),
      handle: needsHandle ? _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(scope, args[0]) : undefined
    });
    this._type_BindingCall = true;
    this._resolve = void 0;
    this._reject = void 0;
    this._promise = void 0;
    this._promise = new Promise((resolve, reject) => {
      this._resolve = resolve;
      this._reject = reject;
    });
  }
  promise() {
    return this._promise;
  }
  async resolve(params, metadata) {
    this._resolve((0, _jsHandleDispatcher.parseArgument)(params.result));
    this._dispose();
  }
  async reject(params, metadata) {
    this._reject((0, _errors.parseError)(params.error));
    this._dispose();
  }
}
exports.BindingCallDispatcher = BindingCallDispatcher;