lmk123 / blog

个人技术博客,博文写在 Issues 里。
https://github.com/lmk123/blog/issues
623 stars 35 forks source link

无痕(隐身)模式对扩展程序的影响 #90

Open lmk123 opened 3 years ago

lmk123 commented 3 years ago

当我们开发扩展程序的时候,一般不会意识到扩展程序在无痕模式下的表现,因为基本上不会遇到什么问题。但是,我在开发划词翻译的时候,却因为无痕模式遇到了两次 bug,最终促使我好好调查一下无痕模式到底会对扩展程序产生什么影响。

在这之前,我先简单描述一下背景。我为了让划词翻译能在 PDF 文件里使用,于是将 PDF.js 的 viewer(即https://mozilla.github.io/pdf.js/web/viewer.html)打包进了划词翻译当中。安装划词翻译之后,在地址栏内粘贴 chrome-extension://ikhdkkncnoglghljlkmcimlnlhkeamad/pdf-viewer/web/viewer.html 就能看到这个 PDF 阅读器。

我第一个遇到的无痕模式的问题就跟这个 PDF 阅读器有关。2016 年的时候,有用户反馈在无痕模式(当时的称呼是“隐私模式”)里打不开 PDF 阅读器(见 https://github.com/lmk123/crx-selection-translate/issues/164),在查阅了 Chrome 扩展程序的文档之后,我很快找到了解决方案:给 manifest.json 加一个新的属性 "incognito": "split"

Chrome 扩展程序开发文档里对这个属性的说明文档地址是 https://developer.chrome.com/docs/extensions/mv2/manifest/incognito/。简单点说,扩展程序在无痕模式下有两种运行模式:

转眼时间过去了五年,我遇到了第二个跟无痕模式有关的问题。最近有一个用户反馈说,只要在无痕模式里使用划词翻译,就会导致划词翻译的账号掉线(见 https://github.com/lmk123/crx-selection-translate/issues/969)。

整个扩展程序里,跟无痕模式有关的配置就只有 manifest.json 里的 "incognito": "split"。在重新阅读了这个配置项的说明后,我明白了出现这个问题的过程:

明白了问题的过程,解决方案也就有了:我们只需要把用户的认证凭证从 cookie 里转到浏览器里的 chrome.storage 里就可以了——但是实施起来却遇到了问题。

用户在反馈这个问题的时候,Chrome 的版本是 88;当我实施解决方案的时候,Chrome 的版本是 90,而这个版本对扩展程序在无痕模式下的表现却发生了一些变化,而且,我不确定这是 feature 还是 bug(但我猜应该是 bug)。

这个变化就是: split 模式下,无痕模式里的扩展程序没有主机权限了。

发现这个变化的原因是,在无痕模式下划词翻译内置的 PDF 阅读器不能加载网上的 PDF 文件了,会报 CORS 错误——这是不应该出现的错误,因为划词翻译是有 <all_urls> 权限的。我尝试在无痕模式下的内容脚本、PDF 阅读器、背景页这三个地方执行 fetch('https://www.baidu.com'),发现只有内容脚本能正常发起请求,而其它两个地方都会报 CORS 错误。

我查找了 Chrome 90 的变更日志(见 https://chromereleases.googleblog.com/2021/04/stable-channel-update-for-desktop_20.html),但是并没有找到相关的说明,所以我猜测这应该是个 bug。

我又在之前为了测试兼容性问题而安装的 Chromium 65 里试了下,这时的表现就跟文档上描述一致:在无痕模式打开 PDF 阅读器时会直接显示“被拦截”。

但是,因为 Chrome 90 的这个变化,我需要重新考虑解决方案了。

无论 Chrome 90 的这个变化是不是一个 bug,但肯定会有用户在 Chrome 90 上使用划词翻译,而由于 CORS 的问题,用户压根就无法在无痕模式里正常使用划词翻译。

我还在 stack overflow 搜到一个相关的回答,意思是 split 模式对扩展程序而言不常用,而且总是有 bug;我还想起来,Firefox 是不支持 split 模式的,它在无痕模式下的运行方式只能是 spanning

基于以上原因,我决定将划词翻译在无痕模式下的运行模式从 split 改回默认的 spanning,这需要对划词翻译做以下的变动:

当我将 "incognito": "split" 改为 "incognito": "spanning" 之后,我发现即使是在无痕模式下调用的 chrome.tabs.create() 方法,它也会改为在普通模式的窗口中打开,而不是像 "incognito": "split" 时会直接在无痕窗口中打开,这样,以上改造中的第一条其实就“自动”完成了。

对于第二条,其实几乎没有用户会通过直接在地址栏输入地址的方式打开内置 PDF 阅读器,所以这个情况可以忽略不计;但是,划词翻译为了添加“自动打开 PDF”功能,会使用 chrome,webRequest 将在线 pdf 的网址转到内置 PDF 阅读器,此时就需要判断在线 PDF 的网址是否是在无痕窗口加载的了,如果是的话就需要用 chrome.tabs.create() 方法在普通窗口下打开内置 PDF 阅读器,因为无痕模式下打开内置 PDF 阅读器会显示一个错误页面。

对于第三条,最后我没有在设置页加这个提示,而是当内置 PDF 加载的是来自无痕窗口的在线 PDF 时才会给出一个提示。因为,毕竟只有在无痕模式使用内置 PDF 时才需要提示用户知道,否则其他情况跟在普通窗口里使用其实没有区别。