How does Puppeteer handle the click Object / DevTools Protocol Chromium/Chrome

天涯浪子 提交于 2021-01-28 19:37:59

问题


I need to know how puppeteer handles the click object, as well as Chromium DevTools API. I've tried to research it on my own and have found myself not being able to find the actual code that handles it.

The reason why I need to know is I'm developing a wrapper that tests events in code for testing Web Pages, and was looking to see if implementing a event handling routine is beneficial instead of using puppeteers interface of events (clicks and taps an hover, as well as other events that might be needed like touch events, or scrolling)

Here is how far I've gotten:

Puppeteer API uses the Frame Logic of DevTools to contact 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;
        }

This is as far as I have gotten because I've tried to look up the Page Protocol but I can't figure out what happens there.

Any help would be appreciated, even in research.


回答1:


The main parts are happening in JSHandle here:

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

It scrolls until the element is in viewport (otherwise it won't click) which is happening here, then it finds the clickable coordinates on the element using DevTools API here:

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
    };
}

and finally it moves the mouse to the coordinate here and clicks on it here

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),
      ]);
    }
}

which uses DevTools API to interact with mouse here

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
    });
}


来源:https://stackoverflow.com/questions/60973617/how-does-puppeteer-handle-the-click-object-devtools-protocol-chromium-chrome

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!