Closed10

Puppeteerのコードを見つつ、BiDiを手でさわってみる

Yusuke IwakiYusuke Iwaki

起動パラメータが変わるとかではなく、設定値に書かれる値が変わるだけ?
リモートデバッギングを有効にするのはオプションパラメータで、そのときに使うのがCDPかWebDriver BiDiかの違いのようだ。

      userDataDir = await mkdtemp(this.getProfilePath());
      firefoxArguments.push('--profile');
      firefoxArguments.push(userDataDir);
    }

    await createProfile(SupportedBrowsers.FIREFOX, {
      path: userDataDir,
      preferences: FirefoxLauncher.getPreferences(
        extraPrefsFirefox,
        options.protocol
      ),
    });
  static getPreferences(
    extraPrefsFirefox?: Record<string, unknown>,
    protocol?: 'cdp' | 'webDriverBiDi'
  ): Record<string, unknown> {
    return {
      ...extraPrefsFirefox,
      ...(protocol === 'webDriverBiDi'
        ? {
            // Only enable the WebDriver BiDi protocol
            'remote.active-protocols': 1,
          }
        : {
            // Do not close the window when the last tab gets closed
            'browser.tabs.closeWindowWithLastTab': false,
            // Prevent various error message on the console
            // jest-puppeteer asserts that no error message is emitted by the console
            'network.cookie.cookieBehavior': 0,
            // Temporarily force disable BFCache in parent (https://bit.ly/bug-1732263)
            'fission.bfcacheInParent': false,
            // Only enable the CDP protocol
            'remote.active-protocols': 2,
          }),
      // Force all web content to use a single content process. TODO: remove
      // this once Firefox supports mouse event dispatch from the main frame
      // context. Once this happens, webContentIsolationStrategy should only
      // be set for CDP. See
      // https://bugzilla.mozilla.org/show_bug.cgi?id=1773393
      'fission.webContentIsolationStrategy': 0,
    };

Firefoxのドキュメントにも明記がある

https://firefox-source-docs.mozilla.org/remote/Prefs.html#remote-active-protocols

remote.active-protocols¶
Defines the remote protocols that are active. Available protocols are, WebDriver BiDi (1), and CDP (2). Multiple protocols can be activated at the same time by using bitwise or with the values. Defaults to 3 (WebDriver BiDi and CDP).

Yusuke IwakiYusuke Iwaki
profile.mjs
import { createProfile } from '@puppeteer/browsers';

await createProfile('firefox', {
    path: '/Users/yusuke-iwaki/src/github.com/YusukeIwaki/bidi_playground_firefox/my_prefs',
    preferences: {
        'remote.active-protocols': 1,
        'fission.webContentIsolationStrategy': 0,
    }
})

console.log("DONE")
node profile.mjs

すると、以下のようなファイルが書き出された。

my_prefs/user.js
user_pref("app.normandy.api_url", "");
user_pref("app.update.checkInstallTime", false);
user_pref("app.update.disabledForTesting", true);
user_pref("apz.content_response_timeout", 60000);
user_pref("browser.contentblocking.features.standard", "-tp,tpPrivate,cookieBehavior0,-cm,-fp");
user_pref("browser.dom.window.dump.enabled", true);
user_pref("browser.newtabpage.activity-stream.feeds.system.topstories", false);
user_pref("browser.newtabpage.enabled", false);
user_pref("browser.pagethumbnails.capturing_disabled", true);
user_pref("browser.safebrowsing.blockedURIs.enabled", false);
user_pref("browser.safebrowsing.downloads.enabled", false);
user_pref("browser.safebrowsing.malware.enabled", false);
user_pref("browser.safebrowsing.phishing.enabled", false);
user_pref("browser.search.update", false);
user_pref("browser.sessionstore.resume_from_crash", false);
user_pref("browser.shell.checkDefaultBrowser", false);
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.startup.homepage_override.mstone", "ignore");
user_pref("browser.startup.page", 0);
user_pref("browser.tabs.disableBackgroundZombification", false);
user_pref("browser.tabs.warnOnCloseOtherTabs", false);
user_pref("browser.tabs.warnOnOpen", false);
user_pref("browser.translations.automaticallyPopup", false);
user_pref("browser.uitour.enabled", false);
user_pref("browser.urlbar.suggest.searches", false);
user_pref("browser.usedOnWindows10.introURL", "");
user_pref("browser.warnOnQuit", false);
user_pref("datareporting.healthreport.documentServerURI", "http://dummy.test/dummy/healthreport/");
user_pref("datareporting.healthreport.logging.consoleEnabled", false);
user_pref("datareporting.healthreport.service.enabled", false);
user_pref("datareporting.healthreport.service.firstRun", false);
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
user_pref("devtools.jsonview.enabled", false);
user_pref("dom.disable_open_during_load", false);
user_pref("dom.file.createInChild", true);
user_pref("dom.ipc.reportProcessHangs", false);
user_pref("dom.max_chrome_script_run_time", 0);
user_pref("dom.max_script_run_time", 0);
user_pref("extensions.autoDisableScopes", 0);
user_pref("extensions.enabledScopes", 5);
user_pref("extensions.getAddons.cache.enabled", false);
user_pref("extensions.installDistroAddons", false);
user_pref("extensions.screenshots.disabled", true);
user_pref("extensions.update.enabled", false);
user_pref("extensions.update.notifyUser", false);
user_pref("extensions.webservice.discoverURL", "http://dummy.test/dummy/discoveryURL");
user_pref("focusmanager.testmode", true);
user_pref("general.useragent.updates.enabled", false);
user_pref("geo.provider.testing", true);
user_pref("geo.wifi.scan", false);
user_pref("hangmonitor.timeout", 0);
user_pref("javascript.options.showInConsole", true);
user_pref("media.gmp-manager.updateEnabled", false);
user_pref("media.sanity-test.disabled", true);
user_pref("network.cookie.sameSite.laxByDefault", false);
user_pref("network.http.prompt-temp-redirect", false);
user_pref("network.http.speculative-parallel-limit", 0);
user_pref("network.manage-offline-status", false);
user_pref("network.sntp.pools", "dummy.test");
user_pref("plugin.state.flash", 0);
user_pref("privacy.trackingprotection.enabled", false);
user_pref("remote.enabled", true);
user_pref("security.certerrors.mitm.priming.enabled", false);
user_pref("security.fileuri.strict_origin_policy", false);
user_pref("security.notification_enable_delay", 0);
user_pref("services.settings.server", "http://dummy.test/dummy/blocklist/");
user_pref("signon.autofillForms", false);
user_pref("signon.rememberSignons", false);
user_pref("startup.homepage_welcome_url", "about:blank");
user_pref("startup.homepage_welcome_url.additional", "");
user_pref("toolkit.cosmeticAnimations.enabled", false);
user_pref("toolkit.startup.max_resumed_crashes", -1);
user_pref("remote.active-protocols", 1);
user_pref("fission.webContentIsolationStrategy", 0);
Yusuke IwakiYusuke Iwaki
launch.sh
#!/bin/sh

"/Applications/Firefox Developer Edition.app/Contents/MacOS/firefox" \
  --remote-debugging-port=0 \
  --profile my_prefs \
  --no-remote \
  --foreground \
  about:blank
$ ./launch.sh
console.warn: services.settings: Ignoring preference override of remote settings server
console.warn: services.settings: Allow by setting MOZ_REMOTE_SETTINGS_DEVTOOLS=1 in the environment
WebDriver BiDi listening on ws://127.0.0.1:51376

こんな感じでblank画面が起動し、BiDiポートが開かれる。

Yusuke IwakiYusuke Iwaki

/session というところにWebSocket経由でしゃべりかけるといいらしい。

./node_modules/.bin/wscat -c ws://127.0.0.1:51376/session
Connected (press CTRL+C to quit)
Yusuke IwakiYusuke Iwaki
yusukeiwaki.mjs
import { launch } from 'puppeteer-core'

const browser = await launch({
    product: 'firefox',
    protocol: 'webDriverBiDi',
    executablePath: '/Applications/Firefox Developer Edition.app/Contents/MacOS/firefox',
})
const page = await browser.newPage()
await page.goto('https://github.com/YusukeIwaki')
await page.screenshot({path: 'YusukeIwaki.png'})
await browser.close()

こういうサンプルコードを動かしてみる。

$ DEBUG=puppeteer:* node yusukeiwaki.mjs
  puppeteer:browsers:launcher Launching /Applications/Firefox Developer Edition.app/Contents/MacOS/firefox --no-remote --foreground --headless about:blank --remote-debugging-port=0 --profile /var/folders/d6/f04pgvzn7zv_sfkxgzs5tfxc0000gn/T/puppeteer_dev_firefox_profile-Ugxjz0 { detached: true, env: {}, stdio: [ 'pipe', 'ignore', 'pipe' ] } +0ms
  puppeteer:browsers:launcher Launched 98295 +6ms
  puppeteer:webDriverBiDi:SEND ► [
  puppeteer:webDriverBiDi:SEND ►   '{"id":1,"method":"session.new","params":{"capabilities":{"alwaysMatch":{"acceptInsecureCerts":false,"webSocketUrl":true}}}}'
  puppeteer:webDriverBiDi:SEND ► ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"success","id":1,"result":{"sessionId":"a421f2b8-9da8-4bf3-8ba3-f39a9bcec3f4","capabilities":{"browserName":"firefox","browserVersion":"125.0","platformName":"mac","acceptInsecureCerts":false,"proxy":{},"setWindowRect":true,"userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0","moz:accessibilityChecks":false,"moz:buildID":"20240405130120","moz:headless":true,"moz:platformVersion":"23.3.0","moz:processID":98295,"moz:profile":"/var/folders/d6/f04pgvzn7zv_sfkxgzs5tfxc0000gn/T/puppeteer_dev_firefox_profile-Ugxjz0","moz:shutdownTimeout":60000,"moz:windowless":false}}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:SEND ► [ '{"id":2,"method":"browser.getUserContexts","params":{}}' ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"success","id":2,"result":{"userContexts":[{"userContext":"default"},{"userContext":"20ebb20a-259d-489c-99af-9c916b83022f"},{"userContext":"8c6db705-9ce3-4b5c-adb2-d2db53c8d443"},{"userContext":"59494d2b-d668-4ee0-84ac-8c2a21fa6f4a"},{"userContext":"dfc081f1-47a2-4d41-a086-6d1b96eef8fd"}]}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:SEND ► [ '{"id":3,"method":"browsingContext.getTree","params":{}}' ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"success","id":3,"result":{"contexts":[{"children":[],"context":"c4ee8e5e-6b43-4fd6-974a-c09cb16e8b39","url":"about:blank","userContext":"default","parent":null}]}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:SEND ► [
  puppeteer:webDriverBiDi:SEND ►   '{"id":4,"method":"session.subscribe","params":{"events":["browsingContext","network","log","script"]}}'
  puppeteer:webDriverBiDi:SEND ► ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"event","method":"script.realmCreated","params":{"realm":"6bff0075-2438-4bdc-bc3f-545c9fc5e43c","origin":"null","context":"c4ee8e5e-6b43-4fd6-974a-c09cb16e8b39","type":"window"}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [ '{"type":"success","id":4,"result":{}}' ] +0ms
  puppeteer:webDriverBiDi:SEND ► [
  puppeteer:webDriverBiDi:SEND ►   '{"id":5,"method":"browsingContext.create","params":{"type":"tab","userContext":"default"}}'
  puppeteer:webDriverBiDi:SEND ► ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"event","method":"browsingContext.contextCreated","params":{"children":null,"context":"851bf1c6-652f-487c-89ef-c0bc231782ae","url":"about:blank","userContext":"default","parent":null}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"event","method":"script.realmCreated","params":{"realm":"d14a4c8f-b576-4ff0-8dfe-abcba8def3f1","origin":"null","context":"851bf1c6-652f-487c-89ef-c0bc231782ae","type":"window"}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"event","method":"browsingContext.navigationStarted","params":{"context":"851bf1c6-652f-487c-89ef-c0bc231782ae","navigation":"57e85a6a-3796-4290-b67e-0725c4b86673","timestamp":1712495783822,"url":"about:blank"}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"event","method":"script.realmDestroyed","params":{"realm":"d14a4c8f-b576-4ff0-8dfe-abcba8def3f1"}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"event","method":"script.realmCreated","params":{"realm":"9e784e87-5c31-4cd1-afbb-6c556eb79f1e","origin":"null","context":"851bf1c6-652f-487c-89ef-c0bc231782ae","type":"window"}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"event","method":"browsingContext.domContentLoaded","params":{"context":"851bf1c6-652f-487c-89ef-c0bc231782ae","timestamp":1712495783856,"url":"about:blank","navigation":"57e85a6a-3796-4290-b67e-0725c4b86673"}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"event","method":"browsingContext.load","params":{"context":"851bf1c6-652f-487c-89ef-c0bc231782ae","timestamp":1712495783856,"url":"about:blank","navigation":"57e85a6a-3796-4290-b67e-0725c4b86673"}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"success","id":5,"result":{"context":"851bf1c6-652f-487c-89ef-c0bc231782ae"}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:SEND ► [
  puppeteer:webDriverBiDi:SEND ►   '{"id":6,"method":"browsingContext.setViewport","params":{"context":"851bf1c6-652f-487c-89ef-c0bc231782ae","viewport":{"width":800,"height":600},"devicePixelRatio":null}}'
  puppeteer:webDriverBiDi:SEND ► ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [ '{"type":"success","id":6,"result":{}}' ] +0ms
  puppeteer:webDriverBiDi:SEND ► [
  puppeteer:webDriverBiDi:SEND ►   '{"id":7,"method":"browsingContext.navigate","params":{"context":"851bf1c6-652f-487c-89ef-c0bc231782ae","url":"https://github.com/YusukeIwaki","wait":"interactive"}}'
  puppeteer:webDriverBiDi:SEND ► ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBiDi:RECV ◀   '{"type":"event","method":"browsingContext.navigationStarted","params":{"context":"851bf1c6-652f-487c-89ef-c0bc231782ae","navigation":"bc45c64c-f687-491b-b493-d30939ac916d","timestamp":1712495783916,"url":"https://github.com/YusukeIwaki"}}'
  puppeteer:webDriverBiDi:RECV ◀ ] +0ms
  puppeteer:webDriverBiDi:RECV ◀ [
  puppeteer:webDriverBi

こんな具合に、いろいろやり取りが交わされている。

Yusuke IwakiYusuke Iwaki

wscatで手で打っていってみる。

> {"id":1,"method":"session.new","params":{"capabilities":{"alwaysMatch":{"acceptInsecureCerts":false,"webSocketUrl":true}}}}
< {"type":"success","id":1,"result":{"sessionId":"73f08924-7d62-400c-bcfd-f0b665658d23","capabilities":{"browserName":"firefox","browserVersion":"125.0","platformName":"mac","acceptInsecureCerts":false,"proxy":{},"setWindowRect":true,"userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0","moz:accessibilityChecks":false,"moz:buildID":"20240405130120","moz:headless":false,"moz:platformVersion":"23.3.0","moz:processID":93550,"moz:profile":"/Users/yusuke-iwaki/src/github.com/YusukeIwaki/bidi_playground_firefox/my_prefs","moz:shutdownTimeout":60000,"moz:windowless":false}}}

セッションID 73f08924-7d62-400c-bcfd-f0b665658d23 が払い出された。

> {"id":2,"method":"browser.getUserContexts","params":{}}
< {"type":"success","id":2,"result":{"userContexts":[{"userContext":"default"},{"userContext":"afb4559f-4293-4649-9a4e-35a505c1f2b0"},{"userContext":"dd9fb073-d7ca-4fd8-b77e-4d93724a9122"},{"userContext":"088f3c09-b35d-4569-8ca7-37acc6e00b5f"},{"userContext":"a1aa0e16-d3fd-4193-a103-e3a7808644e0"}]}}
> {"id":3,"method":"browsingContext.getTree","params":{}}
< {"type":"success","id":3,"result":{"contexts":[{"children":[],"context":"18e4e97c-2427-4264-aa39-cf2391b34831","url":"about:blank","userContext":"default","parent":null}]}}

現在のBrowserContextのIDなどもわかる。

Yusuke IwakiYusuke Iwaki

{"id":4,"method":"session.subscribe","params":{"events":["browsingContext","network","log","script"]}}

これ、networkをsubscribeすると、ものすごいログが出るので、一旦 browsingContext だけsubscribeしておけばよかろう。

> {"id":4,"method":"session.subscribe","params":{"events":["browsingContext"]}}
< {"type":"success","id":4,"result":{}}
> {"id":5,"method":"browsingContext.create","params":{"type":"tab","userContext":"default"}}
< {"type":"event","method":"browsingContext.contextCreated","params":{"children":null,"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","url":"about:blank","userContext":"default","parent":null}}
< {"type":"event","method":"browsingContext.navigationStarted","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","navigation":"a553e0f2-3817-4ed0-9f91-ef1365ba154e","timestamp":1712496372734,"url":"about:blank"}}
< {"type":"event","method":"browsingContext.domContentLoaded","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","timestamp":1712496372758,"url":"about:blank","navigation":"a553e0f2-3817-4ed0-9f91-ef1365ba154e"}}
< {"type":"event","method":"browsingContext.load","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","timestamp":1712496372758,"url":"about:blank","navigation":"a553e0f2-3817-4ed0-9f91-ef1365ba154e"}}
< {"type":"success","id":5,"result":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e"}}

タブが1個開いた。

eebf5426-34d2-4017-b0ec-f920553a4f2e というBrowserContextでアクセスすればいいらしい。

> {"id":7,"method":"browsingContext.navigate","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","url":"https://github.com/YusukeIwaki","wait":"interactive"}}
< {"type":"event","method":"browsingContext.navigationStarted","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","navigation":"3a68d01f-db3b-43f5-bb29-4011565b9276","timestamp":1712496544684,"url":"https://github.com/YusukeIwaki"}}
< {"type":"event","method":"browsingContext.fragmentNavigated","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","navigation":"fcbaa27c-2f34-446f-a9e4-e1af2d0b4fa8","timestamp":1712496546023,"url":"https://github.com/YusukeIwaki"}}
< {"type":"success","id":7,"result":{"navigation":"3a68d01f-db3b-43f5-bb29-4011565b9276","url":"https://github.com/YusukeIwaki"}}
< {"type":"event","method":"browsingContext.fragmentNavigated","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","navigation":"e3654924-487b-408e-9a95-3f7989fa00f0","timestamp":1712496546097,"url":"https://github.com/YusukeIwaki"}}
< {"type":"event","method":"browsingContext.domContentLoaded","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","timestamp":1712496546131,"url":"https://github.com/YusukeIwaki","navigation":"e3654924-487b-408e-9a95-3f7989fa00f0"}}
< {"type":"event","method":"browsingContext.fragmentNavigated","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","navigation":"a49e32a5-5d99-4fc1-b6ae-6a882d17c7c2","timestamp":1712496546160,"url":"https://github.com/YusukeIwaki"}}
< {"type":"event","method":"browsingContext.load","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","timestamp":1712496546161,"url":"https://github.com/YusukeIwaki","navigation":"a49e32a5-5d99-4fc1-b6ae-6a882d17c7c2"}}

おお、ページが読み込まれた。

> {"id":9,"method":"browsingContext.captureScreenshot","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","origin":"viewport","format":{"type":"image/png"}}}
< {"type":"success","id":9,"result":{"data":"iVBORw0KGgoAAAANSUhEUgAACtIAAAYsCAYAAACRfouVAAAgAElEQVR4XuydBbwV1RrFP0HqCQgijYSBgAqohJSSKiopiEiHpKAoXSKSktJdgpS0CkgKIl2KEhYo3d........m9DeelSI5Pq3qtZbsFUi6nLwrRrGzQ4jrXGsMRMIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIGEUARlqjSGMeEAABEACB+EDg/+r/v/hgEVweAAAAAElFTkSuQmCC"}}

スクリーンショットがBase64エンコードされて返ってくる。

タブを閉じるのが

{"id":10,"method":"browsingContext.close","params":{"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e"}}
< {"type":"event","method":"browsingContext.contextDestroyed","params":{"children":null,"context":"eebf5426-34d2-4017-b0ec-f920553a4f2e","url":"https://github.com/YusukeIwaki","userContext":"default","parent":null}}
< {"type":"success","id":10,"result":{}}

ブラウザ自身を落とすには

> {"id":11,"method":"browser.close","params":{}}
< {"type":"success","id":11,"result":{}}
Disconnected (code: 1000, reason: "")
Yusuke IwakiYusuke Iwaki

Elementを見つける系の処理

これはがっつりJS。CDPにせよBiDiにせよ、ただの土管。

  • script.evaluate
  • script.callFunction

あたりがフル活用されている。

ざっくりいうと、DOMノードをワンショットで見つける系の処理は querySelector , querySelectorAll などが
waitForSelectorは、MutationObserverなどが
それぞれ内部で使われている。

このスクラップは2025/01/06にクローズされました