<!-- Source: https://deepseek-browser-use.io.nexus/23 -->

# 源码教程：Cookie、存储与页面设置

这一组命令最容易混淆作用域。任务的 Page 可以独立，但 BrowserContext 可能共享；修改上下文会影响多个任务。不能把所有 set 开头的命令都当作当前页设置。

## 1. Cookie 是 Context 级数据

| 命令 | 实现 |
| --- | --- |
| `get_cookies` | 调用 context.cookies，可按 URL 过滤；将 Cookie 对象转为字段列表并返回 count |
| `set_cookie` | 创建含 name、value 的 Cookie，按可选 url 设置作用域，再调用 addCookies |
| `clear_cookies` | 调用 context.clearCookies，清除共享上下文 Cookie，不是只清当前域名 |

当前 set_cookie 命令没有暴露 domain/path 等完整 Cookie 构造参数。实际使用应提供有效 URL；不能因为 url 在注册层可选就认为缺少作用域的 Cookie 一定能添加成功。

```json
{"id":"1001","method":"set_cookie","params":{"name":"demo_preference","value":"compact","url":"https://example.com"}}
```

不要在公开示例中使用真实登录 Cookie。读取 Cookie 的返回属于账号数据，不能因为是只读操作就随意写入教程。

## 2. localStorage 由当前页面 origin 决定

`get_local_storage` 在当前 Page 执行脚本：传 key 时读取单项，未传时返回整个 localStorage 的 JSON 字符串，放在 data.value。客户端若需要对象，应再解析该字符串，而不是假定 data.value 本身已经是对象。

`set_local_storage` 调用 localStorage.setItem；`clear_local_storage` 调用 localStorage.clear。它们作用于当前页面的 origin。共享上下文中，同源页签会看到同一份持久化数据；换到另一个域名后，读到的不是原站点数据。

不透明 origin 或受限制页面可能拒绝存储访问。先导航到需要测试的本地站点，不要在空白页上用失败结果判断存储实现坏了。

## 3. 设置命令的真实作用域

| 命令 | 调用对象 | 实现和影响 |
| --- | --- | --- |
| `set_viewport` | Page | setViewportSize，修改当前页 CSS 视口，返回 width/height |
| `set_media` | Page | emulateMedia，设置 light/dark/no-preference，不是操作系统主题 |
| `set_geolocation` | Context | 先授予 geolocation 权限，再设置经纬度 |
| `set_offline` | Context | setOffline，影响共享上下文网络状态 |
| `set_headers` | Context | 解析 headersJson 为字符串映射，调用 setExtraHTTPHeaders |
| `set_credentials` | Context 创建配置 | HTTP 基本认证参数需要重建上下文，服务会检查任务数和启动方式 |

headersJson 是一个包含 JSON 文本的字符串，不是 params 内任意嵌套对象：

```json
{"id":"1001","method":"set_headers","params":{"headersJson":"{\"X-Demo-Mode\":\"fixture\"}"}}
```

`set_credentials` 不是网站登录表单填用户名密码。当前实现只允许具备可重建配置、且没有其他任务会受影响时执行；CDP 接入路径可能没有可重建的持久上下文选项，会明确拒绝。

## 4. 验证与恢复

使用两个同源页签和一个不同源页签测试 Cookie、localStorage 的共享边界。视口与主题测试则验证只改变当前 Page。设置离线、请求头或地理位置后记录原始配置，在测试结束恢复，避免影响后续任务。涉及真实共享 profile 时，不能把 clear_cookies 当作普通的无损重置。

## 注册参数与 Java 入口

以下按 `CommandTable` 实际读取参数整理。`*` 表示注册层使用必填读取器；其余字段省略后由服务决定默认行为。带条件的入口仍需满足正文说明，例如上传文件来源、元素定位二选一。外层 `id` 不重复列出。

| 命令 | params 字段 | Java 入口 |
| --- | --- | --- |
| `get_cookies` | `url` | `getCookies` |
| `set_cookie` | `name`*、`value`*、`url` | `setCookie` |
| `clear_cookies` | 无 | `clearCookies` |
| `get_local_storage` | `key` | `getLocalStorage` |
| `set_local_storage` | `key`*、`value` | `setLocalStorage` |
| `clear_local_storage` | 无 | `clearLocalStorage` |
| `set_viewport` | `width`*、`height`* | `setViewport` |
| `set_geolocation` | `latitude`*、`longitude`* | `setGeolocation` |
| `set_offline` | `offline` | `setOffline` |
| `set_headers` | `headersJson`* | `setHeaders` |
| `set_credentials` | `username`*、`password`* | `setCredentials` |
| `set_media` | `colorScheme`* | `setMedia` |

<!-- current-source-tutorial -->
## 当前源码：命令注册与执行

先在本章上半部分理解行为，再按命令展开实现。注册代码说明 JSON 参数如何传给 Java；服务方法展示实际浏览器操作。方法依赖共享类中的字段和辅助函数，不应脱离原类直接粘贴编译。

### `get_cookies`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("get_cookies", (svc, id, a) -> svc.getCookies(id, optStr(a, "url")));
```

:::

::: details 展开 getCookies 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo getCookies(Long browserId, String url) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    List<Cookie> cookies = (url == null || url.isEmpty()) ? inst.context.cookies() : inst.context.cookies(url);
    List<Kv> result = new ArrayList<>();
    for (Cookie cookie : cookies) {
      result.add(Kv.by("name", cookie.name).set("value", cookie.value).set("domain", cookie.domain)
          .set("path", cookie.path).set("expires", cookie.expires).set("httpOnly", cookie.httpOnly)
          .set("secure", cookie.secure).set("sameSite", cookie.sameSite == null ? null : cookie.sameSite.name()));
    }
    return RespBodyVo.ok(Kv.by("cookies", result).set("count", result.size()));
  }
```

:::

### `set_cookie`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("set_cookie", (svc, id, a) -> svc.setCookie(id, reqStr(a, "name"), reqStr(a, "value"), optStr(a, "url")));
```

:::

::: details 展开 setCookie 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo setCookie(Long browserId, String name, String value, String url) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    Cookie cookie = new Cookie(name, value);
    if (url != null && !url.isEmpty()) {
      cookie.setUrl(url);
    }
    try {
      inst.context.addCookies(Arrays.asList(cookie));
    } catch (PlaywrightException e) {
      return RespBodyVo.fail("set_cookie 失败：" + briefMessage(e.getMessage()));
    }
    return RespBodyVo.ok();
  }
```

:::

### `clear_cookies`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("clear_cookies", (svc, id, a) -> svc.clearCookies(id));
```

:::

::: details 展开 clearCookies 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo clearCookies(Long browserId) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    inst.context.clearCookies();
    return RespBodyVo.ok();
  }
```

:::

### `get_local_storage`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("get_local_storage", (svc, id, a) -> svc.getLocalStorage(id, optStr(a, "key")));
```

:::

::: details 展开 getLocalStorage 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo getLocalStorage(Long browserId, String key) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    try {
      Object value = inst.page.evaluate("(key) => key ? localStorage.getItem(key) : JSON.stringify(localStorage)",
          key == null ? "" : key);
      return RespBodyVo.ok(Kv.by("value", value));
    } catch (PlaywrightException e) {
      return RespBodyVo.fail("get_local_storage 失败：" + briefMessage(e.getMessage()));
    }
  }
```

:::

### `set_local_storage`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("set_local_storage", (svc, id, a) -> svc.setLocalStorage(id, reqStr(a, "key"), optStr(a, "value")));
```

:::

::: details 展开 setLocalStorage 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo setLocalStorage(Long browserId, String key, String value) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    try {
      inst.page.evaluate("([k, v]) => localStorage.setItem(k, v)", Arrays.asList(key, value));
      return RespBodyVo.ok();
    } catch (PlaywrightException e) {
      return RespBodyVo.fail("set_local_storage 失败：" + briefMessage(e.getMessage()));
    }
  }
```

:::

### `clear_local_storage`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("clear_local_storage", (svc, id, a) -> svc.clearLocalStorage(id));
```

:::

::: details 展开 clearLocalStorage 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo clearLocalStorage(Long browserId) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    try {
      inst.page.evaluate("() => localStorage.clear()");
      return RespBodyVo.ok();
    } catch (PlaywrightException e) {
      return RespBodyVo.fail("clear_local_storage 失败：" + briefMessage(e.getMessage()));
    }
  }
```

:::

### `set_viewport`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("set_viewport", (svc, id, a) -> svc.setViewport(id, reqInt(a, "width"), reqInt(a, "height")));
```

:::

::: details 展开 setViewport 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo setViewport(Long browserId, int width, int height) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    inst.page.setViewportSize(width, height);
    return RespBodyVo.ok(Kv.by("width", width).set("height", height));
  }
```

:::

### `set_geolocation`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("set_geolocation", (svc, id, a) -> svc.setGeolocation(id, reqDouble(a, "latitude"), reqDouble(a, "longitude")));
```

:::

::: details 展开 setGeolocation 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo setGeolocation(Long browserId, double latitude, double longitude) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    try {
      inst.context.grantPermissions(Arrays.asList("geolocation"));
      inst.context.setGeolocation(new Geolocation(latitude, longitude));
      return RespBodyVo.ok();
    } catch (PlaywrightException e) {
      return RespBodyVo.fail("set_geolocation 失败：" + briefMessage(e.getMessage()));
    }
  }
```

:::

### `set_offline`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("set_offline", (svc, id, a) -> svc.setOffline(id, optBool(a, "offline")));
```

:::

::: details 展开 setOffline 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo setOffline(Long browserId, boolean offline) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    inst.context.setOffline(offline);
    return RespBodyVo.ok(Kv.by("offline", offline));
  }
```

:::

### `set_headers`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("set_headers", (svc, id, a) -> svc.setHeaders(id, reqStr(a, "headersJson")));
```

:::

::: details 展开 setHeaders 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo setHeaders(Long browserId, String headersJson) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    try {
      JSONObject obj = JSON.parseObject(headersJson);
      Map<String, String> headers = new LinkedHashMap<>();
      for (String key : obj.keySet()) {
        headers.put(key, String.valueOf(obj.get(key)));
      }
      inst.context.setExtraHTTPHeaders(headers);
      return RespBodyVo.ok(Kv.by("headers", headers));
    } catch (Exception e) {
      return RespBodyVo.fail("set_headers 失败：" + briefMessage(e.getMessage()));
    }
  }
```

:::

### `set_credentials`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("set_credentials", (svc, id, a) -> svc.setCredentials(id, reqStr(a, "username"), reqStr(a, "password")));
```

:::

::: details 展开 setCredentials 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo setCredentials(Long browserId, String username, String password) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    if (inst.opts == null) {
      // opts 为空 = 这次浏览器是「自己拉进程 + CDP 接上」起来的(用户自己的 Chrome profile,或 Edge):
      // 这种上下文的创建参数不在我们手里,HTTP 认证凭据没地方设
      SharedBrowser shared = sharedBrowser;
      boolean edge = shared != null && shared.resolvedType.isEdge();
      return RespBodyVo.fail("set_credentials 失败：当前浏览器走的是「自己拉进程 + CDP 接入」这条启动路径（"
          + (edge ? "browser=edge" : "用用户自己的 Chrome profile") + "），HTTP 认证凭据只能在 Playwright "
          + "创建上下文时设置，这条路径上没法重建上下文"
          + (edge ? "；需要 HTTP 基本认证时改用 browser=chrome 或 browser=chromium（托管 profile 那条路）" : ""));
    }
    if (INSTANCES.size() > 1) {
      return RespBodyVo.fail("set_credentials 会重建整个浏览器，而所有任务共用同一个浏览器与 profile；请先 close 掉其它任务再试");
    }
    inst.opts.setHttpCredentials(new HttpCredentials(username, password));
    restartContext(inst);
    return RespBodyVo.ok(Kv.by("restarted", true));
  }
```

:::

### `set_media`

::: details 展开参数注册

源码：`playwright-server/src/main/java/nexus/io/ai/browser/actions/registry/CommandTable.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
    put("set_media", (svc, id, a) -> svc.setMedia(id, reqStr(a, "colorScheme")));
```

:::

::: details 展开 setMedia 实现

源码：`playwright-server/src/main/java/nexus/io/ai/browser/service/PlaywrightService.java`。以下为当前实现，可放回原类中阅读；依赖同类字段和辅助方法，并非独立编译单元。

```java
  public RespBodyVo setMedia(Long browserId, String colorScheme) {
    BrowserInstance inst = INSTANCES.get(browserId);
    if (inst == null) {
      return notFound(browserId);
    }
    try {
      ColorScheme scheme = "dark".equalsIgnoreCase(colorScheme) ? ColorScheme.DARK
          : "light".equalsIgnoreCase(colorScheme) ? ColorScheme.LIGHT : ColorScheme.NO_PREFERENCE;
      inst.page.emulateMedia(new Page.EmulateMediaOptions().setColorScheme(scheme));
      return RespBodyVo.ok(Kv.by("colorScheme", scheme.name()));
    } catch (PlaywrightException e) {
      return RespBodyVo.fail("set_media 失败：" + briefMessage(e.getMessage()));
    }
  }
```

:::
