{"id":3311,"date":"2019-12-12T14:55:16","date_gmt":"2019-12-12T12:55:16","guid":{"rendered":"https:\/\/blog.tomayac.com\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/"},"modified":"2019-12-12T14:55:16","modified_gmt":"2019-12-12T12:55:16","slug":"same-same-but-different-unicode-variation-selector-16","status":"publish","type":"post","link":"https:\/\/tomayac.com\/wordpress\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/","title":{"rendered":"Same same but different: Unicode Variation Selector-16"},"content":{"rendered":"\n\t\t\t<p>The other day, I did an <a href=\"https:\/\/blog.tomayac.com\/2019\/12\/09\/inspecting_facebooks_webview\/\">analysis of Facebook's WebView<\/a>,\nwhich you are kindly invited to read.\nThey have a code path in which they check whether a given page is using <a href=\"https:\/\/amp.dev\/\">AMPHTML<\/a>,\nwhere <code>\\u26A1<\/code> is the\n<a href=\"https:\/\/mathiasbynens.be\/notes\/javascript-escapes#unicode-code-point\">Unicode code point escape<\/a>\nof the <a href=\"https:\/\/emojipedia.org\/high-voltage-sign\/\">\u26a1 High Voltage emoji<\/a>.<\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">var<\/span> nvtiming__fb_html_amp <span class=\"token operator\">=<\/span><br>  nvtiming__fb_html<span class=\"token punctuation\">.<\/span><span class=\"token function\">hasAttribute<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">\"amp\"<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">||<\/span><br>  nvtiming__fb_html<span class=\"token punctuation\">.<\/span><span class=\"token function\">hasAttribute<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">\"\\u26A1\"<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br>console<span class=\"token punctuation\">.<\/span><span class=\"token function\">log<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">\"FBNavAmpDetect:\"<\/span> <span class=\"token operator\">+<\/span> nvtiming__fb_html_amp<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<h2 id=\"an-undetected-fake-amp-page\">An undetected fake AMP page <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/#an-undetected-fake-amp-page\">\u2693<\/a><\/h2>\n<p>I was curious to see if they did something special when they detect a page is using AMP\n(spoiler alert: they do not),\nso I quickly hacked together a fake AMP page that <em>seemingly<\/em> fulfilled their simple test.<\/p>\n<pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>html<\/span> <span class=\"token attr-name\">\u26a1\ufe0f<\/span><span class=\"token punctuation\">><\/span><\/span><br>  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>body<\/span><span class=\"token punctuation\">><\/span><\/span>Fake AMP<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>body<\/span><span class=\"token punctuation\">><\/span><\/span><br><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>html<\/span><span class=\"token punctuation\">><\/span><\/span><\/code><\/pre>\n<p>I am a big emoji fan, so instead of the\n<a href=\"https:\/\/amp.dev\/documentation\/guides-and-tutorials\/start\/create\/basic_markup\/#required-mark-up\"><code>&lt;html amp&gt;<\/code><\/a>\nvariant, I went for the <code>&lt;html \u26a1&gt;<\/code> variant and entered the <code>\u26a1<\/code> via the macOS emoji picker.\nTo my surprise, Facebook logged <code>&quot;FBNavAmpDetect: false&quot;<\/code>. Huh \ud83e\udd37\u200d\u2642\ufe0f?<\/p>\n<h2 id=\"%E2%9A%A1%EF%B8%8F-high-voltage-sign-is-a-valid-attribute-name\">\u26a1\ufe0f High Voltage sign is a valid attribute name <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/#%E2%9A%A1%EF%B8%8F-high-voltage-sign-is-a-valid-attribute-name\">\u2693<\/a><\/h2>\n<p>My first reaction was: <code>&lt;html \u26a1\ufe0f&gt;<\/code> does not quite look like what the founders of HTML had in mind,\nso maybe <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Element\/hasAttribute\"><code>hasAttribute()<\/code><\/a>\nis <a href=\"https:\/\/dom.spec.whatwg.org\/#dom-element-hasattribute\">specified<\/a> to return <code>false<\/code>\nwhen an attribute name is invalid.\nBut what even is a valid attribute name?\nI consulted the <a href=\"https:\/\/html.spec.whatwg.org\/multipage\/syntax.html#attributes-2\">HTML spec<\/a>\nwhere it says (emphasis mine):<\/p>\n<blockquote>\n<p>Attribute names must consist of one or more characters\nother than controls, U+0020 SPACE, U+0022 (&quot;), U+0027 ('), U+003E (&gt;), U+002F (\/), U+003D (=),\nand noncharacters. <strong>In the HTML syntax, attribute names, even those for foreign elements,\nmay be written with any mix of ASCII lower and ASCII upper alphas.<\/strong><\/p>\n<\/blockquote>\n<p>I was on company chat with <a href=\"https:\/\/twitter.com\/jaffathecake\">Jake Archibald<\/a> at that moment,\nso I confirmed my reading of the spec that <code>\u26a1<\/code> is not a valid attribute name.\nTurns out, it is a valid name, but the spec is formulated in an ambiguous way, so Jake filed\n<a href=\"https:\/\/github.com\/whatwg\/html\/issues\/5144\">&quot;HTML syntax&quot; attribute names<\/a>.\nAnd my lead to a rational explanation was gone.<\/p>\n<h2 id=\"perfect-heisenbug%3F\">Perfect Heisenbug? <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/#perfect-heisenbug%3F\">\u2693<\/a><\/h2>\n<p>Luckily a valid <a href=\"https:\/\/amp.dev\/boilerplate\/\">AMP boilerplate example<\/a>\nwas just a quick Web search away, so I copy-pasted the code and Facebook, as expected,\nreported <code>&quot;FBNavAmpDetect: true&quot;<\/code>.\nI reduced the AMP boilerplate example until it looked like my fake AMP page,\nbut still Facebook detected the modified boilerplate as AMP, but did not detect mine as AMP.\nEssentially my experiment looked like the below code sample.\nPerfect Heisenbug?<\/p>\n<p><img src=\"https:\/\/blog.tomayac.com\/images\/heisenbolt.png\" alt=\"JavaScript console showing the code sample from this post\"><\/p>\n<h2 id=\"the-unicode-variation-selector-16\">The Unicode Variation Selector-16 <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/#the-unicode-variation-selector-16\">\u2693<\/a><\/h2>\n<p>Jake eventually traced it down to the Unicode\n<a href=\"https:\/\/emojipedia.org\/variation-selector-16\/\">Variation Selector-16<\/a>:<\/p>\n<blockquote>\n<p>An invisible code point which specifies that the preceding character should be displayed\nwith emoji presentation. Only required if the preceding character defaults to text presentation.<\/p>\n<\/blockquote>\n<p>You may have seen this in effect with the Unicode snowman that appears in a textual \u2603\ufe0e\nas well as in an emoji representation \u2603\ufe0f (depending on the device you read this on,\nthey may both look the same).\nAs far as I can tell, Chrome DevTools prefers to always render the textual variant,\nas you can see in the screenshot above.\nBut with the help of the\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/String\/length\"><code>length()<\/code><\/a>\nand the\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/String\/charCodeAt\"><code>charCodeAt()<\/code><\/a>\nfunctions, the difference gets visible.<\/p>\n<pre class=\"language-js\"><code class=\"language-js\">document<span class=\"token punctuation\">.<\/span><span class=\"token function\">querySelector<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'html'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">hasAttribute<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'\u26a1'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token comment\">\/\/ false<\/span><br>document<span class=\"token punctuation\">.<\/span><span class=\"token function\">querySelector<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'html'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">hasAttribute<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'\u26a1\ufe0f'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token comment\">\/\/ true<\/span><br><span class=\"token string\">'\u26a1\ufe0f'<\/span><span class=\"token punctuation\">.<\/span>length<span class=\"token punctuation\">;<\/span><br><span class=\"token comment\">\/\/ 2<\/span><br><span class=\"token string\">'\u26a1'<\/span><span class=\"token punctuation\">.<\/span>length<span class=\"token punctuation\">;<\/span><br><span class=\"token comment\">\/\/ 1<\/span><br><span class=\"token string\">'\u26a1'<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">charCodeAt<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">+<\/span> <span class=\"token string\">' '<\/span> <span class=\"token operator\">+<\/span> <span class=\"token string\">'\u26a1'<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">charCodeAt<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">1<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token comment\">\/\/ \"9889 NaN\"<\/span><br><span class=\"token string\">'\u26a1\ufe0f'<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">charCodeAt<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">+<\/span> <span class=\"token string\">' '<\/span> <span class=\"token operator\">+<\/span> <span class=\"token string\">'\u26a1\ufe0f'<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">charCodeAt<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">1<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token comment\">\/\/ \"9889 65039\"<\/span><\/code><\/pre>\n<h2 id=\"the-amp-validator-and-%E2%9A%A1%EF%B8%8F\">The AMP Validator and \u26a1\ufe0f <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/#the-amp-validator-and-%E2%9A%A1%EF%B8%8F\">\u2693<\/a><\/h2>\n<p>The macOS emoji picker creates the variant \u26a1\ufe0f, which includes the Variation Selector-16,\nbut AMP requires the variant without, which I have also confirmed in the\n<a href=\"https:\/\/github.com\/ampproject\/amphtml\/blob\/a561d0e8be10c8996d9f3db6920f69ffffafd5d8\/validator\/engine\/validator.js#L5366-L5395\">validator code<\/a>.\nYou can see in the screenshot below how the <a href=\"https:\/\/validator.ampproject.org\/\">AMP Validator<\/a>\nrejects one of the two High Voltage symbols.<\/p>\n<p><img src=\"https:\/\/blog.tomayac.com\/images\/amp-validator.png\" alt=\"AMP Validator rejecting the emoji variant with Variation Selector-16\"><\/p>\n<h2 id=\"making-this-actionable\">Making this actionable <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/#making-this-actionable\">\u2693<\/a><\/h2>\n<p>I have filed <a href=\"https:\/\/crbug.com\/1033453\">crbug.com\/1033453<\/a> against the Chrome DevTools\nasking for rendering the characters differently, depending on whether the Variation Selector-16\nis present or not.\nFurther, I have opened a feature request on the AMP Project repo demanding that\n<a href=\"https:\/\/github.com\/ampproject\/amphtml\/issues\/25990\">AMP should respect \u26a1\ufe0f apart from \u26a1<\/a>.\nSame same, but different.<\/p>\n\n\t\t\t<p>\n\t\t\t\t<img alt=\"Thomas Steiner\" width=\"32\" height=\"32\" src=\"https:\/\/blog.tomayac.com\/feed.php?dl=https%3A%2F%2Fblog.tomayac.com%2F2019%2F12%2F12%2Fsame-same-but-different-unicode-variation-selector-16%2F&dp=%2F2019%2F12%2F12%2Fsame-same-but-different-unicode-variation-selector-16%2F&dt=Same%20same%20but%20different%3A%20Unicode%20Variation%20Selector-16\" alt=\"\">\n\t\t\t\t<br\/>This post appeared first on <a href=\"https:\/\/blog.tomayac.com\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/\">https:\/\/blog.tomayac.com\/2019\/12\/12\/same-same-but-different-unicode-variation-selector-16\/<\/a>.\n\t\t\t<\/p>\n\t\t","protected":false},"excerpt":{"rendered":"<p>\t\t\tThe other day, I did an analysis of Facebook&#8217;s WebView,<br \/>\nwhich you are kindly invited to read.<br \/>\nThey have a code path in which they check whether a given page is using AMPHTML,<br \/>\nwhere \\u26A1 is the<br \/>\nUnicode code point escape<br \/>\nof the \u26a1 High Voltage emoji&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"intlwemo_wallet_address":"","footnotes":""},"categories":[],"tags":[],"class_list":["post-3311","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts\/3311","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/comments?post=3311"}],"version-history":[{"count":1,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts\/3311\/revisions"}],"predecessor-version":[{"id":3312,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts\/3311\/revisions\/3312"}],"wp:attachment":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/media?parent=3311"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/categories?post=3311"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/tags?post=3311"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}