Chrome DevTools Protocol 进阶:Runtime 域

前言

在 Chrome DevTools Protocol(CDP)中,Runtime 域是一个非常重要的部分,它主要用于与 JavaScript 的执行环境进行交互。通过 Runtime 域,开发者可以在页面上下文中执行 JavaScript 代码、评估表达式、捕获异常等操作,类似于在浏览器的控制台中手动输入代码的操作。Runtime 域的灵活性使得它在调试、测试和自动化任务中得到了广泛应用。

本文将详细介绍 CDP 的 Runtime 域,并通过几个示例展示如何使用它在浏览器中执行代码和处理结果。


Runtime 域简介

Runtime 域包含了一系列与 JavaScript 执行相关的命令和事件,用于控制和管理页面中的 JavaScript 执行环境。常见的功能包括:

  • 在页面上下文中执行任意 JavaScript 代码。
  • 获取 JavaScript 的执行结果。
  • 捕获和处理脚本执行时的异常。
  • 处理 JavaScript 的 promise 和异步操作。
  • 检查、调试 JavaScript 对象的属性和值。

Runtime 域常用命令

1. Runtime.evaluate — 执行 JavaScript 表达式

Runtime.evaluateRuntime 域中最常用的命令,它用于在页面的 JavaScript 上下文中评估一个表达式。这个命令可以在页面中执行任意的 JavaScript 代码,并返回执行结果。

请求结构:

{
  "id": 1,
  "method": "Runtime.evaluate",
  "params": {
    "expression": "document.title",
    "returnByValue": true
  }
}

参数说明:

  • expression: 需要执行的 JavaScript 表达式。例如,获取页面的 document.title
  • returnByValue: 指定返回结果时,是否将 JavaScript 对象以纯 JSON 格式返回。如果为 true,则会将值序列化后返回;如果为 false,则返回对象的引用。

返回结构:

{
  "id": 1,
  "result": {
    "result": {
      "type": "string",
      "value": "Example Page Title"
    }
  }
}

在这个例子中,Runtime.evaluate 执行了表达式 document.title,并返回了当前页面的标题。

2. Runtime.callFunctionOn — 调用 JavaScript 函数

Runtime.callFunctionOn 命令允许开发者在指定的 JavaScript 对象上下文中调用一个函数。这个命令特别适合用于操作已有的 JavaScript 对象,例如 DOM 元素或自定义对象。

请求结构:

{
  "id": 2,
  "method": "Runtime.callFunctionOn",
  "params": {
    "objectId": "1234.5",  // 目标对象的 ID
    "functionDeclaration": "function() { return this.innerHTML; }"
  }
}

参数说明:

  • objectId: JavaScript 对象的 ID,通常通过 Runtime.evaluate 获取到的对象引用。
  • functionDeclaration: 需要调用的 JavaScript 函数声明。

返回结构:

{
  "id": 2,
  "result": {
    "result": {
      "type": "string",
      "value": "<p>Hello, World!</p>"
    }
  }
}

在此示例中,Runtime.callFunctionOn 被用于在特定 DOM 元素的上下文中调用一个函数,获取其 innerHTML 值。

3. Runtime.getProperties — 获取对象属性

Runtime.getProperties 命令用于获取 JavaScript 对象的属性和值。它在调试时非常有用,尤其当你想深入了解某个对象的结构或状态时。

请求结构:

{
  "id": 2,
  "result": {
    "result": {
      "type": "string",
      "value": "<p>Hello, World!</p>"
    }
  }
}

参数说明:

  • objectId: JavaScript 对象的 ID。
  • ownProperties: 如果为 true,则只返回对象自身的属性,不包括继承的属性。

返回结构:

{
  "id": 3,
  "result": {
    "result": [
      {
        "name": "tagName",
        "value": {
          "type": "string",
          "value": "DIV"
        }
      },
      {
        "name": "innerHTML",
        "value": {
          "type": "string",
          "value": "<p>Hello, World!</p>"
        }
      }
    ]
  }
}

通过这个命令,开发者可以获取对象的所有属性和值,并在调试过程中检查它们的状态。


异步操作和 Promise 处理

现代 JavaScript 应用广泛使用异步操作和 Promise 对象。Runtime 域同样提供了一些机制来处理这些异步操作。

1. Runtime.awaitPromise — 处理 Promise

Runtime.awaitPromise 命令可以等待一个 Promise 对象的解析(resolve)或拒绝(reject)。这在处理异步代码时非常有用。

请求结构:

{
  "id": 4,
  "method": "Runtime.evaluate",
  "params": {
    "expression": "new Promise((resolve) => setTimeout(() => resolve(42), 1000))",
    "awaitPromise": true
  }
}

返回结构:

{
  "id": 4,
  "result": {
    "result": {
      "type": "number",
      "value": 42
    }
  }
}

在此例中,Runtime.evaluate 被用于执行一个延迟 1 秒后返回 42 的 Promise。通过设置 awaitPromise: true,我们可以等待 Promise 完成并直接获取其结果。


错误处理

在执行 JavaScript 代码时,错误和异常是不可避免的。Runtime 域提供了一些机制来捕获和处理这些错误,帮助开发者调试代码。

1. 捕获执行错误

Runtime.evaluateRuntime.callFunctionOn 都可以通过响应结构中的 exceptionDetails 字段来返回 JavaScript 执行中的异常。

异常响应结构:

{
  "id": 5,
  "result": {
    "exceptionDetails": {
      "text": "ReferenceError: myVar is not defined",
      "exception": {
        "type": "object",
        "className": "ReferenceError",
        "description": "ReferenceError: myVar is not defined"
      }
    }
  }
}

通过检查 exceptionDetails,开发者可以捕获并处理执行中的错误,进一步提高代码的健壮性。

测试案例

import asyncio
import websockets
import json

async def runtime_complex_test(cdp_url):
    async with websockets.connect(cdp_url) as websocket:
        # 1. 使用 Runtime.evaluate 创建一个对象,并获取对象引用
        expression = """
        (() => {
            const obj = {
                a: 1,
                b: 2,
                add: function() {
                    return this.a + this.b;
                },
                async computeAsync() {
                    return new Promise(resolve => setTimeout(() => resolve(42), 1000));
                }
            };
            return obj;
        })();
        """
        await websocket.send(json.dumps({
            'id': 1,
            'method': 'Runtime.evaluate',
            'params': {
                'expression': expression,
                'returnByValue': False,  # 返回对象的引用而不是值
                'objectGroup': 'exampleGroup'  # 对象组名,方便后续引用
            }
        }))

        response = json.loads(await websocket.recv())
        object_id = response['result']['result']['objectId']
        print(f"创建对象成功,objectId: {object_id}")

        # 2. 使用 Runtime.callFunctionOn 调用对象的 add 方法
        await websocket.send(json.dumps({
            'id': 2,
            'method': 'Runtime.callFunctionOn',
            'params': {
                'objectId': object_id,
                'functionDeclaration': 'function() { return this.add(); }',
                'returnByValue': True  # 直接返回函数结果
            }
        }))

        response = json.loads(await websocket.recv())
        add_result = response['result']['result']['value']
        print(f"调用 add 方法的结果: {add_result}")

        # 3. 使用 Runtime.getProperties 获取对象的属性
        await websocket.send(json.dumps({
            'id': 3,
            'method': 'Runtime.getProperties',
            'params': {
                'objectId': object_id,
                'ownProperties': True  # 仅获取对象自身的属性
            }
        }))

        response = json.loads(await websocket.recv())
        properties = response['result']['result']
        print("对象属性:")
        for prop in properties:
            name = prop['name']
            value = prop['value']['value'] if 'value' in prop['value'] else '不可读取'
            print(f"  {name}: {value}")

        # 4. 使用 Runtime.callFunctionOn 调用异步方法,并通过 Runtime.awaitPromise 处理返回的 Promise
        await websocket.send(json.dumps({
            'id': 4,
            'method': 'Runtime.callFunctionOn',
            'params': {
                'objectId': object_id,
                'functionDeclaration': 'function() { return this.computeAsync(); }',
                'awaitPromise': True  # 等待异步操作完成
            }
        }))

        response = json.loads(await websocket.recv())
        if 'result' in response and response['result']['result']['value'] == 42:
            print(f"异步方法的返回值: {response['result']['result']['value']}")

        # 清理对象
        await websocket.send(json.dumps({
            'id': 5,
            'method': 'Runtime.releaseObjectGroup',
            'params': {
                'objectGroup': 'exampleGroup'
            }
        }))
        print("对象已释放")

# 替换成你实际的 CDP WebSocket URL
cdp_url = "ws://localhost:9222/devtools/page/1F1A646103FECD9BDC9C29868F0E31D1"
asyncio.get_event_loop().run_until_complete(runtime_complex_test(cdp_url))

代码解释:

  1. Runtime.evaluate:我们首先通过 Runtime.evaluate 执行一段 JavaScript 代码,返回一个包含属性和方法的对象。这段代码定义了一个简单的对象 obj,具有属性 ab,一个同步方法 add,和一个异步方法 computeAsync
    • returnByValue: False 表示我们只想返回对象的引用(objectId),而不是返回整个对象的内容。
  2. Runtime.callFunctionOn:使用 Runtime.callFunctionOn,我们在第一个对象上调用 add 方法,并返回其执行结果。该方法直接执行对象内部的 add 函数并返回其结果。
    • returnByValue: True 表示直接返回调用结果,而不是对象引用。
  3. Runtime.getProperties:我们使用 Runtime.getProperties 来获取对象的所有属性及其值。返回的属性列表包括对象 ab 的值。
  4. Runtime.awaitPromise:调用异步方法 computeAsync 时,我们需要等待 Promise 完成。通过 awaitPromise: True,CDP 会等待异步操作完成,并返回 Promise 的最终结果。
  5. 清理对象:最后通过 Runtime.releaseObjectGroup 释放对象,避免内存泄漏。

运行结果:

执行此脚本时,你会看到以下输出,展示了每一步操作的结果:

创建对象成功,objectId: -2387282854439575449.11.1
调用 add 方法的结果: 3
对象属性:
  a: 1
  b: 2
  add: 不可读取
  computeAsync: 不可读取
异步方法的返回值: 42
对象已释放

总结

Runtime 域为开发者提供了一个强大的工具集,可以用于控制和管理页面中的 JavaScript 执行环境。通过 Runtime.evaluateRuntime.callFunctionOn 等命令,开发者能够以编程的方式与页面交互,执行任意的 JavaScript 代码,获取执行结果,并处理 JavaScript 的错误和异步操作。

熟练掌握 Runtime 域可以极大提升调试和自动化任务的效率,帮助开发者更好地管理和控制浏览器中的 JavaScript 执行流程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/922789.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AI技术赋能有限元分析应用实践】pycharm终端与界面设置导入Abaqus2024自带python开发环境

目录 一、具体说明1. **如何在 Windows 环境中执行 Abaqus Python 脚本**2. **如何在 PyCharm 中配置并激活 Abaqus Python 环境**3. **创建 Windows 批处理脚本自动执行 Abaqus Python 脚本**总结二、方法1:通过下面输出获取安装路径导入pycharm方法2:终端脚本执行批处理脚本…

【消息序列】详解(6):深入探讨缓冲区管理与流量控制机制

目录 一、概述 1.1. 缓冲区管理的重要性 1.2. 实现方式 1.2.1. HCI_Read_Buffer_Size 命令 1.2.2. HCI_Number_Of_Completed_Packets 事件 1.2.3. HCI_Set_Controller_To_Host_Flow_Control 命令 1.2.4. HCI_Host_Buffer_Size 命令 1.2.5. HCI_Host_Number_Of_Complete…

虚拟局域网PPTP配置与验证(二)

虚拟局域网PPTP配置与验证(二) windows VPN客户端linux 客户端openwrt客户端性能验证虚拟局域网PPTP配置与验证(一)虚拟局域网PPTP配置与验证(二) : 本文介绍几种客户端连接PPTP服务端的方法,同时对linux/windows/openwrt 操作系统及x86、arm硬件平台下PPTP包转发性能进…

uniapp中使用uni-forms实现表单管理,验证表单

前言 uni-forms 是一个用于表单管理的组件。它提供了一种简化和统一的方式来处理表单数据&#xff0c;包括表单验证、字段绑定和提交逻辑等。使用 uni-forms可以方便地创建各种类型的表单&#xff0c;支持数据双向绑定&#xff0c;可以与其他组件及API进行良好的集成。开发者可…

Hive构建日搜索引擎日志数据分析系统

1.数据预处理 根据自己或者学校系统预制的数据 使用less sogou.txt可查看 wc -l sogou.txt 能够查看总行数 2.数据扩展部分 我的数据位置存放在 /data/bigfiles 点击q退出 将一个文件的内容传递到另一个目录文件下 原数据在 /data/bigfiles ->传递 到/data/workspac…

网络安全的学习方向和路线是怎么样的?

最近有同学问我&#xff0c;网络安全的学习路线是怎么样的&#xff1f; 废话不多说&#xff0c;先上一张图镇楼&#xff0c;看看网络安全有哪些方向&#xff0c;它们之间有什么关系和区别&#xff0c;各自需要学习哪些东西。 在这个圈子技术门类中&#xff0c;工作岗位主要有以…

深入浅出分布式缓存:原理与应用

文章目录 概述缓存分片算法1. Hash算法2. 一致性Hash算法3. 应用场景Redis集群方案1. Redis 集群方案原理2. Redis 集群方案的优势3. Java 代码示例:Redis 集群数据定位Redis 集群中的节点通信机制:Gossip 协议Redis 集群的节点通信:Gossip 协议Redis 集群的节点通信流程Red…

Mysql的加锁情况详解

最近在复习mysql的知识点&#xff0c;像索引、优化、主从复制这些很容易就激活了脑海里尘封的知识&#xff0c;但是在mysql锁的这一块真的是忘的一干二净&#xff0c;一点映像都没有&#xff0c;感觉也有点太难理解了&#xff0c;但是还是想把这块给啃下来&#xff0c;于是想通…

论文模型设置与实验数据:scBERT

Yang, F., Wang, W., Wang, F. et al. scBERT as a large-scale pretrained deep language model for cell type annotation of single-cell RNA-seq data. Nat Mach Intell 4, 852–866 (2022). https://doi.org/10.1038/s42256-022-00534-z 论文地址&#xff1a;scBERT as a…

TCP三次握手的过程是怎样的?

一开始&#xff0c;客户端和服务端都处于CLOSE状态。先是服务端主动监听某个端口&#xff0c;处于LISTEN状态。 &#xff08;1&#xff09;第一次握手 客户端会随机初始化序号&#xff08;client_isn&#xff09;&#xff0c;将此序号填入TCP首部的32位序号字段中&#xff0c…

Java核心知识详解:String类、StringBuffer、数组及日期时间的全面解析

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Java &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 标题 Java核心知识详解&#xff1a;String类、StringBuffer、数组及日期时间的全面解析 摘要 在Java中…

【MATLAB源码-第218期】基于matlab的北方苍鹰优化算法(NGO)无人机三维路径规划,输出做短路径图和适应度曲线.

操作环境&#xff1a; MATLAB 2022a 1、算法描述 北方苍鹰优化算法&#xff08;Northern Goshawk Optimization&#xff0c;简称NGO&#xff09;是一种新兴的智能优化算法&#xff0c;灵感来源于北方苍鹰的捕猎行为。北方苍鹰是一种敏捷且高效的猛禽&#xff0c;广泛分布于北…

SplatFormer: Point Transformer for Robust3D Gaussian Splatting 论文解读

目录 一、概述 二、相关工作 1、NVI新视角插值 2、稀疏视角重建 3、OOD-NVS 4、无约束重建下的正则化技术 5、基于学习的2D-to-3D模型 6、3D点云处理技术 三、SplatFormer 1、Point Transformer V3 2、特征解码器 3、损失函数 四、数据集 五、实验 一、概述 该论…

Azkaban部署

首先我们需要现在相关的组件&#xff0c;在这里已经给大家准备好了相关的安装包&#xff0c;有需要的可以自行下载。 只需要启动hadoop集群就可以&#xff0c;如果现在你的hive是打开的&#xff0c;那么请你关闭&#xff01;&#xff01;&#xff01; 如果不关会造成证书冲突…

目标检测模型优化与部署

目录 引言数据增强 随机裁剪随机翻转颜色抖动 模型微调 加载预训练模型修改分类器训练模型 损失函数 分类损失回归损失 优化器算法思路 RPN (Region Proposal Network)Fast R-CNN损失函数 部署与应用 使用 Flask 部署使用 Docker 容器化 参考资料 引言 目标检测是计算机视觉…

Charles抓包工具-笔记

摘要 概念&#xff1a; Charles是一款基于 HTTP 协议的代理服务器&#xff0c;通过成为电脑或者浏览器的代理&#xff0c;然后截取请求和请求结果来达到分析抓包的目的。 功能&#xff1a; Charles 是一个功能全面的抓包工具&#xff0c;适用于各种网络调试和优化场景。 它…

java: itext8.05 create pdf

只能调用windows 已安装的字体&#xff0c;这样可以在系统中先预装字体&#xff0c;5.0 可以调用自配文件夹的字体文件。CSharp donetItext8.0 可以调用。 /*** encoding: utf-8* 版权所有 2024 ©涂聚文有限公司 言語成了邀功盡責的功臣&#xff0c;還需要行爲每日來值班…

Kafka 生产者优化与数据处理经验

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…

C高级学习笔记

……接上文 硬链接和软连接&#xff08;符号链接&#xff09; 硬链接 硬链接文件可以理解为文件的副本&#xff08;可以理解为复制粘贴&#xff09; ln 根据Linux系统分配给文件的inode(ls -li)号进行建立&#xff0c;没有办法跨越文件系统 格式&#xff1a;ln 被链接的文件&am…

Java基于SpringBoot+Vue的藏区特产销售平台

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…