1 // Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
2 // 2016 The Chromium Authors. All rights reserved. Use of this source code is
3 // governed by a BSD-style license that can be found in the LICENSE file.
4
5 #include "libcef/common/crash_reporter_client.h"
6
7 #include <utility>
8
9 #if BUILDFLAG(IS_WIN)
10 #include <windows.h>
11 #endif
12
13 #include "base/environment.h"
14 #include "base/logging.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_piece.h"
18 #include "base/strings/string_split.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "components/crash/core/common/crash_key.h"
22 #include "content/public/common/content_switches.h"
23 #include "third_party/crashpad/crashpad/client/annotation.h"
24
25 #if BUILDFLAG(IS_MAC)
26 #include "libcef/common/util_mac.h"
27 #endif
28
29 #if BUILDFLAG(IS_POSIX)
30 // Don't use CommandLine, FilePath or PathService on Windows. FilePath has
31 // dependencies outside of kernel32, which is disallowed by chrome_elf.
32 // CommandLine and PathService depend on global state that will not be
33 // initialized at the time the CefCrashReporterClient object is created.
34 #include "base/command_line.h"
35 #include "base/environment.h"
36 #include "base/files/file_path.h"
37 #include "base/path_service.h"
38 #include "chrome/common/chrome_paths.h"
39 #endif
40
41 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
42 #include "content/public/common/content_switches.h"
43 #include "libcef/common/cef_crash_report_utils.h"
44 #endif
45
46 #if BUILDFLAG(IS_WIN)
47 #include "base/debug/leak_annotations.h"
48 #include "chrome/install_static/install_util.h"
49 #include "components/crash/core/app/crashpad.h"
50 #endif
51
52 namespace {
53
54 #if BUILDFLAG(IS_WIN)
55 using PathString = std::wstring;
56 const char kPathSep = '\\';
57 #else
58 using PathString = std::string;
59 #endif
60
GetCrashConfigPath()61 PathString GetCrashConfigPath() {
62 #if BUILDFLAG(IS_WIN)
63 // Start with the path to the running executable.
64 wchar_t module_path[MAX_PATH];
65 if (GetModuleFileName(nullptr, module_path, MAX_PATH) == 0)
66 return PathString();
67
68 PathString config_path = module_path;
69
70 // Remove the executable file name.
71 PathString::size_type last_backslash =
72 config_path.rfind(kPathSep, config_path.size());
73 if (last_backslash != PathString::npos)
74 config_path.erase(last_backslash + 1);
75
76 config_path += L"crash_reporter.cfg";
77 return config_path;
78 #elif BUILDFLAG(IS_POSIX)
79 base::FilePath config_path;
80
81 #if BUILDFLAG(IS_MAC)
82 // Start with the path to the main app Resources directory. May be empty if
83 // not running in an app bundle.
84 config_path = util_mac::GetMainResourcesDirectory();
85 #endif
86
87 if (config_path.empty()) {
88 // Start with the path to the running executable.
89 if (!base::PathService::Get(base::DIR_EXE, &config_path))
90 return PathString();
91 }
92
93 return config_path.Append(FILE_PATH_LITERAL("crash_reporter.cfg")).value();
94 #endif // BUILDFLAG(IS_POSIX)
95 }
96
97 #if BUILDFLAG(IS_WIN)
98
99 // On Windows, FAT32 and NTFS both limit filenames to a maximum of 255
100 // characters. On POSIX systems, the typical filename length limit is 255
101 // character units. HFS+'s limit is actually 255 Unicode characters using
102 // Apple's modification of Normalization Form D, but the differences aren't
103 // really worth dealing with here.
104 const unsigned maxFilenameLength = 255;
105
106 const char kInvalidFileChars[] = "<>:\"/\\|?*";
107
isInvalidFileCharacter(unsigned char c)108 bool isInvalidFileCharacter(unsigned char c) {
109 if (c < ' ' || c == 0x7F)
110 return true;
111 for (size_t i = 0; i < sizeof(kInvalidFileChars); ++i) {
112 if (c == kInvalidFileChars[i])
113 return true;
114 }
115 return false;
116 }
117
isAbsolutePath(const std::string & s)118 bool isAbsolutePath(const std::string& s) {
119 // Check for local paths (beginning with "c:\") and network paths
120 // (beginning with "\\").
121 return s.length() > 2 &&
122 ((isalpha(s[0]) && s[1] == ':' && s[2] == kPathSep) ||
123 (s[0] == kPathSep && s[1] == kPathSep));
124 }
125
extractAbsolutePathStart(std::string & s)126 std::string extractAbsolutePathStart(std::string& s) {
127 if (!isAbsolutePath(s))
128 return std::string();
129
130 std::string start;
131 if (s[0] == kPathSep) {
132 // Network path.
133 start = s.substr(0, 2);
134 s = s.substr(2);
135 } else {
136 // Local path.
137 start = s.substr(0, 3);
138 s = s.substr(3);
139 }
140 return start;
141 }
142
sanitizePathComponentPart(const std::string & s)143 std::string sanitizePathComponentPart(const std::string& s) {
144 if (s.empty())
145 return std::string();
146
147 std::string result;
148 result.reserve(s.length());
149 for (size_t i = 0; i < s.length(); ++i) {
150 if (!isInvalidFileCharacter(s[i]))
151 result.push_back(s[i]);
152 }
153 return result;
154 }
155
sanitizePathComponent(const std::string & s)156 std::string sanitizePathComponent(const std::string& s) {
157 std::string name, ext;
158
159 // Separate name and extension, if any.
160 std::string::size_type pos = s.rfind('.');
161 if (pos != std::string::npos) {
162 name = s.substr(0, pos);
163 ext = s.substr(pos + 1);
164 } else {
165 name = s;
166 }
167
168 // Remove invalid characters.
169 name = sanitizePathComponentPart(name);
170 ext = sanitizePathComponentPart(ext);
171
172 // Remove a ridiculously-long extension.
173 if (ext.length() >= maxFilenameLength)
174 ext = std::string();
175
176 // Truncate an overly-long filename, reserving one character for a dot.
177 std::string::size_type max_name_len = maxFilenameLength - ext.length() - 1;
178 if (name.length() > max_name_len)
179 name = name.substr(0, max_name_len);
180
181 return ext.empty() ? name : name + "." + ext;
182 }
183
sanitizePath(const std::string & s)184 std::string sanitizePath(const std::string& s) {
185 std::string path = s;
186
187 // Extract the absolute path start component, if any (e.g. "c:\" on Windows).
188 std::string result = extractAbsolutePathStart(path);
189 result.reserve(s.length());
190
191 std::vector<std::string> parts =
192 base::SplitString(path, std::string() + kPathSep, base::KEEP_WHITESPACE,
193 base::SPLIT_WANT_NONEMPTY);
194 for (size_t i = 0; i < parts.size(); ++i) {
195 std::string part = parts[i];
196 if (part != "." && part != "..")
197 part = sanitizePathComponent(part);
198 if (!result.empty() && result[result.length() - 1] != kPathSep)
199 result += kPathSep;
200 result += part;
201 }
202
203 return result;
204 }
205
joinPath(const std::string & s1,const std::string & s2)206 std::string joinPath(const std::string& s1, const std::string& s2) {
207 if (s1.empty() && s2.empty())
208 return std::string();
209 if (s1.empty())
210 return s2;
211 if (s2.empty())
212 return s1;
213
214 // Don't try to join absolute paths on Windows.
215 // Skip this check on POSIX where it's more difficult to differentiate.
216 if (isAbsolutePath(s2))
217 return s2;
218
219 std::string result = s1;
220 if (result[result.size() - 1] != kPathSep)
221 result += kPathSep;
222 if (s2[0] == kPathSep)
223 result += s2.substr(1);
224 else
225 result += s2;
226 return result;
227 }
228
229 // This will only be non-nullptr in the chrome_elf address space.
230 CefCrashReporterClient* g_crash_reporter_client = nullptr;
231
232 #endif // BUILDFLAG(IS_WIN)
233
234 const char kKeyMapDelim = ',';
235
NormalizeCrashKey(const base::StringPiece & key)236 std::string NormalizeCrashKey(const base::StringPiece& key) {
237 std::string str(key);
238 std::replace(str.begin(), str.end(), kKeyMapDelim, '-');
239 if (str.length() > crashpad::Annotation::kNameMaxLength) {
240 return str.substr(0, crashpad::Annotation::kNameMaxLength);
241 }
242 return str;
243 }
244
ParseURL(const std::string & value,std::string * url)245 void ParseURL(const std::string& value, std::string* url) {
246 if (value.find("http://") == 0 || value.find("https://") == 0) {
247 *url = value;
248 if (url->rfind('/') <= 8) {
249 // Make sure the URL includes a path component. Otherwise, crash
250 // upload will fail on older Windows versions due to
251 // https://crbug.com/826564.
252 *url += "/";
253 }
254 }
255 }
256
ParseBool(const std::string & value)257 bool ParseBool(const std::string& value) {
258 return base::EqualsCaseInsensitiveASCII(value, "true") || value == "1";
259 }
260
ParseZeroBasedInt(const std::string & value)261 int ParseZeroBasedInt(const std::string& value) {
262 int int_val;
263 if (base::StringToInt(value, &int_val) && int_val > 0)
264 return int_val;
265 return 0;
266 }
267
268 } // namespace
269
270 #if BUILDFLAG(IS_WIN)
271
272 extern "C" {
273
274 // Export functions from chrome_elf that are required by crash_reporting.cc
275
SetCrashKeyValueImpl(const char * key,size_t key_size,const char * value,size_t value_size)276 int __declspec(dllexport) __cdecl SetCrashKeyValueImpl(const char* key,
277 size_t key_size,
278 const char* value,
279 size_t value_size) {
280 if (g_crash_reporter_client) {
281 return g_crash_reporter_client->SetCrashKeyValue(
282 base::StringPiece(key, key_size), base::StringPiece(value, value_size));
283 }
284 return 0;
285 }
286
IsCrashReportingEnabledImpl()287 int __declspec(dllexport) __cdecl IsCrashReportingEnabledImpl() {
288 return g_crash_reporter_client &&
289 g_crash_reporter_client->HasCrashConfigFile();
290 }
291
292 } // extern "C"
293
294 // The below functions were deleted from chrome/install_static/install_util.cc
295 // in https://crbug.com/565446#c17.
296
297 constexpr wchar_t kUserDataDirname[] = L"User Data";
298
299 // Populates |result| with the default User Data directory for the current
300 // user.This may be overidden by a command line option. Returns false if all
301 // attempts at locating a User Data directory fail.
GetDefaultUserDataDirectory(std::wstring * result,const std::wstring & install_sub_directory)302 bool GetDefaultUserDataDirectory(std::wstring* result,
303 const std::wstring& install_sub_directory) {
304 // This environment variable should be set on Windows Vista and later
305 // (https://msdn.microsoft.com/library/windows/desktop/dd378457.aspx).
306 std::wstring user_data_dir =
307 install_static::GetEnvironmentString(L"LOCALAPPDATA");
308
309 if (user_data_dir.empty()) {
310 // LOCALAPPDATA was not set; fallback to the temporary files path.
311 DWORD size = ::GetTempPath(0, nullptr);
312 if (!size)
313 return false;
314 user_data_dir.resize(size + 1);
315 size = ::GetTempPath(size + 1, &user_data_dir[0]);
316 if (!size || size >= user_data_dir.size())
317 return false;
318 user_data_dir.resize(size);
319 }
320
321 result->swap(user_data_dir);
322 if ((*result)[result->length() - 1] != L'\\')
323 result->push_back(L'\\');
324 result->append(install_sub_directory);
325 result->push_back(L'\\');
326 result->append(kUserDataDirname);
327 return true;
328 }
329
330 // Populates |crash_dir| with the default crash dump location regardless of
331 // whether DIR_USER_DATA or DIR_CRASH_DUMPS has been overridden.
GetDefaultCrashDumpLocation(std::wstring * crash_dir,const std::wstring & install_sub_directory)332 bool GetDefaultCrashDumpLocation(std::wstring* crash_dir,
333 const std::wstring& install_sub_directory) {
334 // In order to be able to start crash handling very early, we do not rely on
335 // chrome's PathService entries (for DIR_CRASH_DUMPS) being available on
336 // Windows. See https://crbug.com/564398.
337 if (!GetDefaultUserDataDirectory(crash_dir, install_sub_directory))
338 return false;
339
340 // We have to make sure the user data dir exists on first run. See
341 // http://crbug.com/591504.
342 if (!install_static::RecursiveDirectoryCreate(*crash_dir))
343 return false;
344 crash_dir->append(L"\\Crashpad");
345 return true;
346 }
347
348 #endif // OS_WIN
349
CefCrashReporterClient()350 CefCrashReporterClient::CefCrashReporterClient() {}
~CefCrashReporterClient()351 CefCrashReporterClient::~CefCrashReporterClient() {}
352
353 // Be aware that logging is not initialized at the time this method is called.
ReadCrashConfigFile()354 bool CefCrashReporterClient::ReadCrashConfigFile() {
355 if (has_crash_config_file_)
356 return true;
357
358 PathString config_path = GetCrashConfigPath();
359 if (config_path.empty())
360 return false;
361
362 #if BUILDFLAG(IS_WIN)
363 FILE* fp = _wfopen(config_path.c_str(), L"r");
364 #else
365 FILE* fp = fopen(config_path.c_str(), "r");
366 #endif
367 if (!fp)
368 return false;
369
370 char line[1000];
371
372 size_t small_index = 0;
373 size_t medium_index = 0;
374 size_t large_index = 0;
375 std::string map_keys;
376
377 enum section {
378 kNoSection,
379 kConfigSection,
380 kCrashKeysSection,
381 } current_section = kNoSection;
382
383 while (fgets(line, sizeof(line) - 1, fp) != nullptr) {
384 std::string str = line;
385 base::TrimString(str, base::kWhitespaceASCII, &str);
386 if (str.empty() || str[0] == '#')
387 continue;
388
389 if (str == "[Config]") {
390 current_section = kConfigSection;
391 continue;
392 } else if (str == "[CrashKeys]") {
393 current_section = kCrashKeysSection;
394 continue;
395 } else if (str[0] == '[') {
396 current_section = kNoSection;
397 continue;
398 }
399
400 if (current_section == kNoSection)
401 continue;
402
403 size_t div = str.find('=');
404 if (div == std::string::npos)
405 continue;
406
407 std::string name_str = str.substr(0, div);
408 base::TrimString(name_str, base::kWhitespaceASCII, &name_str);
409 std::string val_str = str.substr(div + 1);
410 base::TrimString(val_str, base::kWhitespaceASCII, &val_str);
411 if (name_str.empty())
412 continue;
413
414 if (current_section == kConfigSection) {
415 if (name_str == "ServerURL") {
416 ParseURL(val_str, &server_url_);
417 } else if (name_str == "ProductName") {
418 product_name_ = val_str;
419 } else if (name_str == "ProductVersion") {
420 product_version_ = val_str;
421 } else if (name_str == "RateLimitEnabled") {
422 rate_limit_ = ParseBool(val_str);
423 } else if (name_str == "MaxUploadsPerDay") {
424 max_uploads_ = ParseZeroBasedInt(val_str);
425 } else if (name_str == "MaxDatabaseSizeInMb") {
426 max_db_size_ = ParseZeroBasedInt(val_str);
427 } else if (name_str == "MaxDatabaseAgeInDays") {
428 max_db_age_ = ParseZeroBasedInt(val_str);
429 }
430 #if BUILDFLAG(IS_WIN)
431 else if (name_str == "ExternalHandler") {
432 if (!val_str.empty())
433 external_handler_ = sanitizePath(val_str);
434 } else if (name_str == "AppName") {
435 if (!val_str.empty()) {
436 val_str = sanitizePathComponent(val_str);
437 if (!val_str.empty())
438 app_name_ = val_str;
439 }
440 }
441 #elif BUILDFLAG(IS_MAC)
442 else if (name_str == "BrowserCrashForwardingEnabled") {
443 enable_browser_crash_forwarding_ = ParseBool(val_str);
444 }
445 #endif
446 } else if (current_section == kCrashKeysSection) {
447 // Skip duplicate definitions.
448 if (!crash_keys_.empty() &&
449 crash_keys_.find(name_str) != crash_keys_.end()) {
450 continue;
451 }
452
453 KeySize size;
454 size_t index;
455 char group;
456 if (val_str == "small") {
457 size = SMALL_SIZE;
458 index = small_index++;
459 group = 'S';
460 } else if (val_str == "medium") {
461 size = MEDIUM_SIZE;
462 index = medium_index++;
463 group = 'M';
464 } else if (val_str == "large") {
465 size = LARGE_SIZE;
466 index = large_index++;
467 group = 'L';
468 } else {
469 continue;
470 }
471
472 name_str = NormalizeCrashKey(name_str);
473 crash_keys_.insert(std::make_pair(name_str, std::make_pair(size, index)));
474
475 const std::string& key =
476 std::string(1, group) + "-" + std::string(1, 'A' + index);
477 if (!map_keys.empty()) {
478 map_keys.append(std::string(1, kKeyMapDelim));
479 }
480 map_keys.append(key + "=" + name_str);
481 }
482 }
483
484 fclose(fp);
485
486 if (!map_keys.empty()) {
487 // Split |map_keys| across multiple Annotations if necessary.
488 // Must match the logic in crash_report_utils::FilterParameters.
489 using IDKey =
490 crash_reporter::CrashKeyString<crashpad::Annotation::kValueMaxSize>;
491 static IDKey ids[] = {
492 {"K-A", IDKey::Tag::kArray},
493 {"K-B", IDKey::Tag::kArray},
494 {"K-C", IDKey::Tag::kArray},
495 };
496
497 // Make sure we can fit all possible name/value pairs.
498 static_assert(base::size(ids) * crashpad::Annotation::kValueMaxSize >=
499 3 * 26 /* sizes (small, medium, large) * slots (A to Z) */
500 * (3 + 2 /* key size ("S-A") + delim size ("=,") */
501 + crashpad::Annotation::kNameMaxLength),
502 "Not enough storage for key map");
503
504 size_t offset = 0;
505 for (size_t i = 0; i < base::size(ids); ++i) {
506 size_t length = std::min(map_keys.size() - offset,
507 crashpad::Annotation::kValueMaxSize);
508 ids[i].Set(base::StringPiece(map_keys.data() + offset, length));
509 offset += length;
510 if (offset >= map_keys.size())
511 break;
512 }
513 }
514
515 // Allow override of some values via environment variables.
516 {
517 std::unique_ptr<base::Environment> env(base::Environment::Create());
518 std::string val_str;
519
520 if (env->GetVar("CEF_CRASH_REPORTER_SERVER_URL", &val_str)) {
521 ParseURL(val_str, &server_url_);
522 }
523 if (env->GetVar("CEF_CRASH_REPORTER_RATE_LIMIT_ENABLED", &val_str)) {
524 rate_limit_ = ParseBool(val_str);
525 }
526 }
527
528 has_crash_config_file_ = true;
529 return true;
530 }
531
HasCrashConfigFile() const532 bool CefCrashReporterClient::HasCrashConfigFile() const {
533 return has_crash_config_file_;
534 }
535
536 #if BUILDFLAG(IS_WIN)
537
538 // static
InitializeCrashReportingForProcess()539 void CefCrashReporterClient::InitializeCrashReportingForProcess() {
540 if (g_crash_reporter_client)
541 return;
542
543 g_crash_reporter_client = new CefCrashReporterClient();
544 ANNOTATE_LEAKING_OBJECT_PTR(g_crash_reporter_client);
545
546 if (!g_crash_reporter_client->ReadCrashConfigFile())
547 return;
548
549 std::wstring process_type = install_static::GetSwitchValueFromCommandLine(
550 ::GetCommandLineW(), install_static::kProcessType);
551 if (process_type != install_static::kCrashpadHandler) {
552 crash_reporter::SetCrashReporterClient(g_crash_reporter_client);
553
554 // If |embedded_handler| is true then we launch another instance of the main
555 // executable as the crashpad-handler process.
556 const bool embedded_handler =
557 !g_crash_reporter_client->HasCrashExternalHandler();
558 if (embedded_handler) {
559 crash_reporter::InitializeCrashpadWithEmbeddedHandler(
560 process_type.empty(), install_static::WideToUTF8(process_type),
561 std::string(), base::FilePath());
562 } else {
563 crash_reporter::InitializeCrashpad(
564 process_type.empty(), install_static::WideToUTF8(process_type));
565 }
566 }
567 }
568
GetAlternativeCrashDumpLocation(std::wstring * crash_dir)569 bool CefCrashReporterClient::GetAlternativeCrashDumpLocation(
570 std::wstring* crash_dir) {
571 // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
572 // location to write breakpad crash dumps can be set.
573 *crash_dir = install_static::GetEnvironmentString(L"BREAKPAD_DUMP_LOCATION");
574 return !crash_dir->empty();
575 }
576
GetProductNameAndVersion(const std::wstring & exe_path,std::wstring * product_name,std::wstring * version,std::wstring * special_build,std::wstring * channel_name)577 void CefCrashReporterClient::GetProductNameAndVersion(
578 const std::wstring& exe_path,
579 std::wstring* product_name,
580 std::wstring* version,
581 std::wstring* special_build,
582 std::wstring* channel_name) {
583 *product_name = base::ASCIIToWide(product_name_);
584 *version = base::ASCIIToWide(product_version_);
585 *special_build = std::wstring();
586 *channel_name = std::wstring();
587 }
588
GetCrashDumpLocation(std::wstring * crash_dir)589 bool CefCrashReporterClient::GetCrashDumpLocation(std::wstring* crash_dir) {
590 // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
591 // location to write breakpad crash dumps can be set.
592 if (GetAlternativeCrashDumpLocation(crash_dir))
593 return true;
594
595 return GetDefaultCrashDumpLocation(crash_dir, base::UTF8ToWide(app_name_));
596 }
597
GetCrashMetricsLocation(std::wstring * metrics_dir)598 bool CefCrashReporterClient::GetCrashMetricsLocation(
599 std::wstring* metrics_dir) {
600 return GetDefaultUserDataDirectory(metrics_dir, base::UTF8ToWide(app_name_));
601 }
602
603 #elif BUILDFLAG(IS_POSIX)
604
GetProductNameAndVersion(const char ** product_name,const char ** version)605 void CefCrashReporterClient::GetProductNameAndVersion(const char** product_name,
606 const char** version) {
607 *product_name = product_name_.c_str();
608 *version = product_version_.c_str();
609 }
610
GetProductNameAndVersion(std::string * product_name,std::string * version,std::string * channel)611 void CefCrashReporterClient::GetProductNameAndVersion(std::string* product_name,
612 std::string* version,
613 std::string* channel) {
614 *product_name = product_name_;
615 *version = product_version_;
616 }
617
618 #if !BUILDFLAG(IS_MAC)
619
GetReporterLogFilename()620 base::FilePath CefCrashReporterClient::GetReporterLogFilename() {
621 return base::FilePath(FILE_PATH_LITERAL("uploads.log"));
622 }
623
EnableBreakpadForProcess(const std::string & process_type)624 bool CefCrashReporterClient::EnableBreakpadForProcess(
625 const std::string& process_type) {
626 return process_type == switches::kRendererProcess ||
627 process_type == switches::kPpapiPluginProcess ||
628 process_type == switches::kZygoteProcess ||
629 process_type == switches::kGpuProcess;
630 }
631
632 #endif // !BUILDFLAG(IS_MAC)
633
GetCrashDumpLocation(base::FilePath * crash_dir)634 bool CefCrashReporterClient::GetCrashDumpLocation(base::FilePath* crash_dir) {
635 // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
636 // location to write breakpad crash dumps can be set.
637 std::unique_ptr<base::Environment> env(base::Environment::Create());
638 std::string alternate_crash_dump_location;
639 if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) {
640 base::FilePath crash_dumps_dir_path =
641 base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location);
642 base::PathService::Override(chrome::DIR_CRASH_DUMPS, crash_dumps_dir_path);
643 }
644 return base::PathService::Get(chrome::DIR_CRASH_DUMPS, crash_dir);
645 }
646
647 #endif // !BUILDFLAG(IS_POSIX)
648
GetCollectStatsConsent()649 bool CefCrashReporterClient::GetCollectStatsConsent() {
650 return true;
651 }
652
GetCollectStatsInSample()653 bool CefCrashReporterClient::GetCollectStatsInSample() {
654 return true;
655 }
656
657 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
ReportingIsEnforcedByPolicy(bool * crashpad_enabled)658 bool CefCrashReporterClient::ReportingIsEnforcedByPolicy(
659 bool* crashpad_enabled) {
660 *crashpad_enabled = true;
661 return true;
662 }
663 #endif
664
665 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
IsRunningUnattended()666 bool CefCrashReporterClient::IsRunningUnattended() {
667 // Crash upload will only be enabled with Breakpad on Linux if this method
668 // returns false.
669 return false;
670 }
671 #endif
672
GetUploadUrl()673 std::string CefCrashReporterClient::GetUploadUrl() {
674 return server_url_;
675 }
676
677 // See HandlerMain() in third_party/crashpad/crashpad/handler/handler_main.cc
678 // for supported arguments.
GetCrashOptionalArguments(std::vector<std::string> * arguments)679 void CefCrashReporterClient::GetCrashOptionalArguments(
680 std::vector<std::string>* arguments) {
681 if (!rate_limit_)
682 arguments->push_back(std::string("--no-rate-limit"));
683
684 if (max_uploads_ > 0) {
685 arguments->push_back(std::string("--max-uploads=") +
686 base::NumberToString(max_uploads_));
687 }
688
689 if (max_db_size_ > 0) {
690 arguments->push_back(std::string("--max-db-size=") +
691 base::NumberToString(max_db_size_));
692 }
693
694 if (max_db_age_ > 0) {
695 arguments->push_back(std::string("--max-db-age=") +
696 base::NumberToString(max_db_age_));
697 }
698 }
699
700 #if BUILDFLAG(IS_WIN)
701
GetCrashExternalHandler(const std::wstring & exe_dir)702 std::wstring CefCrashReporterClient::GetCrashExternalHandler(
703 const std::wstring& exe_dir) {
704 if (external_handler_.empty())
705 return CrashReporterClient::GetCrashExternalHandler(exe_dir);
706 if (isAbsolutePath(external_handler_))
707 return base::UTF8ToWide(external_handler_);
708 return base::UTF8ToWide(
709 joinPath(base::WideToUTF8(exe_dir), external_handler_));
710 }
711
HasCrashExternalHandler() const712 bool CefCrashReporterClient::HasCrashExternalHandler() const {
713 return !external_handler_.empty();
714 }
715
716 #endif // BUILDFLAG(IS_WIN)
717
718 #if BUILDFLAG(IS_MAC)
EnableBrowserCrashForwarding()719 bool CefCrashReporterClient::EnableBrowserCrashForwarding() {
720 return enable_browser_crash_forwarding_;
721 }
722 #endif
723
724 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
FilterParameters(const ParameterMap & parameters)725 CefCrashReporterClient::ParameterMap CefCrashReporterClient::FilterParameters(
726 const ParameterMap& parameters) {
727 return crash_report_utils::FilterParameters(parameters);
728 }
729 #endif
730
731 // The new Crashpad Annotation API requires that annotations be declared using
732 // static storage. We work around this limitation by defining a fixed amount of
733 // storage for each key size and later substituting the actual key name during
734 // crash dump processing.
735
736 #define IDKEY(name) \
737 { name, IDKey::Tag::kArray }
738
739 #define IDKEY_ENTRIES(n) \
740 IDKEY(n "-A"), IDKEY(n "-B"), IDKEY(n "-C"), IDKEY(n "-D"), IDKEY(n "-E"), \
741 IDKEY(n "-F"), IDKEY(n "-G"), IDKEY(n "-H"), IDKEY(n "-I"), \
742 IDKEY(n "-J"), IDKEY(n "-K"), IDKEY(n "-L"), IDKEY(n "-M"), \
743 IDKEY(n "-N"), IDKEY(n "-O"), IDKEY(n "-P"), IDKEY(n "-Q"), \
744 IDKEY(n "-R"), IDKEY(n "-S"), IDKEY(n "-T"), IDKEY(n "-U"), \
745 IDKEY(n "-V"), IDKEY(n "-W"), IDKEY(n "-X"), IDKEY(n "-Y"), \
746 IDKEY(n "-Z")
747
748 #define IDKEY_FUNCTION(name, size_) \
749 static_assert(size_ <= crashpad::Annotation::kValueMaxSize, \
750 "Annotation size is too large."); \
751 bool Set##name##Annotation(size_t index, const base::StringPiece& value) { \
752 using IDKey = crash_reporter::CrashKeyString<size_>; \
753 static IDKey ids[] = {IDKEY_ENTRIES(#name)}; \
754 if (index < base::size(ids)) { \
755 if (value.empty()) { \
756 ids[index].Clear(); \
757 } else { \
758 ids[index].Set(value); \
759 } \
760 return true; \
761 } \
762 return false; \
763 }
764
765 // The first argument must be kept synchronized with the logic in
766 // CefCrashReporterClient::ReadCrashConfigFile and
767 // crash_report_utils::FilterParameters.
768 IDKEY_FUNCTION(S, 64)
769 IDKEY_FUNCTION(M, 256)
770 IDKEY_FUNCTION(L, 1024)
771
SetCrashKeyValue(const base::StringPiece & key,const base::StringPiece & value)772 bool CefCrashReporterClient::SetCrashKeyValue(const base::StringPiece& key,
773 const base::StringPiece& value) {
774 if (key.empty() || crash_keys_.empty())
775 return false;
776
777 KeyMap::const_iterator it = crash_keys_.find(NormalizeCrashKey(key));
778 if (it == crash_keys_.end())
779 return false;
780
781 const KeySize size = it->second.first;
782 const size_t index = it->second.second;
783
784 base::AutoLock lock_scope(crash_key_lock_);
785
786 switch (size) {
787 case SMALL_SIZE:
788 return SetSAnnotation(index, value);
789 case MEDIUM_SIZE:
790 return SetMAnnotation(index, value);
791 case LARGE_SIZE:
792 return SetLAnnotation(index, value);
793 }
794
795 return false;
796 }
797