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(ENABLE_TESTS)
23 # include "mozilla/StaticPrefs_intl.h"
24 # endif
25 #endif
26
27 using namespace mozilla;
28
29 #if defined(MOZ_SANDBOX)
UseBrokeredLineBreaking()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
NS_GetComplexLineBreaks(const char16_t * aText,uint32_t aLength,uint8_t * aBreakBefore)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(ENABLE_TESTS) && 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 (int i = 0; i < aLength; ++i) {
122 if (aBreakBefore[i] != brokeredBreaks[i]) {
123 mismatch = true;
124 break;
125 }
126 }
127
128 if (mismatch) {
129 // The logging here doesn't handle surrogates, but we only have tests using
130 // Thai currently, which is BMP-only.
131 printf_stderr("uniscribe: ");
132 for (int i = 0; i < aLength; ++i) {
133 if (aBreakBefore[i]) printf_stderr("#");
134 printf_stderr("%s", NS_ConvertUTF16toUTF8(aText + i, 1).get());
135 }
136 printf_stderr("\n");
137 printf_stderr("brokered : ");
138 for (int i = 0; i < aLength; ++i) {
139 if (brokeredBreaks[i]) printf_stderr("#");
140 printf_stderr("%s", NS_ConvertUTF16toUTF8(aText + i, 1).get());
141 }
142 printf_stderr("\n");
143 MOZ_CRASH("Brokered breaks did not match.");
144 }
145 #endif
146 }
147