Как Puppeteer обрабатывает щелчок Object / DevTools Protocol Chromium / Chrome - PullRequest
0 голосов
/ 01 апреля 2020

Мне нужно знать, как кукловод обрабатывает объект щелчка, а также API Chromium DevTools. Я попытался исследовать его самостоятельно и обнаружил, что не могу найти фактический код, который его обрабатывает.

Причина, по которой мне нужно знать, заключается в том, что я разрабатываю оболочку, которая проверяет события в код для тестирования веб-страниц, и пытался выяснить, выгодна ли реализация подпрограммы обработки событий вместо использования интерфейса событий кукловода (щелчки и касания при наведении курсора, а также других событий, которые могут потребоваться, например сенсорные события или прокрутка)

Вот как далеко я продвинулся:

API Puppeteer использует Frame Logi c DevTools для связи с API: https://github.com/puppeteer/puppeteer/blob/master/lib/Page.js

    /**
       * @param {string} selector
       * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
   */
      click(selector, options = {}) {
      return this.mainFrame().click(selector, options);
      }
       /**
       * @return {!Puppeteer.Frame}
       */
      /**
       * @param {!Protocol.Page.Frame} framePayload`
       */
      _onFrameNavigated(framePayload) {
       const isMainFrame = !framePayload.parentId;
       let frame = isMainFrame ? this._mainFrame : this._frames.get(framePayload.id);
       assert(isMainFrame || frame, 'We either navigate top level or have old version of the navigated frame');
    // Detach all child frames first.
        if (frame) {
          for (const child of frame.childFrames())
           this._removeFramesRecursively(child);
        }
        if (isMainFrame) {
          if (frame) {
            // Update frame id to retain frame identity on cross-process navigation.
            this._frames.delete(frame._id);
            frame._id = framePayload.id;
          } else {
            // Initial main frame navigation.
            frame = new Frame(this, this._client, null, framePayload.id);
          }
          this._frames.set(framePayload.id, frame);
          this._mainFrame = frame;
        }

Это все, что я получил, потому что я пытался найти протокол страницы, но не могу понять, что там происходит.

Любая помощь будет признательна, даже в исследованиях.

1 Ответ

0 голосов
/ 01 апреля 2020

Основные части происходят в JSHandle здесь :

async click(options) {
    await this._scrollIntoViewIfNeeded();
    const {x, y} = await this._clickablePoint();
    await this._page.mouse.click(x, y, options);
}

Он прокручивается, пока элемент не находится в области просмотра (в противном случае он не щелкает), что происходит здесь , затем он находит интерактивные координаты элемента с помощью API DevTools здесь :

async _clickablePoint() {
    const [result, layoutMetrics] = await Promise.all([
      this._client.send('DOM.getContentQuads', {
        objectId: this._remoteObject.objectId
      }).catch(debugError),
      this._client.send('Page.getLayoutMetrics'),
    ]);
    if (!result || !result.quads.length)
      throw new Error('Node is either not visible or not an HTMLElement');
    // Filter out quads that have too small area to click into.
    const {clientWidth, clientHeight} = layoutMetrics.layoutViewport;
    const quads = result.quads.map(quad => this._fromProtocolQuad(quad)).map(quad => this._intersectQuadWithViewport(quad, clientWidth, clientHeight)).filter(quad => computeQuadArea(quad) > 1);
    if (!quads.length)
      throw new Error('Node is either not visible or not an HTMLElement');
    // Return the middle point of the first quad.
    const quad = quads[0];
    let x = 0;
    let y = 0;
    for (const point of quad) {
      x += point.x;
      y += point.y;
    }
    return {
      x: x / 4,
      y: y / 4
    };
}

и, наконец, перемещает мышь к координате здесь и нажимает на нее здесь

async click(x, y, options = {}) {
    const {delay = null} = options;
    if (delay !== null) {
      await Promise.all([
        this.move(x, y),
        this.down(options),
      ]);
      await new Promise(f => setTimeout(f, delay));
      await this.up(options);
    } else {
      await Promise.all([
        this.move(x, y),
        this.down(options),
        this.up(options),
      ]);
    }
}

, который использует API DevTools для взаимодействия с мышью здесь

async down(options = {}) {
    const {button = 'left', clickCount = 1} = options;
    this._button = button;
    await this._client.send('Input.dispatchMouseEvent', {
      type: 'mousePressed',
      button,
      x: this._x,
      y: this._y,
      modifiers: this._keyboard._modifiers,
      clickCount
    });
}
...