• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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