/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MainThreadUtils.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Preferences.h" #include "nsIDNService.h" #include "nsReadableUtils.h" #include "nsCRT.h" #include "nsServiceManagerUtils.h" #include "nsString.h" #include "nsStringFwd.h" #include "nsUnicharUtils.h" #include "nsUnicodeProperties.h" #include "harfbuzz/hb.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Casting.h" #include "mozilla/StaticPrefs_network.h" #include "mozilla/TextUtils.h" #include "mozilla/Utf8.h" #include "mozilla/intl/UnicodeProperties.h" #include "mozilla/intl/UnicodeScriptCodes.h" #include "nsNetUtil.h" #include "nsStandardURL.h" using namespace mozilla; using namespace mozilla::intl; using namespace mozilla::unicode; using namespace mozilla::net; using mozilla::Preferences; //----------------------------------------------------------------------------- #define ISDIGIT(c) ((c) >= '0' && (c) <= '9') template static inline bool TLDEqualsLiteral(mozilla::Span aTLD, const char (&aStr)[N]) { if (aTLD.Length() != N - 1) { return false; } const char* a = aStr; for (const char32_t c : aTLD) { if (c != char32_t(*a)) { return false; } ++a; } return true; } template static inline bool TLDStartsWith(mozilla::Span aTLD, const char (&aStr)[N]) { // Ensure the span is long enough to contain the prefix if (aTLD.Length() < N - 1) { return false; } for (size_t i = 0; i < N - 1; ++i) { if (aTLD[i] != char32_t(aStr[i])) { return false; } } return true; } static inline bool isOnlySafeChars(mozilla::Span aLabel, const nsTArray& aBlocklist) { if (aBlocklist.IsEmpty()) { return true; } for (const char32_t c : aLabel) { if (c > 0xFFFF) { // The blocklist only support BMP! continue; } if (CharInBlocklist(char16_t(c), aBlocklist)) { return false; } } return true; } static bool isCyrillicDomain(mozilla::Span& aTLD) { return TLDEqualsLiteral(aTLD, "bg") || TLDEqualsLiteral(aTLD, "by") || TLDEqualsLiteral(aTLD, "kz") || TLDEqualsLiteral(aTLD, "pyc") || TLDEqualsLiteral(aTLD, "ru") || TLDEqualsLiteral(aTLD, "su") || TLDEqualsLiteral(aTLD, "ua") || TLDEqualsLiteral(aTLD, "uz"); } //----------------------------------------------------------------------------- // nsIDNService //----------------------------------------------------------------------------- /* Implementation file */ NS_IMPL_ISUPPORTS(nsIDNService, nsIIDNService) nsresult nsIDNService::Init() { MOZ_ASSERT(NS_IsMainThread()); InitializeBlocklist(mIDNBlocklist); InitCJKSlashConfusables(); InitCJKIdeographs(); InitDigitConfusables(); InitCyrillicLatinConfusables(); InitThaiLatinConfusables(); return NS_OK; } void nsIDNService::InitCJKSlashConfusables() { mCJKSlashConfusables.Insert(0x30CE); // ノ mCJKSlashConfusables.Insert(0x30BD); // ソ mCJKSlashConfusables.Insert(0x30BE); // ゾ mCJKSlashConfusables.Insert(0x30F3); // ン mCJKSlashConfusables.Insert(0x4E36); // 丶 mCJKSlashConfusables.Insert(0x4E40); // 乀 mCJKSlashConfusables.Insert(0x4E41); // 乁 mCJKSlashConfusables.Insert(0x4E3F); // 丿 } void nsIDNService::InitCJKIdeographs() { mCJKIdeographs.Insert(0x4E00); // 一 mCJKIdeographs.Insert(0x3127); // ㄧ mCJKIdeographs.Insert(0x4E28); // 丨 mCJKIdeographs.Insert(0x4E5B); // 乛 mCJKIdeographs.Insert(0x4E03); // 七 mCJKIdeographs.Insert(0x4E05); // 丅 mCJKIdeographs.Insert(0x5341); // 十 mCJKIdeographs.Insert(0x3007); // 〇 mCJKIdeographs.Insert(0x3112); // ㄒ mCJKIdeographs.Insert(0x311A); // ㄚ mCJKIdeographs.Insert(0x311F); // ㄟ mCJKIdeographs.Insert(0x3128); // ㄨ mCJKIdeographs.Insert(0x3129); // ㄩ mCJKIdeographs.Insert(0x3108); // ㄈ mCJKIdeographs.Insert(0x31BA); // ㆺ mCJKIdeographs.Insert(0x31B3); // ㆳ mCJKIdeographs.Insert(0x5DE5); // 工 mCJKIdeographs.Insert(0x31B2); // ㆲ mCJKIdeographs.Insert(0x8BA0); // 讠 mCJKIdeographs.Insert(0x4E01); // 丁 } void nsIDNService::InitDigitConfusables() { mDigitConfusables.Insert(0x03B8); // θ mDigitConfusables.Insert(0x0968); // २ mDigitConfusables.Insert(0x09E8); // ২ mDigitConfusables.Insert(0x0A68); // ੨ mDigitConfusables.Insert(0x0AE8); // ૨ mDigitConfusables.Insert(0x0CE9); // ೩ mDigitConfusables.Insert(0x0577); // շ mDigitConfusables.Insert(0x0437); // з mDigitConfusables.Insert(0x0499); // ҙ mDigitConfusables.Insert(0x04E1); // ӡ mDigitConfusables.Insert(0x0909); // उ mDigitConfusables.Insert(0x0993); // ও mDigitConfusables.Insert(0x0A24); // ਤ mDigitConfusables.Insert(0x0A69); // ੩ mDigitConfusables.Insert(0x0AE9); // ૩ mDigitConfusables.Insert(0x0C69); // ౩ mDigitConfusables.Insert(0x1012); // ဒ mDigitConfusables.Insert(0x10D5); // ვ mDigitConfusables.Insert(0x10DE); // პ mDigitConfusables.Insert(0x0A5C); // ੜ mDigitConfusables.Insert(0x10D9); // კ mDigitConfusables.Insert(0x0A6B); // ੫ mDigitConfusables.Insert(0x4E29); // 丩 mDigitConfusables.Insert(0x3110); // ㄐ mDigitConfusables.Insert(0x0573); // ճ mDigitConfusables.Insert(0x09EA); // ৪ mDigitConfusables.Insert(0x0A6A); // ੪ mDigitConfusables.Insert(0x0B6B); // ୫ mDigitConfusables.Insert(0x0AED); // ૭ mDigitConfusables.Insert(0x0B68); // ୨ mDigitConfusables.Insert(0x0C68); // ౨ } void nsIDNService::InitCyrillicLatinConfusables() { mCyrillicLatinConfusables.Insert(0x0430); // а CYRILLIC SMALL LETTER A mCyrillicLatinConfusables.Insert(0x044B); // ы CYRILLIC SMALL LETTER YERU mCyrillicLatinConfusables.Insert(0x0441); // с CYRILLIC SMALL LETTER ES mCyrillicLatinConfusables.Insert(0x0501); // ԁ CYRILLIC SMALL LETTER KOMI DE mCyrillicLatinConfusables.Insert(0x0435); // е CYRILLIC SMALL LETTER IE mCyrillicLatinConfusables.Insert(0x050D); // ԍ CYRILLIC SMALL LETTER KOMI SJE mCyrillicLatinConfusables.Insert(0x04BB); // һ CYRILLIC SMALL LETTER SHHA mCyrillicLatinConfusables.Insert( 0x0456); // і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I {Old // Cyrillic i} mCyrillicLatinConfusables.Insert(0x044E); // ю CYRILLIC SMALL LETTER YU mCyrillicLatinConfusables.Insert(0x043A); // к CYRILLIC SMALL LETTER KA mCyrillicLatinConfusables.Insert(0x0458); // ј CYRILLIC SMALL LETTER JE mCyrillicLatinConfusables.Insert(0x04CF); // ӏ CYRILLIC SMALL LETTER PALOCHKA mCyrillicLatinConfusables.Insert(0x043C); // м CYRILLIC SMALL LETTER EM mCyrillicLatinConfusables.Insert(0x043E); // о CYRILLIC SMALL LETTER O mCyrillicLatinConfusables.Insert(0x0440); // р CYRILLIC SMALL LETTER ER mCyrillicLatinConfusables.Insert( 0x0517); // ԗ CYRILLIC SMALL LETTER RHA {voiceless r} mCyrillicLatinConfusables.Insert(0x051B); // ԛ CYRILLIC SMALL LETTER QA mCyrillicLatinConfusables.Insert(0x0455); // ѕ CYRILLIC SMALL LETTER DZE mCyrillicLatinConfusables.Insert(0x051D); // ԝ CYRILLIC SMALL LETTER WE mCyrillicLatinConfusables.Insert(0x0445); // х CYRILLIC SMALL LETTER HA mCyrillicLatinConfusables.Insert(0x0443); // у CYRILLIC SMALL LETTER U mCyrillicLatinConfusables.Insert( 0x044A); // ъ CYRILLIC SMALL LETTER HARD SIGN mCyrillicLatinConfusables.Insert( 0x044C); // ь CYRILLIC SMALL LETTER SOFT SIGN mCyrillicLatinConfusables.Insert( 0x04BD); // ҽ CYRILLIC SMALL LETTER ABKHASIAN CHE mCyrillicLatinConfusables.Insert(0x043F); // п CYRILLIC SMALL LETTER PE mCyrillicLatinConfusables.Insert(0x0433); // г CYRILLIC SMALL LETTER GHE mCyrillicLatinConfusables.Insert(0x0475); // ѵ CYRILLIC SMALL LETTER IZHITSA mCyrillicLatinConfusables.Insert(0x0461); // ѡ CYRILLIC SMALL LETTER OMEGA } void nsIDNService::InitThaiLatinConfusables() { // Some of the Thai characters are only confusable on Linux. #if defined(XP_LINUX) && !defined(ANDROID) mThaiLatinConfusables.Insert(0x0E14); // ด mThaiLatinConfusables.Insert(0x0E17); // ท mThaiLatinConfusables.Insert(0x0E19); // น mThaiLatinConfusables.Insert(0x0E1B); // ป mThaiLatinConfusables.Insert(0x0E21); // ม mThaiLatinConfusables.Insert(0x0E25); // ล mThaiLatinConfusables.Insert(0x0E2B); // ห #endif mThaiLatinConfusables.Insert(0x0E1A); // บ mThaiLatinConfusables.Insert(0x0E1E); // พ mThaiLatinConfusables.Insert(0x0E1F); // ฟ mThaiLatinConfusables.Insert(0x0E23); // ร mThaiLatinConfusables.Insert(0x0E40); // เ mThaiLatinConfusables.Insert(0x0E41); // แ mThaiLatinConfusables.Insert(0x0E50); // ๐ } nsIDNService::nsIDNService() { MOZ_ASSERT(NS_IsMainThread()); } nsIDNService::~nsIDNService() = default; NS_IMETHODIMP nsIDNService::DomainToASCII(const nsACString& input, nsACString& ace) { return NS_DomainToASCII(input, ace); } NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString& input, nsACString& ace) { return NS_DomainToASCIIAllowAnyGlyphfulASCII(input, ace); } NS_IMETHODIMP nsIDNService::ConvertACEtoUTF8(const nsACString& input, nsACString& _retval) { return NS_DomainToUnicodeAllowAnyGlyphfulASCII(input, _retval); } NS_IMETHODIMP nsIDNService::DomainToDisplay(const nsACString& input, nsACString& _retval) { nsresult rv = NS_DomainToDisplay(input, _retval); return rv; } NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString& input, nsACString& _retval) { nsresult rv = NS_DomainToDisplayAllowAnyGlyphfulASCII(input, _retval); return rv; } //----------------------------------------------------------------------------- namespace mozilla::net { enum ScriptCombo : int32_t { UNSET = -1, BOPO = 0, CYRL = 1, GREK = 2, HANG = 3, HANI = 4, HIRA = 5, KATA = 6, LATN = 7, OTHR = 8, JPAN = 9, // Latin + Han + Hiragana + Katakana CHNA = 10, // Latin + Han + Bopomofo KORE = 11, // Latin + Han + Hangul HNLT = 12, // Latin + Han (could be any of the above combinations) FAIL = 13, }; // Ignore - set if the label contains a character that makes it // obvious it's not a lookalike. // Safe - set if the label contains no lookalike characters. // Block - set if the label contains lookalike characters. enum class LookalikeStatus { Ignore, Safe, Block }; class MOZ_STACK_CLASS LookalikeStatusChecker { public: // Constructor for Script Confusable Checkers (Cyrillic, Thai, etc) LookalikeStatusChecker(nsTHashSet& aConfusables, mozilla::Span& aTLD, Script aTLDScript, bool aValidTLD) : mConfusables(aConfusables), mStatus(aValidTLD ? LookalikeStatus::Ignore : LookalikeStatus::Safe), mTLDMatchesScript(doesTLDScriptMatch(aTLD, aTLDScript)), mTLDScript(aTLDScript) {} // Constructor that DigitLookalikeStatusChecker inherits explicit LookalikeStatusChecker(nsTHashSet& aConfusables) : mConfusables(aConfusables), mStatus(LookalikeStatus::Safe) {} // For the Script Confusable Checkers virtual void CheckCharacter(char32_t aChar, Script aScript) { if (mStatus != LookalikeStatus::Ignore && !mTLDMatchesScript && aScript == mTLDScript) { mStatus = mConfusables.Contains(aChar) ? LookalikeStatus::Block : LookalikeStatus::Ignore; } } virtual LookalikeStatus Status() { return mStatus; } protected: // A hash set containing confusable characters nsTHashSet& mConfusables; // The current lookalike status LookalikeStatus mStatus; bool doesTLDScriptMatch(mozilla::Span& aTLD, Script aScript) { mozilla::Span::const_iterator current = aTLD.cbegin(); mozilla::Span::const_iterator end = aTLD.cend(); while (current != end) { char32_t ch = *current++; if (UnicodeProperties::GetScriptCode(ch) == aScript) { return true; } } return false; } private: // Indicates whether the TLD matches the given script bool mTLDMatchesScript{false}; // The script associated with the TLD to be matched Script mTLDScript{Script::INVALID}; }; // Overrides the CheckCharacter method to validate digits class DigitLookalikeStatusChecker : public LookalikeStatusChecker { public: explicit DigitLookalikeStatusChecker(nsTHashSet& aConfusables) : LookalikeStatusChecker(aConfusables) {} // Note: aScript is not used in this override. void CheckCharacter(char32_t aChar, Script aScript) override { if (mStatus == LookalikeStatus::Ignore) { return; } // If the character is not a numeric digit, check whether it is confusable // or not. if (!ISDIGIT(aChar)) { mStatus = mConfusables.Contains(aChar) ? LookalikeStatus::Block : LookalikeStatus::Ignore; } } }; } // namespace mozilla::net bool nsIDNService::IsLabelSafe(mozilla::Span aLabel, mozilla::Span aTLD) { if (StaticPrefs::network_IDN_show_punycode()) { return false; } if (!isOnlySafeChars(aLabel, mIDNBlocklist)) { return false; } // Bug 1917119 - Avoid bypassing the doesTLDScriptMatch check // aTLD should be a decoded label, but in the case of invalid labels such as // `xn--xn--d--fg4n` we might end up with something that starts with `xn--`. // Treat those as unsafe just in case. if (TLDStartsWith(aTLD, "xn--")) { return false; } mozilla::Span::const_iterator current = aLabel.cbegin(); mozilla::Span::const_iterator end = aLabel.cend(); Script lastScript = Script::INVALID; char32_t previousChar = 0; char32_t baseChar = 0; // last non-diacritic seen (base char for marks) char32_t savedNumberingSystem = 0; // Ignore digit confusables if there is a non-digit and non-digit confusable // character. If aLabel only consists of digits and digit confusables or // digit confusables, return false. DigitLookalikeStatusChecker digitStatusChecker(mDigitConfusables); // Check if all the cyrillic letters in the label are confusables LookalikeStatusChecker cyrillicStatusChecker(mCyrillicLatinConfusables, aTLD, Script::CYRILLIC, isCyrillicDomain(aTLD)); // Check if all the Thai letters in the label are confusables LookalikeStatusChecker thaiStatusChecker( mThaiLatinConfusables, aTLD, Script::THAI, TLDEqualsLiteral(aTLD, "th")); // Simplified/Traditional Chinese check temporarily disabled -- bug 857481 #if 0 HanVariantType savedHanVariant = HVT_NotHan; #endif ScriptCombo savedScript = ScriptCombo::UNSET; while (current != end) { char32_t ch = *current++; IdentifierType idType = GetIdentifierType(ch); if (idType == IDTYPE_RESTRICTED) { return false; } MOZ_ASSERT(idType == IDTYPE_ALLOWED); // Check for mixed script Script script = UnicodeProperties::GetScriptCode(ch); if (script != Script::COMMON && script != Script::INHERITED && script != lastScript) { if (illegalScriptCombo(script, savedScript)) { return false; } } #ifdef XP_MACOSX // U+0620, U+0f8c, U+0f8d, U+0f8e, U+0f8f and are blocked due to a font // issue on macOS if (ch == 0x620 || ch == 0xf8c || ch == 0xf8d || ch == 0xf8e || ch == 0xf8f) { return false; } #endif // U+30FC should be preceded by a Hiragana/Katakana. if (ch == 0x30fc && lastScript != Script::HIRAGANA && lastScript != Script::KATAKANA) { return false; } Script nextScript = Script::INVALID; if (current != end) { nextScript = UnicodeProperties::GetScriptCode(*current); } // U+3078 to U+307A (へ, べ, ぺ) in Hiragana mixed with Katakana should be // unsafe if (ch >= 0x3078 && ch <= 0x307A && (lastScript == Script::KATAKANA || nextScript == Script::KATAKANA)) { return false; } // U+30D8 to U+30DA (ヘ, ベ, ペ) in Katakana mixed with Hiragana should be // unsafe if (ch >= 0x30D8 && ch <= 0x30DA && (lastScript == Script::HIRAGANA || nextScript == Script::HIRAGANA)) { return false; } // U+30FD and U+30FE are allowed only after Katakana if ((ch == 0x30FD || ch == 0x30FE) && lastScript != Script::KATAKANA) { return false; } // Slash confusables not enclosed by {Han,Hiragana,Katakana} should be // unsafe but by itself should be allowed. if (isCJKSlashConfusable(ch) && aLabel.Length() > 1 && lastScript != Script::HAN && lastScript != Script::HIRAGANA && lastScript != Script::KATAKANA && nextScript != Script::HAN && nextScript != Script::HIRAGANA && nextScript != Script::KATAKANA) { return false; } if (ch == 0x30FB && (lastScript == Script::LATIN || nextScript == Script::LATIN)) { return false; } // Combining Diacritic marks (U+0300-U+0339) after a script other than // Latin-Greek-Cyrillic is unsafe if (ch >= 0x300 && ch <= 0x339 && lastScript != Script::LATIN && lastScript != Script::GREEK && lastScript != Script::CYRILLIC) { return false; } if (ch == 0x307 && (previousChar == 'i' || previousChar == 'j' || previousChar == 'l')) { return false; } // U+00B7 is only allowed on Catalan domains between two l's. if (ch == 0xB7 && (!TLDEqualsLiteral(aTLD, "cat") || previousChar != 'l' || current == end || *current != 'l')) { return false; } // Disallow Icelandic confusables for domains outside Icelandic and Faroese // ccTLD (.is, .fo) if ((ch == 0xFE || ch == 0xF0) && !TLDEqualsLiteral(aTLD, "is") && !TLDEqualsLiteral(aTLD, "fo")) { return false; } // Disallow U+0259 for domains outside Azerbaijani ccTLD (.az) if (ch == 0x259 && !TLDEqualsLiteral(aTLD, "az")) { return false; } // Block single/double-quote-like characters. if (ch == 0x2BB || ch == 0x2BC) { return false; } // Update the status based on whether the current character is a confusable // or not and determine if it should be blocked or ignored. // Note: script is not used for digitStatusChecker digitStatusChecker.CheckCharacter(ch, script); cyrillicStatusChecker.CheckCharacter(ch, script); thaiStatusChecker.CheckCharacter(ch, script); // Block these CJK ideographs if they are adjacent to non-CJK characters. // These characters can be used to spoof Latin characters/punctuation marks. if (isCJKIdeograph(ch)) { // Check if there is a non-Bopomofo, non-Hiragana, non-Katakana, non-Han, // and non-Numeric character on the left. previousChar is 0 when ch is the // first character. if (lastScript != Script::BOPOMOFO && lastScript != Script::HIRAGANA && lastScript != Script::KATAKANA && lastScript != Script::HAN && previousChar && !ISDIGIT(previousChar)) { return false; } // Check if there is a non-Bopomofo, non-Hiragana, non-Katakana, non-Han, // and non-Numeric character on the right. if (nextScript != Script::BOPOMOFO && nextScript != Script::HIRAGANA && nextScript != Script::KATAKANA && nextScript != Script::HAN && current != aLabel.end() && !ISDIGIT(*current)) { return false; } } // Check for mixed numbering systems auto genCat = GetGeneralCategory(ch); if (genCat == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) { uint32_t zeroCharacter = ch - mozilla::intl::UnicodeProperties::GetNumericValue(ch); if (savedNumberingSystem == 0) { // If we encounter a decimal number, save the zero character from that // numbering system. savedNumberingSystem = zeroCharacter; } else if (zeroCharacter != savedNumberingSystem) { return false; } } if (genCat == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { // Check for consecutive non-spacing marks. if (previousChar != 0 && previousChar == ch) { return false; } // Check for marks whose expected script doesn't match the base script. if (lastScript != Script::INVALID) { UnicodeProperties::ScriptExtensionVector scripts; auto extResult = UnicodeProperties::GetExtensions(ch, scripts); MOZ_ASSERT(extResult.isOk()); if (extResult.isErr()) { return false; } int nScripts = AssertedCast(scripts.length()); // nScripts will always be >= 1, because even for undefined characters // it will return Script::INVALID. // If the mark just has script=COMMON or INHERITED, we can't check any // more carefully, but if it has specific scriptExtension codes, then // assume those are the only valid scripts to use it with. if (nScripts > 1 || (Script(scripts[0]) != Script::COMMON && Script(scripts[0]) != Script::INHERITED)) { while (--nScripts >= 0) { if (Script(scripts[nScripts]) == lastScript) { break; } } if (nScripts == -1) { return false; } } } // Check for diacritics on dotless-i, which would be indistinguishable // from normal accented letter i. if (baseChar == 0x0131 && ((ch >= 0x0300 && ch <= 0x0314) || ch == 0x031a)) { return false; } } else { baseChar = ch; } if (script != Script::COMMON && script != Script::INHERITED) { lastScript = script; } // Simplified/Traditional Chinese check temporarily disabled -- bug 857481 #if 0 // Check for both simplified-only and traditional-only Chinese characters HanVariantType hanVariant = GetHanVariant(ch); if (hanVariant == HVT_SimplifiedOnly || hanVariant == HVT_TraditionalOnly) { if (savedHanVariant == HVT_NotHan) { savedHanVariant = hanVariant; } else if (hanVariant != savedHanVariant) { return false; } } #endif previousChar = ch; } return digitStatusChecker.Status() != LookalikeStatus::Block && (!StaticPrefs::network_idn_punycode_cyrillic_confusables() || cyrillicStatusChecker.Status() != LookalikeStatus::Block) && thaiStatusChecker.Status() != LookalikeStatus::Block; } // Scripts that we care about in illegalScriptCombo static inline ScriptCombo findScriptIndex(Script aScript) { switch (aScript) { case Script::BOPOMOFO: return ScriptCombo::BOPO; case Script::CYRILLIC: return ScriptCombo::CYRL; case Script::GREEK: return ScriptCombo::GREK; case Script::HANGUL: return ScriptCombo::HANG; case Script::HAN: return ScriptCombo::HANI; case Script::HIRAGANA: return ScriptCombo::HIRA; case Script::KATAKANA: return ScriptCombo::KATA; case Script::LATIN: return ScriptCombo::LATN; default: return ScriptCombo::OTHR; } } static const ScriptCombo scriptComboTable[13][9] = { /* thisScript: BOPO CYRL GREK HANG HANI HIRA KATA LATN OTHR * savedScript */ /* BOPO */ {BOPO, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL}, /* CYRL */ {FAIL, CYRL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL}, /* GREK */ {FAIL, FAIL, GREK, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL}, /* HANG */ {FAIL, FAIL, FAIL, HANG, KORE, FAIL, FAIL, KORE, FAIL}, /* HANI */ {CHNA, FAIL, FAIL, KORE, HANI, JPAN, JPAN, HNLT, FAIL}, /* HIRA */ {FAIL, FAIL, FAIL, FAIL, JPAN, HIRA, JPAN, JPAN, FAIL}, /* KATA */ {FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, KATA, JPAN, FAIL}, /* LATN */ {CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, LATN, OTHR}, /* OTHR */ {FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, OTHR, FAIL}, /* JPAN */ {FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, JPAN, JPAN, FAIL}, /* CHNA */ {CHNA, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL}, /* KORE */ {FAIL, FAIL, FAIL, KORE, KORE, FAIL, FAIL, KORE, FAIL}, /* HNLT */ {CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, HNLT, FAIL}}; bool nsIDNService::illegalScriptCombo(Script script, ScriptCombo& savedScript) { if (savedScript == ScriptCombo::UNSET) { savedScript = findScriptIndex(script); return false; } savedScript = scriptComboTable[savedScript][findScriptIndex(script)]; return savedScript == OTHR || savedScript == FAIL; } extern "C" MOZ_EXPORT bool mozilla_net_is_label_safe(const char32_t* aLabel, size_t aLabelLen, const char32_t* aTld, size_t aTldLen) { return static_cast(nsStandardURL::GetIDNService()) ->IsLabelSafe(mozilla::Span(aLabel, aLabelLen), mozilla::Span(aTld, aTldLen)); } bool nsIDNService::isCJKSlashConfusable(char32_t aChar) { return mCJKSlashConfusables.Contains(aChar); } bool nsIDNService::isCJKIdeograph(char32_t aChar) { return mCJKIdeographs.Contains(aChar); }