Bug 1636126 [wpt PR 23460] - Improve test: csp/reporting/post-redirect-stacktrace...
[gecko.git] / gfx / 2d / SFNTNameTable.cpp
blob2645b0d5c822ffdf053d070b550f613c84556fa6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SFNTNameTable.h"
9 #include <utility>
11 #include "BigEndianInts.h"
12 #include "Logging.h"
14 #if defined(XP_MACOSX)
15 # include <CoreFoundation/CoreFoundation.h>
16 #endif
18 namespace mozilla {
19 namespace gfx {
21 static const BigEndianUint16 FORMAT_0 = 0;
23 static const BigEndianUint16 NAME_ID_FAMILY = 1;
24 static const BigEndianUint16 NAME_ID_STYLE = 2;
25 static const BigEndianUint16 NAME_ID_FULL = 4;
27 static const BigEndianUint16 PLATFORM_ID_UNICODE = 0;
28 static const BigEndianUint16 PLATFORM_ID_MAC = 1;
29 static const BigEndianUint16 PLATFORM_ID_MICROSOFT = 3;
31 static const BigEndianUint16 ENCODING_ID_MICROSOFT_SYMBOL = 0;
32 static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEBMP = 1;
33 static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEFULL = 10;
35 static const BigEndianUint16 ENCODING_ID_MAC_ROMAN = 0;
37 static const BigEndianUint16 LANG_ID_MAC_ENGLISH = 0;
39 static const BigEndianUint16 LANG_ID_MICROSOFT_EN_US = 0x0409;
41 #pragma pack(push, 1)
43 // Name table has a header, followed by name records, followed by string data.
44 struct NameHeader {
45 BigEndianUint16 format; // Format selector (=0).
46 BigEndianUint16 count; // Number of name records.
47 BigEndianUint16
48 stringOffset; // Offset to string storage from start of table.
51 struct NameRecord {
52 BigEndianUint16 platformID;
53 BigEndianUint16 encodingID; // Platform-specific encoding ID
54 BigEndianUint16 languageID;
55 BigEndianUint16 nameID;
56 BigEndianUint16 length; // String length in bytes.
57 BigEndianUint16 offset; // String offset from start of storage in bytes.
60 #pragma pack(pop)
62 enum ENameDecoder : int {
63 eNameDecoderUTF16,
64 #if defined(XP_MACOSX)
65 eNameDecoderMacRoman,
66 #endif
67 eNameDecoderNone
70 /* static */
71 UniquePtr<SFNTNameTable> SFNTNameTable::Create(const uint8_t* aNameData,
72 uint32_t aDataLength) {
73 MOZ_ASSERT(aNameData);
75 if (aDataLength < sizeof(NameHeader)) {
76 gfxWarning() << "Name data too short to contain NameHeader.";
77 return nullptr;
80 const NameHeader* nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
81 if (nameHeader->format != FORMAT_0) {
82 gfxWarning() << "Only Name Table Format 0 is supported.";
83 return nullptr;
86 uint16_t stringOffset = nameHeader->stringOffset;
88 if (stringOffset !=
89 sizeof(NameHeader) + (nameHeader->count * sizeof(NameRecord))) {
90 gfxWarning() << "Name table string offset is incorrect.";
91 return nullptr;
94 if (aDataLength < stringOffset) {
95 gfxWarning() << "Name data too short to contain name records.";
96 return nullptr;
99 return UniquePtr<SFNTNameTable>(
100 new SFNTNameTable(nameHeader, aNameData, aDataLength));
103 SFNTNameTable::SFNTNameTable(const NameHeader* aNameHeader,
104 const uint8_t* aNameData, uint32_t aDataLength)
105 : mFirstRecord(
106 reinterpret_cast<const NameRecord*>(aNameData + sizeof(NameHeader))),
107 mEndOfRecords(mFirstRecord + aNameHeader->count),
108 mStringData(aNameData + aNameHeader->stringOffset),
109 mStringDataLength(aDataLength - aNameHeader->stringOffset) {
110 MOZ_ASSERT(reinterpret_cast<const uint8_t*>(aNameHeader) == aNameData);
113 static bool IsUTF16Encoding(const NameRecord* aNameRecord) {
114 if (aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
115 (aNameRecord->encodingID == ENCODING_ID_MICROSOFT_UNICODEBMP ||
116 aNameRecord->encodingID == ENCODING_ID_MICROSOFT_SYMBOL)) {
117 return true;
120 if (aNameRecord->platformID == PLATFORM_ID_UNICODE) {
121 return true;
124 return false;
127 #if defined(XP_MACOSX)
128 static bool IsMacRomanEncoding(const NameRecord* aNameRecord) {
129 if (aNameRecord->platformID == PLATFORM_ID_MAC &&
130 aNameRecord->encodingID == ENCODING_ID_MAC_ROMAN) {
131 return true;
134 return false;
136 #endif
138 static NameRecordMatchers* CreateCanonicalMatchers(
139 const BigEndianUint16& aNameID) {
140 // For Windows, we return only Microsoft platform name record
141 // matchers. On Mac, we return matchers for both Microsoft platform
142 // records and Mac platform records.
143 NameRecordMatchers* matchers = new NameRecordMatchers();
145 #if defined(XP_MACOSX)
146 // First, look for the English name.
147 if (!matchers->append([=](const NameRecord* aNameRecord) {
148 if (aNameRecord->nameID == aNameID &&
149 aNameRecord->languageID == LANG_ID_MAC_ENGLISH &&
150 aNameRecord->platformID == PLATFORM_ID_MAC &&
151 IsMacRomanEncoding(aNameRecord)) {
152 return eNameDecoderMacRoman;
153 } else {
154 return eNameDecoderNone;
156 })) {
157 MOZ_CRASH();
160 // Second, look for all languages.
161 if (!matchers->append([=](const NameRecord* aNameRecord) {
162 if (aNameRecord->nameID == aNameID &&
163 aNameRecord->platformID == PLATFORM_ID_MAC &&
164 IsMacRomanEncoding(aNameRecord)) {
165 return eNameDecoderMacRoman;
166 } else {
167 return eNameDecoderNone;
169 })) {
170 MOZ_CRASH();
172 #endif /* defined(XP_MACOSX) */
174 // First, look for the English name (this will normally succeed).
175 if (!matchers->append([=](const NameRecord* aNameRecord) {
176 if (aNameRecord->nameID == aNameID &&
177 aNameRecord->languageID == LANG_ID_MICROSOFT_EN_US &&
178 aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
179 IsUTF16Encoding(aNameRecord)) {
180 return eNameDecoderUTF16;
181 } else {
182 return eNameDecoderNone;
184 })) {
185 MOZ_CRASH();
188 // Second, look for all languages.
189 if (!matchers->append([=](const NameRecord* aNameRecord) {
190 if (aNameRecord->nameID == aNameID &&
191 aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
192 IsUTF16Encoding(aNameRecord)) {
193 return eNameDecoderUTF16;
194 } else {
195 return eNameDecoderNone;
197 })) {
198 MOZ_CRASH();
201 return matchers;
204 static const NameRecordMatchers& FullNameMatchers() {
205 static const NameRecordMatchers* sFullNameMatchers =
206 CreateCanonicalMatchers(NAME_ID_FULL);
207 return *sFullNameMatchers;
210 static const NameRecordMatchers& FamilyMatchers() {
211 static const NameRecordMatchers* sFamilyMatchers =
212 CreateCanonicalMatchers(NAME_ID_FAMILY);
213 return *sFamilyMatchers;
216 static const NameRecordMatchers& StyleMatchers() {
217 static const NameRecordMatchers* sStyleMatchers =
218 CreateCanonicalMatchers(NAME_ID_STYLE);
219 return *sStyleMatchers;
222 bool SFNTNameTable::GetU16FullName(mozilla::u16string& aU16FullName) {
223 if (ReadU16Name(FullNameMatchers(), aU16FullName)) {
224 return true;
227 // If the full name record doesn't exist create the name from the family space
228 // concatenated with the style.
229 mozilla::u16string familyName;
230 if (!ReadU16Name(FamilyMatchers(), familyName)) {
231 return false;
234 mozilla::u16string styleName;
235 if (!ReadU16Name(StyleMatchers(), styleName)) {
236 return false;
239 aU16FullName.assign(std::move(familyName));
240 aU16FullName.append(u" ");
241 aU16FullName.append(styleName);
242 return true;
245 bool SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers,
246 mozilla::u16string& aU16Name) {
247 MOZ_ASSERT(!aMatchers.empty());
249 for (size_t i = 0; i < aMatchers.length(); ++i) {
250 const NameRecord* record = mFirstRecord;
251 while (record != mEndOfRecords) {
252 switch (aMatchers[i](record)) {
253 case eNameDecoderUTF16:
254 return ReadU16NameFromU16Record(record, aU16Name);
255 #if defined(XP_MACOSX)
256 case eNameDecoderMacRoman:
257 return ReadU16NameFromMacRomanRecord(record, aU16Name);
258 #endif
259 case eNameDecoderNone:
260 break;
261 default:
262 MOZ_CRASH("Invalid matcher encoding type");
263 break;
265 ++record;
269 return false;
272 bool SFNTNameTable::ReadU16NameFromU16Record(const NameRecord* aNameRecord,
273 mozilla::u16string& aU16Name) {
274 uint32_t offset = aNameRecord->offset;
275 uint32_t length = aNameRecord->length;
276 if (mStringDataLength < offset + length) {
277 gfxWarning() << "Name data too short to contain name string.";
278 return false;
281 const uint8_t* startOfName = mStringData + offset;
282 size_t actualLength = length / sizeof(char16_t);
283 UniquePtr<char16_t[]> nameData(new char16_t[actualLength]);
284 NativeEndian::copyAndSwapFromBigEndian(nameData.get(), startOfName,
285 actualLength);
287 aU16Name.assign(nameData.get(), actualLength);
288 return true;
291 #if defined(XP_MACOSX)
292 bool SFNTNameTable::ReadU16NameFromMacRomanRecord(
293 const NameRecord* aNameRecord, mozilla::u16string& aU16Name) {
294 uint32_t offset = aNameRecord->offset;
295 uint32_t length = aNameRecord->length;
296 if (mStringDataLength < offset + length) {
297 gfxWarning() << "Name data too short to contain name string.";
298 return false;
300 if (length > INT_MAX) {
301 gfxWarning() << "Name record too long to decode.";
302 return false;
305 // pointer to the Mac Roman encoded string in the name record
306 const uint8_t* encodedStr = mStringData + offset;
308 CFStringRef cfString;
309 cfString = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, encodedStr,
310 length, kCFStringEncodingMacRoman,
311 false, kCFAllocatorNull);
313 // length (in UTF-16 code pairs) of the decoded string
314 CFIndex decodedLength = CFStringGetLength(cfString);
316 // temporary buffer
317 UniquePtr<UniChar[]> u16Buffer = MakeUnique<UniChar[]>(decodedLength);
319 CFStringGetCharacters(cfString, CFRangeMake(0, decodedLength),
320 u16Buffer.get());
322 CFRelease(cfString);
324 aU16Name.assign(reinterpret_cast<char16_t*>(u16Buffer.get()), decodedLength);
326 return true;
328 #endif
330 } // namespace gfx
331 } // namespace mozilla