Chardeng是一种新的小二进制占用空间的字符编码检测器,适用于用Rust编写的Firefox。它的目的是通过解决Chrome已经解决的一个古老的--对于非拉丁文字页面--破坏网页的Web Comat问题来留住用户。它结束了一些页面在Firefox中显示为无法阅读的垃圾的情况,但在Chrome中打开页面比摆弄Firefox菜单更容易补救这种情况。在Firefox 73中部署Chardeng标志着Firefox历史上(以及自引入非ISO-8859-1文本支持以来的整个Netscape浏览器谱系中)第一次HTML字节到DOM的映射不是浏览器用户界面语言的函数。与采用Chrome的检测器相比,这是以明显较低的二进制大小增量实现的。此外,与Chrome的检测器相比,结果更易于解释和修改,也更适合标准化,如果人们感兴趣的话。
图表的目标是遗留编码中未标记的遗留页面的长尾。Web开发人员应该继续对新创作的页面使用UTF-8,并相应地标记HTML。这并不是Web开发人员要使用的新特性!
虽然chardeng最初是在Firefox73中登陆的,但这篇文章讨论的是Firefox78中的chardeng。
有一长条遗留网页没有标记其编码。在历史上,浏览器对未标记的页面使用与浏览器的用户界面语言相关联的默认设置,并为用户提供菜单以选择不同的内容。
为了摆脱字符编码菜单,Chrome采用了(显然)为谷歌搜索和Gmail开发的编码检测器。这是在没有提前讨论标准制定组织中的更改的情况下完成的。Firefox在避免基于内容的猜测方面走了另一个方向,所以这就产生了一个Web Comat问题,当它发生在非拉丁文字页面时,就像Web Comat问题一样糟糕:编码-未标记的传统页面在Firefox中可能完全不可读(除非采取了隐藏良好的菜单操作),但在Chrome中却可以正常显示。最近来自日本的反馈以及世界范围的遥测表明,这个问题在实践中实际上仍然存在。虽然Safari检测得较少,但如果用户在iOS上遇到这个问题,就没有其他引擎可以求助,所以Safari不能用来确定在基于Chromium的浏览器只需点击几下或轻击几下的平台上,Firefox的废弃风险。Edge转而使用Chromium标志着将Web平台带向检测更少的方向的希望破灭了。
ICU4C的探测器不够准确(或完整)。火狐旧的、大部分已经被移除的探测器还不够完整,完成它需要做的工作与编写一个全新的探测器所需的工作量相同。因为Chrome的检测器Ced不是为浏览器使用而开发的,所以它占用的空间比必要的要大。它(在实践中)也是不可修改的跨墙开放源码,因此采用它将意味着采用一堆已知的C++--不必要的臃肿,同时也很难清理。
与开发字符编码转换库时所做的观察相比,开发编码检测器更容易,所需的工作量也更少。Chardeng的基本二进制缩小思想是利用传统的中文、日文和韩文(CJK)解码器(浏览器无论如何都必须具备这些解码器)来检测CJK遗留编码。图表也严格限制在与Web相关的用例中。
在x86_64上,chardeng(以及较早的日本专用检测器)对libxul的二进制大小贡献是ce对libxul贡献的28%。如果我们采用了ce,并且后来想要进行类似的二进制大小节省,那么在代码库周围寻找节省的工作将比从头开始编写图表要多得多。
对二进制大小的关注使得图表处理相同数据的时间比ce多42%。但是,这种权衡对于为旧式页面运行而对于现代页面根本不运行的代码是有意义的。chardeng的精确度与ced具有竞争力,与ced集成到Chrome相比,chardeng集成到Firefox中的方式使其有更好的机会给出正确的答案。
如果WHATWG有兴趣对图表进行标准化,那么该图表的开发方式将是可标准化的。数据表在CC0下,图表的非CC0部分由不到3000行可解释的Rust代码组成,这些代码可以反转成规范英语。
在介绍chardering的工作原理之前,让我们更详细地看看它存在的原因。
早在创建Web的时候,操作系统就有特定于地区的字符编码。例如,本地化为希腊语的系统与本地化为日语的系统具有不同的字符编码。Web是在瑞士创建的,因此假定按照ISO-8859-1解释字节,ISO-8859-1是Unix-ish系统的西欧编码,也与Windows的西欧编码兼容。(Mac OS Classic上的单字节编码与Unix和Windows非常不同,以至于Mac上的浏览器不得不置换字节,无法从Web获取内容来匹配系统编码。)。
随着浏览器适应更多的语言,字节的默认处理遵循系统级的设计,即字节的文本解释是特定于地区的。尽管实际指示字符编码内容是什么的概念出现得相对较快,但损害已经造成了。已经有了未标记的页面,因此-为了兼容性-浏览器保留了特定于区域设置的后备缺省值。这使得作者可以创建更多未加标签的内容,这些内容在使用本地浏览器查看时似乎工作得很好,但在使用非本地浏览器查看时就会中断。对于一个名字中有“世界范围”的系统来说,这并不是很好。
长期以来,浏览器提供的菜单允许用户在页面作者的浏览器的备用字符编码与页面阅读器的浏览器的备用字符编码不匹配时覆盖字符编码。由于Windows和Unix之间存在编码分离,浏览器传统上也为日语区域设置提供了一个检测器,用于在Shift_JIS和EUC-JP(也可能是ISO-2022-JP)之间做出决定。(中欧在windows和unix之间也有这样的编码分裂,但是,由于没有检测器,网络开发人员需要齐心协力,将编码声明为…)。Firefox还为俄罗斯和乌克兰地区(但不是其他西里尔文地区)提供了默认开启检测器!用于在多个西里尔编码中进行检测。后来,Firefox还试图通过在顶级域附属于地区时决定从顶级域而不是UI本地化来缓解这个问题。然而,这并没有解决.com/.net/.org、本地文件或具有多个遗留编码的区域设置的问题,即使没有达到日本的流行程度。
为了摆脱手动覆盖HTML页面字符编码的UI,Chrome采用了COMPACT_ENC_DET(Ce),它似乎是来自Google搜索和Gmail的Google字符编码检测器。这是在没有提前讨论标准制定组织的变化的情况下做出的,这让我们感到惊讶。这导致了这样一种情况:Firefox的顶级域名或UI区域启发式检测可能会失败,但Chrome的基于内容的检测可能会成功。推出基于Chromium的浏览器可能更容易被发现,而不是通过导航隐藏得相当好的子菜单来补救Firefox中的这种情况。
当这个问题出现在非拉丁文页面时,它几乎和Web Comat问题一样糟糕:文本在Firefox中完全不可读,在Chrome中工作得很好。
因此,我们遇到了一个比JavaScript更古老、几乎和Web本身一样古老的Web Comat问题,它的故障模式非常糟糕(文本完全不可读)。但是,它是否真的发生得足够频繁,值得我们去关心呢?如果你在美洲、西欧、澳大利亚、新西兰、撒哈拉以南的非洲许多地区等等,你可能会想:“这真的发生在Firefox73之前吗?这在我浏览网页的时候并没有发生。“。
实际上,对于在WINDOWS-1252区域设置中阅读来自WINDOWS-1252区域设置的内容的用户来说,这个问题已经有很长一段时间不是一个实际问题了。在WINDOWS-1252区域设置中,“WINDOWS-1252区域设置”被定义为该区域设置的Windows本地化的传统“ANSI”代码页是WINDOWS-1252的区域设置。出现这种情况的原因有两个。首先,Web是在这个区域设置队列中创建的(WINDOWS-1252是ISO-8859-1的超集),所以默认设置总是有利的。其次,当问题确实发生时,对于这些地区的语言来说,其影响是相对可以容忍的。这些区域设置使用具有相对较少的非ASCII字符的拉丁文。即使将这些非ASCII字符替换为垃圾,仍然很容易弄清楚整个文本在说什么。
对于非拉丁脚本,问题要严重得多,因为几乎所有字符(除了ASCII空格和ASCII标点符号)都被垃圾替换。
显然,当用户调用Firefox中的Text Encoding菜单时,此用户操作表明用户遇到了此问题。在世界各地,菜单的使用可以说是非常罕见的。然而,到底有多罕见,有一个相当大的因素。如果我们把德国和西班牙的使用率作为菜单使用率的基线,这两个国家是大型的非英语窗口-1252个地区,我们可以认为菜单使用率不值得解决,澳门的使用率是这个的85倍(不是直接访问菜单,而是菜单至少被使用一次的Firefox子会话;即在一个子会话中连续使用的次数只有一次)。可忽略次数85不一定等于需要采取行动,但这表明从Windows-1252经验对问题严重程度的评估不太可能具有代表性。
此外,菜单使用的普遍稀缺性并不是故事的全部。它只衡量用户知道如何在Firefox中解决问题并不厌其烦地解决问题的情况。它不能衡量用户的放弃程度:它不能衡量用户通过打开基于Chromium的浏览器来解决问题的程度。对于每个找到菜单选项并在Firefox中使用它的用户,其他几个用户可能会放弃Firefox。
从日本不断传来的反馈说,问题仍然时有发生。遥测表明,它发生在主要书写系统为繁体中文的地区,发生率甚至高于日本。在使用简体中文的中国大陆,这一比例略低于日本,但相当于日本的水平。我的假设是,尽管繁体中文在Web平台中只有一种传统编码,简体中文在Web平台中也有一种传统编码(出于解码目的),这将根据基于区域的猜测预测这些区域设置是成功的,但用户在泛型域上跨繁体/简化拆分阅读内容(顶级域的猜测不适用),但不会将区域设置外的失败视为可报告的。在日语和日语的情况下,故障是在现场发生的,显然被视为更具报告性。
一般而言,正如人们所期望的那样,在主要书写系统不是拉丁文的地区中菜单使用率高于主要书写系统为拉丁文的地区中的菜单使用率。值得注意的是,保加利亚在榜单上名列前茅。默认情况下,我们为俄罗斯和乌克兰本地化启用了之前的西里尔文检测器(可能没有接受保加利亚人的培训),但没有启用保加利亚本地化。此外,我们缺乏.cy的TLD映射,而且,果然,塞浦路斯的菜单使用率高于希腊。
同样,不出所料,在非windows-1252拉丁文区域设置中的菜单使用率高于在windows-1252拉丁文区域设置中的菜单使用率。值得注意的是,与其他拉丁语地区相比,阿塞拜疆的使用率高得异乎寻常。我怀疑我们有错误的.az TLD映射,因为在将TLD映射引入Firefox时,我们没有阿塞拜疆本地化可以学习。
事实上,在进行TLD映射的那一刻,我怀疑我们的.az的TLD映射是错误的,但我没有数据来证明这一点,所以我犯了一个错误,即不采取行动。现在,几年过去了,我们有了数据。在我看来,相信日本的反馈并解决这个问题比收集更多的数据来证明Web Compat问题值得解决要好得多。
那么,为什么不早点或者最晚在Chrome推出他们现在的探测器时解决这个问题呢?一般来说,在Web平台上避免不明显的行为似乎是一个更好的主意,如果这些行为是可以避免的,而且当时微软走的是相反的方向,而Chrome在基于EdgeHTML的Edge中既没有菜单也没有检测器。这使得Web平台在这一点上似乎有可能最终变得更简单,而不是更复杂。
尽管微软决定将新Edge基于Chromium的决定考虑到这一特殊问题是愚蠢的,但交换机删除了EdgeHTML作为数据点,暗示事情可能没有Gecko中已经存在的那么复杂。在这一点上,假装如果我们不开始检测更多,Chrome和新的Edge就会开始检测更少,这是没有意义的。
Safari的检测能力比Firefox之前更低。据我所知,Safari没有基于TLD的猜测,回退直接来自UI语言,详细说明如果UI语言是日语,则在日语遗留编码之间存在基于内容的猜测。然而,由于Safari/WebKit是针对iOS市场条件而设计的,我们不能将此作为一个迹象,表明相对于Chromium,这种猜测水平对于壁虎来说就足够了。如果用户在iOS上遇到这个问题,他们就没有其他引擎可以选择,而且这个问题非常罕见,用户不会因为这个问题而整体放弃iOS。然而,在Gecko运行的每个平台上,只需点击几下或轻点几下,就可以使用基于Chromium的浏览器。
在决定解决这个问题之后,就有了如何解决它的问题。为什么要编写新代码,而不是重用现有代码?
ICU4C有一个探测器,但Chrome已经以不够准确为由拒绝了它。事实上,在我自己使用标题长度输入进行的测试中,ICU4C的精确度比下面讨论的替代方案要差得多。
此时你可能在想“等等,火狐不是有一个‘通用’探测器吗?难道不是你把它移除的吗?”是的,是的,是的。
Firefox过去有两个检测器:“通用”检测器,也被称为chardet,和一个单独的西里尔文检测器。Chardet有以下可能的配置:日语、繁体中文、简体中文、中文、韩语和通用,其中最后一个模式启用了所有检测功能,而其他模式仅启用了子集。
仅此列表就表明了一个问题:如果您看看目前的Web平台,繁体中文只有一种遗留编码,简体中文只有一种遗留解码模式(GBK和GB18030的解码方式相同,在编码方面也不同),而韩语只有一种遗留编码。该检测器是在壁虎将字符编码视为精灵并试图将其全部捕获的时候编写的。从那时起,我们了解到EUC-TW(繁体中文编码)、ISO-2022-CN(繁体中文和简体中文编码)、HZ(简体中文编码)、ISO-2022-KR(韩语编码)和Johab(韩语编码)从未在Web上流行起来,我们能够将它们从平台上移除。由于每个繁体中文、简体中文和韩文都有一个真正的遗留编码,其他唯一可检测到的编码是UTF-8,但是检测到它是有问题的(稍后将对此进行详细介绍)。
当我在2013年初为Chardet的非日语部分提交删除错误时,对于日语本地化,Chardet默认启用了仅日语模式,对于俄语和乌克兰本地化,Chardet启用了单独的西里尔文检测器,而对于繁体中文本地化,Chardet在通用模式下启用了Chardet。当时,传统中文本地化的字符编码默认值也不同于以另一种方式设置的字符编码:它使用UTF-8而不是Big5作为后备编码,这就是为什么我也没有非常认真地对待检测器设置的原因。根据后来的遥测,在繁体中文和简体中文之间检测到的组合中文模式对于繁体中文和简体中文本地化在默认情况下都是有用的。
时不时地,会有为每个人默认打开“通用”探测器的请求。这是行不通的,因为“通用”探测器实际上不是通用的!它是不完整的,有已知的缺陷,但没有更仔细地检查过它的人认为这个名字是表面上的。也就是说,在提交探测器试纸十多年后(2001年9月12日),探测器仍然不完整,默认关闭,除非在日语子集模式下,并且在看起来明显配置错误的情况下关闭,并且名称令人混淆。
情况本来就很糟糕。实际上有两个选择:拆除或实际投资修复。当时,Chrome还没有全力以赴进行检测。考虑到其他浏览器正在做什么,并且不希望使Web平台比看起来需要的更复杂,我极力主张移除。我仍然认为,考虑到当时的情况和现有的信息,这个电话是有意义的。
至于在2019年复活旧检测器是否有意义,从版本控制历史中挖掘代码仍然会导致检测器不完整,一般情况下不适合默认打开。同时,一个非Mozilla分支添加了一些东西,另一项工作导致了一个铁锈端口。除了必须整理许可(在MPL 2.0升级之前派生的代码,Rust端口只保留许可选项中的LGPL3部分,Mozilla认真遵守LPGL重新链接条款,使得在Gecko中使用LGPLed代码比使用其他开源代码更不实用),即使是派生也需要完成更多的工作,尽管至少C++派生比原始的Mozilla代码更完整。
此外,尽管验证旧的CJK编码是旧检测器的基本思想之一(在本文中称为“编码方案方法”),但是旧的检测器没有使用浏览器的解码器来进行这些编码,而是自己实现了验证。如果您想要一个没有依赖项的C++库,这是很好的,但是如果您考虑的是浏览器中的二进制大小成本,而该浏览器已经有了这些编码的验证解码器,并且在Android上发布了二进制大小仍然很重要的浏览器,那么这就不是很好了。
检测器有两大方面:如何处理单字节编码和如何处理遗留CJK。对于前者,如果您需要开发工具来添加对。
..