一、起点:第 23 个标签
2025 年 11 月 17 日下午三点四十,我坐在公司二楼靠窗的备用机上,屏幕右上角还摊着半份没吃完的饭团。那天我打开的笔记里已经开了 22 个标签,第 23 个是一个内部文档链接,鼠标点下去的那一刻,整扇窗口连同滚动条一起挪到了副屏。
不是新窗口。是旧窗口被搬走了,地址栏还在,焦点却丢了。我愣了两秒,把窗口拖回来,再点一次,它又跳过去,第三次、第四次同样如此。副屏像是被设成了一个磁铁。我打开开发者工具,选中那个链接,看到 target="_blank" 被某段脚本改写成了带窗口特征参数的调用。
一个纯粹的浏览器行为问题,能把人拖进一整个下午。我桌上那杯美式凉了两次,我还是没关掉那个页面。
二、LinkGuard 2.4.1 与那段被改写的 click
顺着 Sources 面板往下翻,我找到了第一个嫌疑对象:一个叫 LinkGuard 的扩展,版本 2.4.1。它在页面加载后注入一个很小的脚本,监听所有 click 事件。我加了一个断点,发现每次点击,调用栈里都会多出一层来自扩展的内容脚本。
这段脚本的逻辑很直接:判断链接是否带下划线属性,如果带,就自己接管,再用带窗口位置参数的 window.open 打开。注释里只有半行英文,写着窗口位置参数沿用前一次会话。原来如此,那个被点亮又没被拖走的副屏,就是“前一次会话”。
我把 LinkGuard 停掉,重试,窗口终于不再搬走了。但故事到这儿没完,因为停掉它之后,下一个问题立刻浮出来。
三、老周的 41 行补丁
11 月 18 日上午我把情况发给了老周。他在公司干了六年,写过一版内部工具,专门管这类链接行为。他回得很快,扔过来一个文本片段,共 41 行,开头注释写着“2023 年 4 月”。他说,拿去试试,别再让窗口乱跑。
那 41 行里有一段我印象很深:它把新窗口的尺寸和位置固定成一个常量,再用 window.open 打开。问题是,常量是写死的,宽度 1024、高度 768,放在今天的屏幕上只会贴到左上角。老周自己承认,这补丁是从上一个项目抄来的。
那段补丁能治标,但会把所有新窗口都缩成 1024×768。老周说完笑了一下,说他也一直没改。
我没直接用它,而是把窗口位置参数改成从 screen.availLeft 和 availTop 读取。改完之后,窗口至少不往副屏跳了。
四、策略文件里的那一行
18 日傍晚六点二十二,我翻到设置页面第六屏,“链接在何处打开”这一项显示被策略管理。顺着提示去看策略目录,在 /etc/opt/chrome/policies/managed 里找到一个 JSON 文件,里面有一条关于新窗口打开方式的键值,被设成了“强制在最近使用的窗口”。
这一行其实解释了副屏的谜团:所谓最近使用的窗口,是系统层面记住的那一次。晚上十点十分,运维小许远程连上来,敲了三行 ls,指着其中一个文件说,就是它,然后备份、修改、重启浏览器。
五、我翻到的那篇 2019 年论文
排查间隙我查了文献,找到 2019 年一篇讨论 target="_blank" 行为的论文,作者在第 118 到 121 页列了七种情况。第四种正好对应我们的环境:扩展接管点击、策略覆盖窗口位置、脚本再改写打开参数。三个因素叠加,才有那种窗口搬家的效果。
论文的脚注里还提到一个对照实验:同一份 HTML,在 Firefox 上只弹一次,在 Safari 上干脆不弹。这和我在 11 月 19 日上午自己做的对照结果一致。问题从来不在页面本身,而在内核和扩展的策略层。
把外部资料和自己的复现对上之后,我才敢在周报里写结论。个人习惯是,没有第二份独立来源,就只写现象,不写判断。
六、一次把 rel="noopener" 全加上的翻车
中间我试过最省事的办法:给所有外链补上 rel 属性。批量替换了 12 个文件之后,登录回跳链路断了,因为那个页面依赖 opener 传递状态。git revert 了两次才恢复。
这次翻车让我改了一个看法:很多人把 rel="noopener" 当成万能药,我不认同。安全收益是真的,但它不是无副作用的。对依赖窗口关系的老系统,粗暴地全量替换只会制造新问题,先分段验证,才是负责的做法。
七、顺手记下的下载与安装字样
排查过程中我还注意到几个下载入口的链接行为不一样。有的是同窗口跳转,有的强制新开。为了方便以后核对,我把几条相关的站内记录归在一起,比如 安卓版49图库下载 这条,以及 四九图库资料下载扩展 对应的页面,它们的链接写法明显不同,正好可以拿来当对照样本。
另外两条我当时也顺手标了记号:一条是关于 49图库资料库恢复成功率提升 的记录,另一条是 49图库下载与安装 的说明页。这些不是结论,只是索引,以后遇到同类跳转行为时可以回头比对。
八、收尾
19 日晚上十点四十,我关掉开发者工具。窗口乖乖待在原屏幕,副屏黑着。楼下便利店的灯还亮着,我下去买了一瓶热豆浆,回来写周报,把三天的过程压成五行字。
这篇长文是从那五行里长出来的。写到这里有点累,像走完一段不短的夜路,但值得。