nsUniscribeBreaker.cpp (4831B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "nsComplexBreaker.h" 7 8 #include <windows.h> 9 10 #include <usp10.h> 11 12 #include "nsUTF8Utils.h" 13 #include "nsString.h" 14 #include "nsTArray.h" 15 16 #if defined(MOZ_SANDBOX) 17 # include "mozilla/WindowsProcessMitigations.h" 18 # include "mozilla/SandboxSettings.h" 19 # include "mozilla/sandboxTarget.h" 20 # include "nsXULAppAPI.h" 21 22 # if defined(MOZ_DEBUG) 23 # include "mozilla/StaticPrefs_intl.h" 24 # endif 25 #endif 26 27 using namespace mozilla; 28 29 #if defined(MOZ_SANDBOX) 30 static bool UseBrokeredLineBreaking() { 31 // If win32k lockdown is enabled we can't use Uniscribe in this process. Also 32 // if the sandbox is above a certain level we can't load the required DLLs 33 // without other intervention. Given that it looks like we are likely to have 34 // win32k lockdown enabled first, using the brokered call for people testing 35 // this case also makes most sense. 36 static bool sUseBrokeredLineBreaking = 37 IsWin32kLockedDown() || 38 (XRE_IsContentProcess() && GetEffectiveContentSandboxLevel() >= 20); 39 40 return sUseBrokeredLineBreaking; 41 } 42 #endif 43 44 void NS_GetComplexLineBreaks(const char16_t* aText, uint32_t aLength, 45 uint8_t* aBreakBefore) { 46 NS_ASSERTION(aText, "aText shouldn't be null"); 47 48 #if defined(MOZ_SANDBOX) 49 if (UseBrokeredLineBreaking()) { 50 // We can't use Uniscribe, so use a brokered call. Use of Uniscribe will be 51 // replaced in bug 1684927. 52 char16ptr_t text = aText; 53 if (!SandboxTarget::Instance()->GetComplexLineBreaks(text, aLength, 54 aBreakBefore)) { 55 NS_WARNING("Brokered line break failed, breaks might be incorrect."); 56 } 57 58 return; 59 } 60 #endif 61 62 int outItems = 0; 63 HRESULT result; 64 AutoTArray<SCRIPT_ITEM, 64> items; 65 char16ptr_t text = aText; 66 67 memset(aBreakBefore, false, aLength); 68 69 items.AppendElements(64); 70 71 do { 72 result = ScriptItemize(text, aLength, items.Length(), nullptr, nullptr, 73 items.Elements(), &outItems); 74 75 if (result == E_OUTOFMEMORY) { 76 // XXX(Bug 1631371) Check if this should use a fallible operation as it 77 // pretended earlier. 78 items.AppendElements(items.Length()); 79 } 80 } while (result == E_OUTOFMEMORY); 81 82 for (int iItem = 0; iItem < outItems; ++iItem) { 83 uint32_t endOffset = 84 (iItem + 1 == outItems ? aLength : items[iItem + 1].iCharPos); 85 uint32_t startOffset = items[iItem].iCharPos; 86 AutoTArray<SCRIPT_LOGATTR, 64> sla; 87 88 // XXX(Bug 1631371) Check if this should use a fallible operation as it 89 // pretended earlier. 90 sla.AppendElements(endOffset - startOffset); 91 92 if (ScriptBreak(text + startOffset, endOffset - startOffset, 93 &items[iItem].a, sla.Elements()) < 0) 94 return; 95 96 // We don't want to set a potential break position at the start of text; 97 // that's the responsibility of a higher level. 98 for (uint32_t j = startOffset ? 0 : 1; j + startOffset < endOffset; ++j) { 99 aBreakBefore[j + startOffset] = sla[j].fSoftBreak; 100 } 101 } 102 103 #if defined(MOZ_DEBUG) && defined(MOZ_SANDBOX) 104 // When tests are enabled and pref is set, we compare the line breaks returned 105 // from the Uniscribe breaker in the content process, with the ones returned 106 // from the brokered call to the parent. If they differ we crash so we can 107 // test using a crashtest. 108 if (!StaticPrefs::intl_compare_against_brokered_complex_line_breaks() || 109 !XRE_IsContentProcess()) { 110 return; 111 } 112 113 nsTArray<uint8_t> brokeredBreaks(aLength); 114 brokeredBreaks.AppendElements(aLength); 115 if (!SandboxTarget::Instance()->GetComplexLineBreaks( 116 text, aLength, brokeredBreaks.Elements())) { 117 MOZ_CRASH("Brokered GetComplexLineBreaks failed."); 118 } 119 120 bool mismatch = false; 121 for (uint32_t i = 0; i < aLength; ++i) { 122 if (aBreakBefore[i] != brokeredBreaks[i]) { 123 mismatch = true; 124 break; 125 } 126 } 127 128 if (mismatch) { 129 nsCString line("uniscribe: "); 130 // The logging here doesn't handle surrogates, but we only have tests using 131 // Thai currently, which is BMP-only. 132 for (uint32_t i = 0; i < aLength; ++i) { 133 if (aBreakBefore[i]) line.Append('#'); 134 line.Append(NS_ConvertUTF16toUTF8(aText + i, 1).get()); 135 } 136 printf_stderr("%s\n", line.get()); 137 line.Assign("brokered : "); 138 for (uint32_t i = 0; i < aLength; ++i) { 139 if (brokeredBreaks[i]) line.Append('#'); 140 line.Append(NS_ConvertUTF16toUTF8(aText + i, 1).get()); 141 } 142 printf_stderr("%s\n", line.get()); 143 MOZ_CRASH("Brokered breaks did not match."); 144 } 145 #endif 146 }