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