# display_override 清单成员

> display_override 按优先级列出显示模式，排在 display 回退链之前，让 PWA 在保留安全回退的同时请求 window-controls-overlay 或 tabbed 模式。

`display_override` 是一个有序的显示模式字符串数组，浏览器会先遍历它，再去看 `display`。
单值的 `display` 被锁定在固定回退链 `fullscreen` → `standalone` → `minimal-ui` → `browser`
上，无法表达 `window-controls-overlay` 或 `tabbed`；这两种模式要在 `display_override` 里请求，
`display` 则留作跳过该成员的引擎的回退值。

Chrome 89、Edge 89 与 Samsung Internet 15.0 会处理该成员（BCD
`html.manifest.display_override`）。Firefox 157 与 Safari 27 忽略它，只按 `display` 处理；
Android WebView 完全不读取它。

## 成员

- **类型**：字符串数组。每个字符串是一种显示模式：`fullscreen`、`standalone`、`minimal-ui`、
  `browser`、`window-controls-overlay` 或 `tabbed`。Chromium 还识别 `borderless`，但它只对
  ChromeOS 上的 Isolated Web App 生效。
- **默认值**：空数组。没有 `display_override` 时，浏览器使用 `display`。
- **示例值**：`["window-controls-overlay", "minimal-ui"]`。

处理过程遵循 Manifest Incubations 的算法（wicg.github.io）：不是显示模式的字符串先被丢弃，
然后浏览器取列表中第一个自己支持的模式。只有列表里没有任何受支持的模式时，才回退到 `display`
及其自身的回退链。由此有两个推论：

- 未知条目被跳过，而不是导致失败。不认识 `window-controls-overlay` 的浏览器会直接看下一个条目。
- 覆盖列表没有隐式回退链。`["fullscreen"]` 不会像 `display: "fullscreen"` 那样落到
  `standalone`；只有在浏览器退回 `display` 之后，回退链才重新生效。

可安装性判定同样把该成员算在内。当 `display` 为 `browser` 时，只要 `display_override` 中第一个受
支持的条目是应用式模式，Chromium 的可安装性检查就会放行；否则 DevTools 会报告
`Manifest contains 'display_override' field, and the first supported display mode must be one of 'standalone', 'fullscreen', or 'minimal-ui'`
（`components/webapps/browser/installable/installable_logging.cc`）。

:::observed
Chrome 155（macOS 26，英文界面）的 DevTools > Application > Manifest 中有 **Window Controls
Overlay** 一节：清单含 `"display_override": ["window-controls-overlay"]` 时，面板显示
`Chrome has successfully found the window-controls-overlay value for the display_override field in the manifest.`，
并提供一个标为 **Emulate the Window Controls Overlay on** 的复选框，附带 Windows / macOS / Linux
选择器。没有该值时，同一节显示
`Define window-controls-overlay in the manifest to use the Window Controls Overlay API and customize your app's title bar.`。
:::

## 示例

下面三份清单共用同一个 `display: "standalone"` 回退值，区别只在首选项。

### 请求标题栏覆盖层并以 standalone 回退

一个自绘工具栏的桌面应用先请求 `window-controls-overlay`，再显式写上 `minimal-ui` 作为第二选择。
`display` 保持 `standalone`，这样 Firefox、Safari 以及任何拒绝覆盖层的 Chromium 版本仍会以应用
窗口启动。

```json
{
  "name": "Ledger",
  "start_url": "/app/",
  "scope": "/app/",
  "display": "standalone",
  "display_override": ["window-controls-overlay", "minimal-ui"]
}
```

在 Windows 与 macOS 上，网页内容会延伸进标题栏区域，关闭、最小化、最大化按钮绘制在内容之上，
因此页面必须给这块区域留位。

### 围绕覆盖层布局并在运行时检测

`titlebar-area-*` 环境变量描述覆盖层留出的矩形区域；没有覆盖层时它们解析为 `0`，所以同一份 CSS
放在普通 standalone 窗口里也安全。下面的脚本检查 `navigator.windowControlsOverlay` 与
`display-mode` 媒体特性（MDN 的 `@media (display-mode)`），不满足时退回普通页头。

```css
.toolbar {
  position: fixed;
  left: env(titlebar-area-x, 0);
  top: env(titlebar-area-y, 0);
  width: env(titlebar-area-width, 100%);
  height: env(titlebar-area-height, 48px);
  -webkit-app-region: drag;
}
```

```js
const overlay = navigator.windowControlsOverlay;
const inOverlay =
  overlay?.visible === true ||
  matchMedia('(display-mode: window-controls-overlay)').matches;

document.documentElement.dataset.titlebar = inOverlay ? 'overlay' : 'standard';

overlay?.addEventListener('geometrychange', (event) => {
  // 用户可以从标题栏关闭覆盖层，需要重新布局。
  document.documentElement.dataset.titlebar = event.visible ? 'overlay' : 'standard';
});
```

在用户通过 Chrome 首次启动时显示的标题栏开关接受覆盖层之前，`overlay.visible` 一直是 `false`，
所以真正保证布局正确的是 `geometrychange` 监听器，而不是初始那次检查。

### 尝试 tabbed 而不丢掉旧版 Chromium

想要窗口内标签栏的已安装应用把 `tabbed` 放在首位。Chrome 126、Edge 126 与 Samsung Internet 28.0
会采用它；Chrome 125 及更早版本跳过该条目，使用下一个。

```json
{
  "display": "standalone",
  "display_override": ["tabbed", "standalone"],
  "tab_strip": {
    "home_tab": { "scope_patterns": [{ "pathname": "/" }] },
    "new_tab_button": { "url": "/new" }
  }
}
```

末尾的 `"standalone"` 与 `display` 重复，写在这里只是为了表明意图；删掉它不会改变任何行为，
因为回退到 `display` 是自动的。

## 另请参阅

- [display 清单成员](/zh/reference/manifest/display/)
- [tabbed 显示模式与 tab_strip 清单成员](/zh/reference/manifest/tabbed-display/)
- [可安装性标准：什么让 PWA 可安装](/zh/reference/installation/installability-criteria/)
- [桌面端 PWA 安装：Chrome、Edge 及其他](/zh/reference/installation/desktop-install/)
- [Manifest Incubations: display_override member](https://wicg.github.io/manifest-incubations/#display_override-member)（wicg.github.io）
- [Window Controls Overlay API](https://developer.mozilla.org/en-US/docs/Web/API/Window_Controls_Overlay_API)（developer.mozilla.org）
- [Customize the window controls overlay of your PWA's title bar](https://web.dev/articles/window-controls-overlay)（web.dev）