WebKit 在所有应用中始终启用“复制”菜单项WebKit Always Enables the Copy Menu Item in Every App
当网页获得焦点时,Safari 和 Mail 等应用的主菜单中“复制”选项会被强制启用,即使页面上根本没有选中任何文本。这一 Bug 的根源在于 Apple 平台上的 WebKit 公共 API 处理逻辑。该问题不仅影响了原生应用的用户体验,也暴露了 WebKit 在处理系统级菜单状态同步时的缺陷。第三方应用在使用 WebKit 渲染内容时也普遍会受到这个 UI 逻辑异常的影响。
2026年6月24日
引用 WebKit 官网的介绍,WebKit"是 Safari、Mail、App Store 以及 macOS、iOS 和 Linux 上许多其他应用所使用的网页浏览器引擎。"显然,Safari 使用 WebKit 来显示网页,但你知道吗,Mail 应用也使用 WebKit 来显示邮件?这不仅适用于 HTML 邮件,也适用于纯文本格式的邮件。有几种方法可以揭示 Mail 中 WebKit 的存在,而这些方法可能都源于 bug。如果你在"系统设置"的"辅助功能"面板中关闭"自动播放动画图像",那么当你在 Mail 应用的邮件中或 Safari 的网页中打开上下文菜单时,你会看到"播放所有动画"这一菜单项。在 Mail 中,"播放所有动画"是菜单中的唯一项目,这让我觉得 Apple 的工程师并没有预料到那里会出现上下文菜单。至少在 macOS Sequoia 上会出现这种行为;我还没有在 Tahoe 上测试过 Mail,因为 Liquid Glass 的问题,我在避免升级。
在 Mail 应用中发现 WebKit 的第二种方法,正是这篇博文标题所提到的。几周前,Daring Fireball 的 John Gruber 问我能否复现他在 Safari 中遇到的一个问题:当网页处于焦点状态时,主菜单中的"拷贝"菜单项始终处于启用状态,无论网页中是否有选中的内容。我确实能够复现这个问题,而且原因出在 WebKit。当邮件处于焦点状态时,Mail 应用中也会出现同样的问题。
在 Apple 平台上,WebKit 是一个公开的 API,除了 Apple 自家的应用外,第三方应用同样可以使用。RSS 阅读器(如 NetNewsWire 和 Vienna,分别是 Gruber 和我个人偏好的选择)就使用 WebKit 来显示 RSS 源中的文章。果不其然,这两个应用都表现出同样的问题:当文章处于焦点状态时,"拷贝"菜单项始终处于启用状态。
如果你从没有选中任何内容的 WebKit WebView 中复制并粘贴,会发生什么?什么也不会发生,什么都不会粘贴出来。然而,从技术上讲,剪贴板并不是空的。
上图所示的 Clipboard Viewer 应用包含在 Additional Tools for Xcode 中,可以从 Apple Developer 网站下载。上面列表中的 com.apple.webarchive 和"Apple Web Archive pasteboard type"两项是完全相同的二进制属性列表(以前缀 bplist 标识)。以下是转换为 XML 格式的 plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>WebMainResource</key>
<dict>
<key>WebResourceData</key>
<data>
PCFET0NUWVBFIGh0bWw+
</data>
<key>WebResourceFrameName</key>
<string></string>
<key>WebResourceMIMEType</key>
<string>text/html</string>
<key>WebResourceTextEncodingName</key>
<string>UTF-8</string>
<key>WebResourceURL</key>
<string>https://daringfireball.net/</string>
</dict>
</dict>
</plist>当你在 Safari 中从没有选中任何内容的网页执行复制操作时,你会得到一个包含页面 URL 以及一些(无用的)Base64 格式数据的页面表示:
% echo 'PCFET0NUWVBFIGh0bWw+' | base64 -d <!DOCTYPE html>
遗憾的是,似乎没有其他应用能够识别这种 web archive 剪贴板类型。com.apple.WebKit.custom-pasteboard-data 项中也包含以某种奇怪格式包装的网页 URL,同样似乎没有其他应用能够理解。列表中的其他剪贴板类型实际上都是空的,数据为零字节。因此,当你粘贴时,什么都得不到。
从 Mail 应用中的邮件执行复制操作,会生成类似的剪贴板内容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>WebMainResource</key>
<dict>
<key>WebResourceData</key>
<data>
</data>
<key>WebResourceFrameName</key>
<string></string>
<key>WebResourceMIMEType</key>
<string>text/html</string>
<key>WebResourceTextEncodingName</key>
<string>UTF-8</string>
<key>WebResourceURL</key>
<string>x-webdoc://48BE32AC-8EEF-4630-8C5E-BC292F17A707</string>
</dict>
</dict>
</plist>在这种情况下没有资源数据,而带有 x-webdoc 方案的页面 URL 看起来毫无用处,无法打开:
% open 'x-webdoc://48BE32AC-8EEF-4630-8C5E-BC292F17A707' No application knows how to open URL x-webdoc://48BE32AC-8EEF-4630-8C5E-BC292F17A707 (Error Domain=NSOSStatusErrorDomain Code=-10814 "kLSApplicationNotFoundErr: E.g. no application claims the file" UserInfo={_LSLine=1796, _LSFunction=runEvaluator}).
毫无疑问,这些都不是预期或良好的用户体验。由于我经常提交 WebKit 的 Bug 报告,我决定代表 Gruber 提交一份 Bug 报告:在网页上没有选择任何内容时,主菜单中的“复制”项依然处于启用状态。随后我了解到,这个 Bug 最早于 2025 年 1 月出现在 WebKit 源代码中,2 月出现在 Safari Technology Preview 213 中,3 月出现在 Safari 18.4 中,这是由于试图修复另一个 Bug 导致的,而那个 Bug 是关于 document.execCommand("copy") 仅在有选中文本时才会触发,该问题早在九年前的 2016 年就已报告!
我以前在博客中讨论过 document.execCommand("copy"):网页可以在你不知情的情况下覆盖你的系统剪贴板。在那篇博文中,我描述了网页如何利用看似无害的用户手势来覆盖系统剪贴板。我的浏览器扩展 StopTheMadness Pro 现在可以通过网站选项“Protect clipboard write”来防止这种情况。当然,document.execCommand("copy") API 的设计初衷并非出于恶意,它也有许多显眼且合理的用例,例如 YouTube 分享弹窗中的“复制”按钮。下面是一个演示,点击该按钮会将“This is a test”写入你的剪贴板。
YouTube 的“分享”按钮以及类似控件(例如 Twitter/X 推文上的“复制链接”按钮)理应在网页上没有选中文本时也能正常工作。这就是 2016 年报告并在 2025 年修复的那个 Bug。尽管如此,如果没有选择任何内容,应用程序主菜单中的“复制”项就不应处于启用状态。这就是我最近报告的 Bug。这两种情况截然不同:一个是在网页上,另一个是 Mac 的原生功能;一个由 JavaScript 实现,另一个由 Objective-C 或 Swift 实现。网页和 Mac 应用有着不同的规则和惯例。此外,至关重要的是,Safari 主菜单中的“复制”项并不会调用 document.execCommand("copy"),即使在显示 YouTube 分享弹窗时也是如此。与我报告的 Bug 有关,与 HTML 的“复制”按钮不同,Safari 原生的“复制”菜单项会生成无用的 Web 归档剪贴板内容。
遗憾的是,我的 Bug 报告被关闭了,解决结果为“不予修复”。这种拒绝似乎是基于一种误解:
https://commits.webkit.org/288559@main 的目的是无论选择状态如何,都启用复制。复制功能要么启用,要么不启用。我们不能两者兼得。
这种僵化的回答应该会引起你的“蜘蛛感应”(警觉)!这种说法听起来难道不很像是一个“虚假两难”谬误吗?我们其实是可以做到两者兼顾的。作为一名资深的 Mac 开发者,我非常熟悉应用程序主菜单项的启用和禁用机制。(其他 Mac 开发者可能熟悉我撰写的“working without a nib”系列博文,其中解释了如何通过编程方式构建菜单。)通常,菜单项的状态由 AppKit 的 validateMenuItem: 方法控制。没有什么能阻止 WebKit 以不同方式处理原生的 validateMenuItem: 和 JavaScript 的 document.execCommand("copy"),并针对每种情况使用略有不同的算法。完全没有理由让针对 JavaScript execCommand 的特殊变通处理渗透到 Mac 应用的原生主菜单中。
我重新打开了 bug 报告并据理力争,但考虑到最初的回复,我担心这份报告可能还会继续被敷衍了事。这就是为什么我要写这篇博客来公开这个 bug,希望能引起大家的关注,并借此带来公众舆论的压力。当 Apple 工程师硬把 bug 说成是 feature 时,总是让人感到很无语。
需要完整排版与评论请前往来源站点阅读。