Browse Source

fix: crashReporter incompatible with sandbox on Linux (#23265) (#23461)

Jeremy Apthorp 5 years ago
parent
commit
2f8150891b
87 changed files with 2701 additions and 2721 deletions
  1. 25 18
      BUILD.gn
  2. 4 0
      chromium_src/BUILD.gn
  3. 1 0
      docs/api/app.md
  4. 105 56
      docs/api/crash-reporter.md
  5. 151 34
      docs/breaking-changes.md
  6. 4 9
      filenames.auto.gni
  7. 10 14
      filenames.gni
  8. 0 12
      lib/browser/api/crash-reporter.js
  9. 83 0
      lib/browser/api/crash-reporter.ts
  10. 0 25
      lib/browser/crash-reporter-init.js
  11. 20 5
      lib/browser/rpc-server.js
  12. 0 98
      lib/common/crash-reporter.js
  13. 0 12
      lib/renderer/api/crash-reporter.js
  14. 50 0
      lib/renderer/api/crash-reporter.ts
  15. 3 2
      package.json
  16. 5 0
      patches/chromium/.patches
  17. 29 0
      patches/chromium/breakpad_allow_getting_string_values_for_crash_keys.patch
  18. 49 0
      patches/chromium/breakpad_disable_upload_compression.patch
  19. 45 0
      patches/chromium/breakpad_treat_node_processes_as_browser_processes.patch
  20. 201 0
      patches/chromium/crash_allow_setting_more_options.patch
  21. 38 0
      patches/chromium/upload_list_add_loadsync_method.patch
  22. 0 1
      patches/node/.patches
  23. 0 22
      patches/node/inherit_electron_crashpad_pipe_name_in_child_process.patch
  24. 3 1
      script/zip_manifests/dist_zip.mac.x64.manifest
  25. 1 0
      script/zip_manifests/dist_zip.mac_mas.x64.manifest
  26. 1 1
      shell/app/electron_content_client.cc
  27. 209 0
      shell/app/electron_crash_reporter_client.cc
  28. 96 0
      shell/app/electron_crash_reporter_client.h
  29. 57 5
      shell/app/electron_main.cc
  30. 104 13
      shell/app/electron_main_delegate.cc
  31. 3 0
      shell/app/electron_main_delegate.h
  32. 71 18
      shell/app/node_main.cc
  33. 3 1
      shell/browser/api/electron_api_app.cc
  34. 1 1
      shell/browser/api/electron_api_app_mac.mm
  35. 1 17
      shell/browser/api/electron_api_auto_updater.cc
  36. 216 0
      shell/browser/api/electron_api_crash_reporter.cc
  37. 40 0
      shell/browser/api/electron_api_crash_reporter.h
  38. 1 1
      shell/browser/browser.cc
  39. 101 1
      shell/browser/electron_browser_client.cc
  40. 6 0
      shell/browser/electron_browser_client.h
  41. 1 1
      shell/browser/electron_browser_context.cc
  42. 1 1
      shell/browser/electron_browser_main_parts.cc
  43. 1 1
      shell/browser/electron_browser_main_parts_mac.mm
  44. 1 1
      shell/browser/ui/devtools_manager_delegate.cc
  45. 0 2
      shell/common/api/api.mojom
  46. 0 67
      shell/common/api/electron_api_crash_reporter.cc
  47. 144 0
      shell/common/crash_keys.cc
  48. 30 0
      shell/common/crash_keys.h
  49. 0 152
      shell/common/crash_reporter/crash_reporter.cc
  50. 0 79
      shell/common/crash_reporter/crash_reporter.h
  51. 0 118
      shell/common/crash_reporter/crash_reporter_crashpad.cc
  52. 0 49
      shell/common/crash_reporter/crash_reporter_crashpad.h
  53. 0 145
      shell/common/crash_reporter/crash_reporter_linux.cc
  54. 0 65
      shell/common/crash_reporter/crash_reporter_linux.h
  55. 0 43
      shell/common/crash_reporter/crash_reporter_mac.h
  56. 0 84
      shell/common/crash_reporter/crash_reporter_mac.mm
  57. 0 150
      shell/common/crash_reporter/crash_reporter_win.cc
  58. 0 52
      shell/common/crash_reporter/crash_reporter_win.h
  59. 0 753
      shell/common/crash_reporter/linux/crash_dump_handler.cc
  60. 0 46
      shell/common/crash_reporter/linux/crash_dump_handler.h
  61. 0 80
      shell/common/crash_reporter/win/crash_service_main.cc
  62. 0 17
      shell/common/crash_reporter/win/crash_service_main.h
  63. 0 4
      shell/common/electron_constants.cc
  64. 0 5
      shell/common/electron_constants.h
  65. 5 3
      shell/common/electron_paths.h
  66. 21 0
      shell/common/gin_converters/time_converter.cc
  67. 25 0
      shell/common/gin_converters/time_converter.h
  68. 2 1
      shell/common/node_bindings.cc
  69. 2 0
      shell/common/options_switches.cc
  70. 2 0
      shell/common/options_switches.h
  71. 42 0
      shell/renderer/api/electron_api_crash_reporter_renderer.cc
  72. 0 8
      shell/renderer/electron_api_service_impl.cc
  73. 0 1
      shell/renderer/electron_api_service_impl.h
  74. 498 344
      spec-main/api-crash-reporter-spec.ts
  75. 63 0
      spec-main/fixtures/apps/crash/main.js
  76. 10 0
      spec-main/fixtures/apps/crash/node-crash.js
  77. 4 0
      spec-main/fixtures/apps/crash/package.json
  78. 10 0
      spec-main/fixtures/apps/crash/sandbox-preload.js
  79. 24 0
      spec-main/fixtures/apps/remote-control/main.js
  80. 4 0
      spec-main/fixtures/apps/remote-control/package.json
  81. 2 0
      spec-main/fixtures/module/print-crash-parameters.js
  82. 1 0
      spec-main/package.json
  83. 19 0
      spec-main/yarn.lock
  84. 8 17
      spec/fixtures/module/crash.js
  85. 2 2
      spec/package.json
  86. 8 56
      spec/yarn.lock
  87. 34 7
      yarn.lock

+ 25 - 18
BUILD.gn

@@ -336,6 +336,7 @@ source_set("electron_lib") {
     "//chrome/app/resources:platform_locale_settings",
     "//chrome/services/printing/public/mojom",
     "//components/certificate_transparency",
+    "//components/crash/core/app",
     "//components/language/core/browser",
     "//components/net_log",
     "//components/network_hints/browser",
@@ -344,6 +345,7 @@ source_set("electron_lib") {
     "//components/network_session_configurator/common",
     "//components/pref_registry",
     "//components/prefs",
+    "//components/upload_list",
     "//components/user_prefs",
     "//components/viz/host",
     "//components/viz/service",
@@ -457,10 +459,15 @@ source_set("electron_lib") {
     ]
   }
 
+  if (is_linux) {
+    deps += [ "//components/crash/content/browser" ]
+  }
+
   if (is_mac) {
     deps += [
       "//components/remote_cocoa/app_shim",
       "//content/common:mac_helpers",
+      "//third_party/crashpad/crashpad/client",
       "//ui/accelerated_widget_mac",
     ]
 
@@ -482,11 +489,7 @@ source_set("electron_lib") {
     ]
     if (is_mas_build) {
       sources += [ "shell/browser/api/electron_api_app_mas.mm" ]
-      sources -= [
-        "shell/browser/auto_updater_mac.mm",
-        "shell/common/crash_reporter/crash_reporter_mac.h",
-        "shell/common/crash_reporter/crash_reporter_mac.mm",
-      ]
+      sources -= [ "shell/browser/auto_updater_mac.mm" ]
       defines += [ "MAS_BUILD" ]
     } else {
       libs += [
@@ -509,7 +512,6 @@ source_set("electron_lib") {
       "//build/config/linux/gtk",
       "//dbus",
       "//device/bluetooth",
-      "//third_party/breakpad:client",
       "//ui/events/devices/x11",
       "//ui/events/platform/x11",
       "//ui/gtk",
@@ -523,7 +525,6 @@ source_set("electron_lib") {
       ]
     }
     configs += [ ":gio_unix" ]
-    include_dirs += [ "//third_party/breakpad" ]
     configs += [ "//build/config/linux:x11" ]
     defines += [
       # Disable warnings for g_settings_list_schemas.
@@ -539,6 +540,7 @@ source_set("electron_lib") {
   if (is_win) {
     libs += [ "dwmapi.lib" ]
     deps += [
+      "//components/crash/core/app:crash_export_thunks",
       "//ui/native_theme:native_theme_browser",
       "//ui/views/controls/webview",
       "//ui/wm",
@@ -550,14 +552,6 @@ source_set("electron_lib") {
     ]
   }
 
-  if ((is_mac && !is_mas_build) || is_win) {
-    sources += [
-      "shell/common/crash_reporter/crash_reporter_crashpad.cc",
-      "shell/common/crash_reporter/crash_reporter_crashpad.h",
-    ]
-    deps += [ "//third_party/crashpad/crashpad/client" ]
-  }
-
   if (enable_plugins) {
     deps += [ "chromium_src:plugins" ]
     sources += [
@@ -774,11 +768,17 @@ if (is_mac) {
   }
 
   bundle_data("electron_crashpad_helper") {
-    sources = [ "$root_out_dir/crashpad_handler" ]
+    sources = [ "$root_out_dir/chrome_crashpad_handler" ]
 
-    outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
+    outputs = [ "{{bundle_contents_dir}}/Helpers/{{source_file_part}}" ]
 
-    public_deps = [ "//third_party/crashpad/crashpad/handler:crashpad_handler" ]
+    public_deps = [ "//components/crash/core/app:chrome_crashpad_handler" ]
+
+    if (is_asan) {
+      # crashpad_handler requires the ASan runtime at its @executable_path.
+      sources += [ "$root_out_dir/libclang_rt.asan_osx_dynamic.dylib" ]
+      public_deps += [ "//build/config/sanitizers:copy_asan_runtime" ]
+    }
   }
 
   mac_framework_bundle("electron_framework") {
@@ -787,6 +787,7 @@ if (is_mac) {
     framework_contents = [
       "Resources",
       "Libraries",
+      "Helpers",
     ]
     public_deps = [
       ":electron_framework_libraries",
@@ -1054,6 +1055,7 @@ if (is_mac) {
       ":electron_app_manifest",
       ":electron_lib",
       ":packed_resources",
+      "//components/crash/core/app",
       "//content:sandbox_helper_win",
       "//electron/buildflags",
       "//ui/strings",
@@ -1083,6 +1085,11 @@ if (is_mac) {
         "shell/browser/resources/win/resource.h",
       ]
 
+      deps += [
+        "//components/browser_watcher:browser_watcher_client",
+        "//components/crash/core/app:run_as_crashpad_handler",
+      ]
+
       libs = [
         "comctl32.lib",
         "uiautomationcore.lib",

+ 4 - 0
chromium_src/BUILD.gn

@@ -14,6 +14,8 @@ static_library("chrome") {
   sources = [
     "//chrome/browser/browser_process.cc",
     "//chrome/browser/browser_process.h",
+    "//chrome/browser/crash_upload_list/crash_upload_list_crashpad.cc",
+    "//chrome/browser/crash_upload_list/crash_upload_list_crashpad.h",
     "//chrome/browser/devtools/devtools_contents_resizing_strategy.cc",
     "//chrome/browser/devtools/devtools_contents_resizing_strategy.h",
     "//chrome/browser/devtools/devtools_embedder_message_dispatcher.cc",
@@ -55,6 +57,8 @@ static_library("chrome") {
     "//chrome/browser/ui/views/autofill/autofill_popup_view_utils.h",
     "//chrome/browser/win/chrome_process_finder.cc",
     "//chrome/browser/win/chrome_process_finder.h",
+    "//chrome/child/v8_crashpad_support_win.cc",
+    "//chrome/child/v8_crashpad_support_win.h",
     "//extensions/browser/app_window/size_constraints.cc",
     "//extensions/browser/app_window/size_constraints.h",
   ]

+ 1 - 0
docs/api/app.md

@@ -608,6 +608,7 @@ Returns `String` - The current application directory.
   * `videos` Directory for a user's videos.
   * `logs` Directory for your app's log folder.
   * `pepperFlashSystemPlugin` Full path to the system version of the Pepper Flash plugin.
+  * `crashDumps` Directory where crash dumps are stored.
 
 Returns `String` - A path to a special directory or file associated with `name`. On
 failure, an `Error` is thrown.

+ 105 - 56
docs/api/crash-reporter.md

@@ -4,18 +4,13 @@
 
 Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
 
-The following is an example of automatically submitting a crash report to a
-remote server:
+The following is an example of setting up Electron to automatically submit
+crash reports to a remote server:
 
 ```javascript
 const { crashReporter } = require('electron')
 
-crashReporter.start({
-  productName: 'YourName',
-  companyName: 'YourCompany',
-  submitURL: 'https://your-domain.com/url-to-submit',
-  uploadToServer: true
-})
+crashReporter.start({ submitURL: 'https://your-domain.com/url-to-submit' })
 ```
 
 For setting up a server to accept and process crash reports, you can use
@@ -30,11 +25,19 @@ Or use a 3rd party hosted solution:
 * [Sentry](https://docs.sentry.io/clients/electron)
 * [BugSplat](https://www.bugsplat.com/docs/platforms/electron)
 
-Crash reports are saved locally in an application-specific temp directory folder.
-For a `productName` of `YourName`, crash reports will be stored in a folder
-named `YourName Crashes` inside the temp directory. You can customize this temp
-directory location for your app by calling the `app.setPath('temp', '/my/custom/temp')`
-API before starting the crash reporter.
+Crash reports are stored temporarily before being uploaded in a directory
+underneath the app's user data directory (called 'Crashpad' on Windows and Mac,
+or 'Crash Reports' on Linux). You can override this directory by calling
+`app.setPath('crashDumps', '/path/to/crashes')` before starting the crash
+reporter.
+
+On Windows and macOS, Electron uses
+[crashpad](https://chromium.googlesource.com/crashpad/crashpad/+/master/README.md)
+to monitor and report crashes. On Linux, Electron uses
+[breakpad](https://chromium.googlesource.com/breakpad/breakpad/+/master/). This
+is an implementation detail driven by Chromium, and it may change in future. In
+particular, crashpad is newer and will likely eventually replace breakpad on
+all platforms.
 
 ## Methods
 
@@ -43,40 +46,68 @@ The `crashReporter` module has the following methods:
 ### `crashReporter.start(options)`
 
 * `options` Object
-  * `companyName` String
   * `submitURL` String - URL that crash reports will be sent to as POST.
   * `productName` String (optional) - Defaults to `app.name`.
-  * `uploadToServer` Boolean (optional) - Whether crash reports should be sent to the server. Default is `true`.
-  * `ignoreSystemCrashHandler` Boolean (optional) - Default is `false`.
-  * `extra` Record<String, String> (optional) - An object you can define that will be sent along with the
-    report. Only string properties are sent correctly. Nested objects are not
-    supported. When using Windows, the property names and values must be fewer than 64 characters.
-  * `crashesDirectory` String (optional) - Directory to store the crash reports temporarily (only used when the crash reporter is started via `process.crashReporter.start`).
-
-You are required to call this method before using any other `crashReporter` APIs
-and in each process (main/renderer) from which you want to collect crash reports.
-You can pass different options to `crashReporter.start` when calling from different processes.
-
-**Note** Child processes created via the `child_process` module will not have access to the Electron modules.
-Therefore, to collect crash reports from them, use `process.crashReporter.start` instead. Pass the same options as above
-along with an additional one called `crashesDirectory` that should point to a directory to store the crash
-reports temporarily. You can test this out by calling `process.crash()` to crash the child process.
-
-**Note:** If you need send additional/updated `extra` parameters after your
-first call `start` you can call `addExtraParameter` on macOS or call `start`
-again with the new/updated `extra` parameters on Linux and Windows.
-
-**Note:** On macOS and windows, Electron uses a new `crashpad` client for crash collection and reporting.
-If you want to enable crash reporting, initializing `crashpad` from the main process using `crashReporter.start` is required
-regardless of which process you want to collect crashes from. Once initialized this way, the crashpad handler collects
-crashes from all processes. You still have to call `crashReporter.start` from the renderer or child process, otherwise crashes from
-them will get reported without `companyName`, `productName` or any of the `extra` information.
+  * `companyName` String (optional) _Deprecated_ - Deprecated alias for
+    `{ globalExtra: { _companyName: ... } }`.
+  * `uploadToServer` Boolean (optional) - Whether crash reports should be sent
+    to the server. If false, crash reports will be collected and stored in the
+    crashes directory, but not uploaded. Default is `true`.
+  * `ignoreSystemCrashHandler` Boolean (optional) - If true, crashes generated
+    in the main process will not be forwarded to the system crash handler.
+    Default is `false`.
+  * `rateLimit` Boolean (optional) _macOS_ _Windows_ - If true, limit the
+    number of crashes uploaded to 1/hour. Default is `false`.
+  * `compress` Boolean (optional) _macOS_ _Windows_ - If true, crash reports
+    will be compressed and uploaded with `Content-Encoding: gzip`. Not all
+    collection servers support compressed payloads. Default is `false`.
+  * `extra` Record<String, String> (optional) - Extra string key/value
+    annotations that will be sent along with crash reports that are generated
+    in the main process. Only string values are supported. Crashes generated in
+    child processes will not contain these extra
+    parameters to crash reports generated from child processes, call
+    [`addExtraParameter`](#crashreporteraddextraparameterkey-value) from the
+    child process.
+  * `globalExtra` Record<String, String> (optional) - Extra string key/value
+    annotations that will be sent along with any crash reports generated in any
+    process. These annotations cannot be changed once the crash reporter has
+    been started. If a key is present in both the global extra parameters and
+    the process-specific extra parameters, then the global one will take
+    precedence. By default, `productName` and the app version are included, as
+    well as the Electron version.
+
+This method must be called before using any other `crashReporter` APIs. Once
+initialized this way, the crashpad handler collects crashes from all
+subsequently created processes. The crash reporter cannot be disabled once
+started.
+
+This method should be called as early as possible in app startup, preferably
+before `app.on('ready')`. If the crash reporter is not initialized at the time
+a renderer process is created, then that renderer process will not be monitored
+by the crash reporter.
+
+**Note:** You can test out the crash reporter by generating a crash using
+`process.crash()`.
+
+**Note:** If you need to send additional/updated `extra` parameters after your
+first call `start` you can call `addExtraParameter`.
+
+**Note:** Parameters passed in `extra`, `globalExtra` or set with
+`addExtraParameter` have limits on the length of the keys and values. Key names
+must be at most 39 bytes long, and values must be no longer than 127 bytes.
+Keys with names longer than the maximum will be silently ignored. Key values
+longer than the maximum length will be truncated.
+
+**Note:** Calling this method from the renderer process is deprecated.
 
 ### `crashReporter.getLastCrashReport()`
 
-Returns [`CrashReport`](structures/crash-report.md):
+Returns [`CrashReport`](structures/crash-report.md) - The date and ID of the
+last crash report. Only crash reports that have been uploaded will be returned;
+even if a crash report is present on disk it will not be returned until it is
+uploaded. In the case that there are no uploaded reports, `null` is returned.
 
-Returns the date and ID of the last crash report. Only crash reports that have been uploaded will be returned; even if a crash report is present on disk it will not be returned until it is uploaded. In the case that there are no uploaded reports, `null` is returned.
+**Note:** Calling this method from the renderer process is deprecated.
 
 ### `crashReporter.getUploadedReports()`
 
@@ -85,43 +116,61 @@ Returns [`CrashReport[]`](structures/crash-report.md):
 Returns all uploaded crash reports. Each report contains the date and uploaded
 ID.
 
+**Note:** Calling this method from the renderer process is deprecated.
+
 ### `crashReporter.getUploadToServer()`
 
 Returns `Boolean` - Whether reports should be submitted to the server. Set through
 the `start` method or `setUploadToServer`.
 
-**Note:** This API can only be called from the main process.
+**Note:** Calling this method from the renderer process is deprecated.
 
 ### `crashReporter.setUploadToServer(uploadToServer)`
 
-* `uploadToServer` Boolean _macOS_ - Whether reports should be submitted to the server.
+* `uploadToServer` Boolean - Whether reports should be submitted to the server.
 
 This would normally be controlled by user preferences. This has no effect if
 called before `start` is called.
 
-**Note:** This API can only be called from the main process.
+**Note:** Calling this method from the renderer process is deprecated.
 
-### `crashReporter.addExtraParameter(key, value)` _macOS_ _Windows_
+### `crashReporter.getCrashesDirectory()` _Deprecated_
 
-* `key` String - Parameter key, must be less than 64 characters long.
-* `value` String - Parameter value, must be less than 64 characters long.
+Returns `String` - The directory where crashes are temporarily stored before being uploaded.
 
-Set an extra parameter to be sent with the crash report. The values
-specified here will be sent in addition to any values set via the `extra` option when `start` was called. This API is only available on macOS and windows, if you need to add/update extra parameters on Linux after your first call to `start` you can call `start` again with the updated `extra` options.
+**Note:** This method is deprecated, use `app.getPath('crashDumps')` instead.
 
-### `crashReporter.removeExtraParameter(key)` _macOS_ _Windows_
+### `crashReporter.addExtraParameter(key, value)`
 
-* `key` String - Parameter key, must be less than 64 characters long.
+* `key` String - Parameter key, must be no longer than 39 bytes.
+* `value` String - Parameter value, must be no longer than 127 bytes.
 
-Remove a extra parameter from the current set of parameters so that it will not be sent with the crash report.
+Set an extra parameter to be sent with the crash report. The values specified
+here will be sent in addition to any values set via the `extra` option when
+`start` was called.
 
-### `crashReporter.getParameters()`
+Parameters added in this fashion (or via the `extra` parameter to
+`crashReporter.start`) are specific to the calling process. Adding extra
+parameters in the main process will not cause those parameters to be sent along
+with crashes from renderer or other child processes. Similarly, adding extra
+parameters in a renderer process will not result in those parameters being sent
+with crashes that occur in other renderer processes or in the main process.
 
-See all of the current parameters being passed to the crash reporter.
+**Note:** Parameters have limits on the length of the keys and values. Key
+names must be no longer than 39 bytes, and values must be no longer than 127
+bytes. Keys with names longer than the maximum will be silently ignored. Key
+values longer than the maximum length will be truncated.
 
-### `crashReporter.getCrashesDirectory()`
+### `crashReporter.removeExtraParameter(key)`
 
-Returns `String` - The directory where crashes are temporarily stored before being uploaded.
+* `key` String - Parameter key, must be no longer than 39 bytes.
+
+Remove a extra parameter from the current set of parameters. Future crashes
+will not include this parameter.
+
+### `crashReporter.getParameters()`
+
+Returns `Record<String, String>` - The current 'extra' parameters of the crash reporter.
 
 ## Crash Report Payload
 

+ 151 - 34
docs/breaking-changes.md

@@ -2,13 +2,124 @@
 
 Breaking changes will be documented here, and deprecation warnings added to JS code where possible, at least [one major version](tutorial/electron-versioning.md#semver) before the change is made.
 
-## `FIXME` comments
+### Types of Breaking Changes
 
-The `FIXME` string is used in code comments to denote things that should be fixed for future releases. See https://github.com/electron/electron/search?q=fixme
+This document uses the following convention to categorize breaking changes:
+
+- **API Changed:** An API was changed in such a way that code that has not been updated is guaranteed to throw an exception.
+- **Behavior Changed:** The behavior of Electron has changed, but not in such a way that an exception will necessarily be thrown.
+- **Default Changed:** Code depending on the old default may break, not necessarily throwing an exception. The old behavior can be restored by explicitly specifying the value.
+- **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
+- **Removed:** An API or feature was removed, and is no longer supported by Electron.
+
+## Planned Breaking API Changes (12.0)
+
+### Removed: `crashReporter` methods in the renderer process
+
+The following `crashReporter` methods are no longer available in the renderer
+process:
+
+- `crashReporter.start`
+- `crashReporter.getLastCrashReport`
+- `crashReporter.getUploadedReports`
+- `crashReporter.getUploadToServer`
+- `crashReporter.setUploadToServer`
+- `crashReporter.getCrashesDirectory`
+
+They should be called only from the main process.
+
+See [#23265](https://github.com/electron/electron/pull/23265) for more details.
+
+## Planned Breaking API Changes (11.0)
+
+## Planned Breaking API Changes (10.0)
+
+### Deprecated: `companyName` argument to `crashReporter.start()`
+
+The `companyName` argument to `crashReporter.start()`, which was previously
+required, is now optional, and further, is deprecated. To get the same
+behavior in a non-deprecated way, you can pass a `companyName` value in
+`globalExtra`.
+
+```js
+// Deprecated in Electron 10
+crashReporter.start({ companyName: 'Umbrella Corporation' })
+// Replace with
+crashReporter.start({ globalExtra: { _companyName: 'Umbrella Corporation' } })
+```
+
+### Deprecated: `crashReporter.getCrashesDirectory()`
+
+The `crashReporter.getCrashesDirectory` method has been deprecated. Usage
+should be replaced by `app.getPath('crashDumps')`.
+
+```js
+// Deprecated in Electron 10
+crashReporter.getCrashesDirectory()
+// Replace with
+app.getPath('crashDumps')
+```
+
+### Deprecated: `crashReporter` methods in the renderer process
+
+Calling the following `crashReporter` methods from the renderer process is
+deprecated:
+
+- `crashReporter.start`
+- `crashReporter.getLastCrashReport`
+- `crashReporter.getUploadedReports`
+- `crashReporter.getUploadToServer`
+- `crashReporter.setUploadToServer`
+- `crashReporter.getCrashesDirectory`
+
+The only non-deprecated methods remaining in the `crashReporter` module in the
+renderer are `addExtraParameter`, `removeExtraParameter` and `getParameters`.
+
+All above methods remain non-deprecated when called from the main process.
+
+See [#23265](https://github.com/electron/electron/pull/23265) for more details.
+
+### Removed: Browser Window Affinity
+
+The `affinity` option when constructing a new `BrowserWindow` will be removed
+as part of our plan to more closely align with Chromium's process model for security,
+performance and maintainability.
+
+For more detailed information see [#18397](https://github.com/electron/electron/issues/18397).
+
+### Default Changed: `enableRemoteModule` defaults to `false`
+
+In Electron 9, using the remote module without explicitly enabling it via the
+`enableRemoteModule` WebPreferences option began emitting a warning. In
+Electron 10, the remote module is now disabled by default. To use the remote
+module, `enableRemoteModule: true` must be specified in WebPreferences:
+
+```js
+const w = new BrowserWindow({
+  webPreferences: {
+    enableRemoteModule: true
+  }
+})
+```
+
+We [recommend moving away from the remote
+module](https://medium.com/@nornagon/electrons-remote-module-considered-harmful-70d69500f31).
 
 ## Planned Breaking API Changes (9.0)
 
-### `<webview>.getWebContents()`
+### Default Changed: Loading non-context-aware native modules in the renderer process is disabled by default
+
+As of Electron 9 we do not allow loading of non-context-aware native modules in
+the renderer process.  This is to improve security, performance and maintainability
+of Electron as a project.
+
+If this impacts you, you can temporarily set `app.allowRendererProcessReuse` to `false`
+to revert to the old behavior.  This flag will only be an option until Electron 11 so
+you should plan to update your native modules to be context aware.
+
+For more detailed information see [#18397](https://github.com/electron/electron/issues/18397).
+
+### Removed: `<webview>.getWebContents()`
 
 This API, which was deprecated in Electron 8.0, is now removed.
 
@@ -20,7 +131,7 @@ const { remote } = require('electron')
 remote.webContents.fromId(webview.getWebContentsId())
 ```
 
-### `webFrame.setLayoutZoomLevelLimits()`
+### Removed: `webFrame.setLayoutZoomLevelLimits()`
 
 Chromium has removed support for changing the layout zoom level limits, and it
 is beyond Electron's capacity to maintain it. The function was deprecated in
@@ -28,7 +139,7 @@ Electron 8.x, and has been removed in Electron 9.x. The layout zoom level limits
 are now fixed at a minimum of 0.25 and a maximum of 5.0, as defined
 [here](https://chromium.googlesource.com/chromium/src/+/938b37a6d2886bf8335fc7db792f1eb46c65b2ae/third_party/blink/common/page/page_zoom.cc#11).
 
-### Sending non-JS objects over IPC now throws an exception
+### Behavior Changed: Sending non-JS objects over IPC now throws an exception
 
 In Electron 8.0, IPC was changed to use the Structured Clone Algorithm,
 bringing significant performance improvements. To help ease the transition, the
@@ -44,9 +155,14 @@ In Electron 9.0, the old serialization algorithm has been removed, and sending
 such non-serializable objects will now throw an "object could not be cloned"
 error.
 
+### API Changed: `shell.openItem` is now `shell.openPath`
+
+The `shell.openItem` API has been replaced with an asynchronous `shell.openPath` API.
+You can see the original API proposal and reasoning [here](https://github.com/electron/governance/blob/master/wg-api/spec-documents/shell-openitem.md).
+
 ## Planned Breaking API Changes (8.0)
 
-### Values sent over IPC are now serialized with Structured Clone Algorithm
+### Behavior Changed: Values sent over IPC are now serialized with Structured Clone Algorithm
 
 The algorithm used to serialize objects sent over IPC (through
 `ipcRenderer.send`, `ipcRenderer.sendSync`, `WebContents.send` and related
@@ -97,7 +213,7 @@ these kinds of objects will throw a 'could not be cloned' error.
 
 [SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
 
-### `<webview>.getWebContents()`
+### Deprecated: `<webview>.getWebContents()`
 
 This API is implemented using the `remote` module, which has both performance
 and security implications. Therefore its usage should be explicit.
@@ -138,7 +254,7 @@ const { ipcRenderer } = require('electron')
 ipcRenderer.invoke('openDevTools', webview.getWebContentsId())
 ```
 
-### `webFrame.setLayoutZoomLevelLimits()`
+### Deprecated: `webFrame.setLayoutZoomLevelLimits()`
 
 Chromium has removed support for changing the layout zoom level limits, and it
 is beyond Electron's capacity to maintain it. The function will emit a warning
@@ -148,7 +264,7 @@ limits are now fixed at a minimum of 0.25 and a maximum of 5.0, as defined
 
 ## Planned Breaking API Changes (7.0)
 
-### Node Headers URL
+### Deprecated: Atom.io Node Headers URL
 
 This is the URL specified as `disturl` in a `.npmrc` file or as the `--dist-url`
 command line flag when building native Node modules.  Both will be supported for
@@ -158,7 +274,7 @@ Deprecated: https://atom.io/download/electron
 
 Replace with: https://electronjs.org/headers
 
-### `session.clearAuthCache(options)`
+### API Changed: `session.clearAuthCache()` no longer accepts options
 
 The `session.clearAuthCache` API no longer accepts options for what to clear, and instead unconditionally clears the whole cache.
 
@@ -169,25 +285,25 @@ session.clearAuthCache({ type: 'password' })
 session.clearAuthCache()
 ```
 
-### `powerMonitor.querySystemIdleState`
+### API Changed: `powerMonitor.querySystemIdleState` is now `powerMonitor.getSystemIdleState`
 
 ```js
 // Removed in Electron 7.0
 powerMonitor.querySystemIdleState(threshold, callback)
 // Replace with synchronous API
-const idleState = getSystemIdleState(threshold)
+const idleState = powerMonitor.getSystemIdleState(threshold)
 ```
 
-### `powerMonitor.querySystemIdleTime`
+### API Changed: `powerMonitor.querySystemIdleTime` is now `powerMonitor.getSystemIdleState`
 
 ```js
 // Removed in Electron 7.0
 powerMonitor.querySystemIdleTime(callback)
 // Replace with synchronous API
-const idleTime = getSystemIdleTime()
+const idleTime = powerMonitor.getSystemIdleTime()
 ```
 
-### webFrame Isolated World APIs
+### API Changed: `webFrame.setIsolatedWorldInfo` replaces separate methods
 
 ```js
 // Removed in Electron 7.0
@@ -204,11 +320,11 @@ webFrame.setIsolatedWorldInfo(
   })
 ```
 
-### Removal of deprecated `marked` property on getBlinkMemoryInfo
+### Removed: `marked` property on `getBlinkMemoryInfo`
 
 This property was removed in Chromium 77, and as such is no longer available.
 
-### `webkitdirectory` attribute for `<input type="file"/>`
+### Behavior Changed: `webkitdirectory` attribute for `<input type="file"/>` now lists directory contents
 
 The `webkitdirectory` property on HTML file inputs allows them to select folders.
 Previous versions of Electron had an incorrect implementation where the `event.target.files`
@@ -241,9 +357,10 @@ In Electron 7, this now returns a `FileList` with a `File` object for:
 Note that `webkitdirectory` no longer exposes the path to the selected folder.
 If you require the path to the selected folder rather than the folder contents,
 see the `dialog.showOpenDialog` API ([link](https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowopendialogbrowserwindow-options)).
+
 ## Planned Breaking API Changes (6.0)
 
-### `win.setMenu(null)`
+### API Changed: `win.setMenu(null)` is now `win.removeMenu()`
 
 ```js
 // Deprecated
@@ -252,7 +369,7 @@ win.setMenu(null)
 win.removeMenu()
 ```
 
-### `contentTracing.getTraceBufferUsage()`
+### API Changed: `contentTracing.getTraceBufferUsage()` is now a promise
 
 ```js
 // Deprecated
@@ -265,7 +382,7 @@ contentTracing.getTraceBufferUsage().then(infoObject => {
 })
 ```
 
-### `electron.screen` in renderer process
+### API Changed: `electron.screen` in the renderer process should be accessed via `remote`
 
 ```js
 // Deprecated
@@ -274,7 +391,7 @@ require('electron').screen
 require('electron').remote.screen
 ```
 
-### `require` in sandboxed renderers
+### API Changed: `require()`ing node builtins in sandboxed renderers no longer implicitly loads the `remote` version
 
 ```js
 // Deprecated
@@ -298,25 +415,25 @@ require('path')
 require('electron').remote.require('path')
 ```
 
-### `powerMonitor.querySystemIdleState`
+### Deprecated: `powerMonitor.querySystemIdleState` replaced with `powerMonitor.getSystemIdleState`
 
 ```js
 // Deprecated
 powerMonitor.querySystemIdleState(threshold, callback)
 // Replace with synchronous API
-const idleState = getSystemIdleState(threshold)
+const idleState = powerMonitor.getSystemIdleState(threshold)
 ```
 
-### `powerMonitor.querySystemIdleTime`
+### Deprecated: `powerMonitor.querySystemIdleTime` replaced with `powerMonitor.getSystemIdleTime`
 
 ```js
 // Deprecated
 powerMonitor.querySystemIdleTime(callback)
 // Replace with synchronous API
-const idleTime = getSystemIdleTime()
+const idleTime = powerMonitor.getSystemIdleTime()
 ```
 
-### `app.enableMixedSandbox`
+### Deprecated: `app.enableMixedSandbox()` is no longer needed
 
 ```js
 // Deprecated
@@ -325,7 +442,7 @@ app.enableMixedSandbox()
 
 Mixed-sandbox mode is now enabled by default.
 
-### `Tray`
+### Deprecated: `Tray.setHighlightMode`
 
 Under macOS Catalina our former Tray implementation breaks.
 Apple's native substitute doesn't support changing the highlighting behavior.
@@ -338,7 +455,7 @@ tray.setHighlightMode(mode)
 
 ## Planned Breaking API Changes (5.0)
 
-### `new BrowserWindow({ webPreferences })`
+### Default Changed: `nodeIntegration` and `webviewTag` default to false, `contextIsolation` defaults to true
 
 The following `webPreferences` option default values are deprecated in favor of the new defaults listed below.
 
@@ -358,16 +475,16 @@ const w = new BrowserWindow({
 })
 ```
 
-### `nativeWindowOpen`
+### Behavior Changed: `nodeIntegration` in child windows opened via `nativeWindowOpen`
 
-Child windows opened with the `nativeWindowOpen` option will always have Node.js integration disabled, unless `nodeIntegrationInSubFrames` is `true.
+Child windows opened with the `nativeWindowOpen` option will always have Node.js integration disabled, unless `nodeIntegrationInSubFrames` is `true`.
 
-### Privileged Schemes Registration
+### API Changed: Registering privileged schemes must now be done before app ready
 
-Renderer process APIs `webFrame.setRegisterURLSchemeAsPrivileged` and `webFrame.registerURLSchemeAsBypassingCSP` as well as browser process API `protocol.registerStandardSchemes` have been removed.
+Renderer process APIs `webFrame.registerURLSchemeAsPrivileged` and `webFrame.registerURLSchemeAsBypassingCSP` as well as browser process API `protocol.registerStandardSchemes` have been removed.
 A new API, `protocol.registerSchemesAsPrivileged` has been added and should be used for registering custom schemes with the required privileges. Custom schemes are required to be registered before app ready.
 
-### webFrame Isolated World APIs
+### Deprecated: `webFrame.setIsolatedWorld*` replaced with `webFrame.setIsolatedWorldInfo`
 
 ```js
 // Deprecated
@@ -384,7 +501,7 @@ webFrame.setIsolatedWorldInfo(
   })
 ```
 
-## `webFrame.setSpellCheckProvider`
+### API Changed: `webFrame.setSpellCheckProvider` now takes an asynchronous callback
 The `spellCheck` callback is now asynchronous, and `autoCorrectWord` parameter has been removed.
 ```js
 // Deprecated

+ 4 - 9
filenames.auto.gni

@@ -137,14 +137,13 @@ auto_filenames = {
     "lib/common/api/module-list.ts",
     "lib/common/api/native-image.js",
     "lib/common/api/shell.js",
-    "lib/common/crash-reporter.js",
     "lib/common/define-properties.ts",
     "lib/common/electron-binding-setup.ts",
     "lib/common/type-utils.ts",
     "lib/common/web-view-methods.ts",
     "lib/common/webpack-globals-provider.ts",
     "lib/renderer/api/context-bridge.ts",
-    "lib/renderer/api/crash-reporter.js",
+    "lib/renderer/api/crash-reporter.ts",
     "lib/renderer/api/desktop-capturer.ts",
     "lib/renderer/api/ipc-renderer.ts",
     "lib/renderer/api/remote.js",
@@ -213,7 +212,7 @@ auto_filenames = {
     "lib/browser/api/browser-view.js",
     "lib/browser/api/browser-window.js",
     "lib/browser/api/content-tracing.js",
-    "lib/browser/api/crash-reporter.js",
+    "lib/browser/api/crash-reporter.ts",
     "lib/browser/api/dialog.js",
     "lib/browser/api/exports/electron.ts",
     "lib/browser/api/global-shortcut.js",
@@ -249,7 +248,6 @@ auto_filenames = {
     "lib/browser/api/web-contents.js",
     "lib/browser/chrome-extension-shim.js",
     "lib/browser/chrome-extension.js",
-    "lib/browser/crash-reporter-init.js",
     "lib/browser/default-menu.ts",
     "lib/browser/desktop-capturer.ts",
     "lib/browser/devtools.ts",
@@ -269,7 +267,6 @@ auto_filenames = {
     "lib/common/api/module-list.ts",
     "lib/common/api/native-image.js",
     "lib/common/api/shell.js",
-    "lib/common/crash-reporter.js",
     "lib/common/define-properties.ts",
     "lib/common/electron-binding-setup.ts",
     "lib/common/init.ts",
@@ -292,7 +289,6 @@ auto_filenames = {
     "lib/common/api/module-list.ts",
     "lib/common/api/native-image.js",
     "lib/common/api/shell.js",
-    "lib/common/crash-reporter.js",
     "lib/common/define-properties.ts",
     "lib/common/electron-binding-setup.ts",
     "lib/common/init.ts",
@@ -301,7 +297,7 @@ auto_filenames = {
     "lib/common/web-view-methods.ts",
     "lib/common/webpack-globals-provider.ts",
     "lib/renderer/api/context-bridge.ts",
-    "lib/renderer/api/crash-reporter.js",
+    "lib/renderer/api/crash-reporter.ts",
     "lib/renderer/api/desktop-capturer.ts",
     "lib/renderer/api/exports/electron.ts",
     "lib/renderer/api/ipc-renderer.ts",
@@ -341,7 +337,6 @@ auto_filenames = {
     "lib/common/api/module-list.ts",
     "lib/common/api/native-image.js",
     "lib/common/api/shell.js",
-    "lib/common/crash-reporter.js",
     "lib/common/define-properties.ts",
     "lib/common/electron-binding-setup.ts",
     "lib/common/init.ts",
@@ -349,7 +344,7 @@ auto_filenames = {
     "lib/common/type-utils.ts",
     "lib/common/webpack-globals-provider.ts",
     "lib/renderer/api/context-bridge.ts",
-    "lib/renderer/api/crash-reporter.js",
+    "lib/renderer/api/crash-reporter.ts",
     "lib/renderer/api/desktop-capturer.ts",
     "lib/renderer/api/exports/electron.ts",
     "lib/renderer/api/ipc-renderer.ts",

+ 10 - 14
filenames.gni

@@ -31,6 +31,8 @@ filenames = {
     "shell/app/command_line_args.h",
     "shell/app/electron_content_client.cc",
     "shell/app/electron_content_client.h",
+    "shell/app/electron_crash_reporter_client.cc",
+    "shell/app/electron_crash_reporter_client.h",
     "shell/app/electron_main_delegate.cc",
     "shell/app/electron_main_delegate.h",
     "shell/app/electron_main_delegate_mac.h",
@@ -51,6 +53,8 @@ filenames = {
     "shell/browser/api/electron_api_content_tracing.cc",
     "shell/browser/api/electron_api_cookies.cc",
     "shell/browser/api/electron_api_cookies.h",
+    "shell/browser/api/electron_api_crash_reporter.cc",
+    "shell/browser/api/electron_api_crash_reporter.h",
     "shell/browser/api/electron_api_data_pipe_holder.cc",
     "shell/browser/api/electron_api_data_pipe_holder.h",
     "shell/browser/api/electron_api_debugger.cc",
@@ -164,7 +168,6 @@ filenames = {
     "shell/browser/electron_javascript_dialog_manager.h",
     "shell/browser/electron_navigation_throttle.cc",
     "shell/browser/electron_navigation_throttle.h",
-    "shell/browser/electron_paths.h",
     "shell/browser/electron_permission_manager.cc",
     "shell/browser/electron_permission_manager.h",
     "shell/browser/electron_quota_permission_context.cc",
@@ -433,7 +436,6 @@ filenames = {
     "shell/common/api/electron_api_clipboard.h",
     "shell/common/api/electron_api_clipboard_mac.mm",
     "shell/common/api/electron_api_command_line.cc",
-    "shell/common/api/electron_api_crash_reporter.cc",
     "shell/common/api/electron_api_key_weak_map.h",
     "shell/common/api/electron_api_native_image.cc",
     "shell/common/api/electron_api_native_image.h",
@@ -456,22 +458,13 @@ filenames = {
     "shell/common/asar/scoped_temporary_file.h",
     "shell/common/color_util.cc",
     "shell/common/color_util.h",
-    "shell/common/crash_reporter/crash_reporter.cc",
-    "shell/common/crash_reporter/crash_reporter.h",
-    "shell/common/crash_reporter/crash_reporter_linux.cc",
-    "shell/common/crash_reporter/crash_reporter_linux.h",
-    "shell/common/crash_reporter/crash_reporter_mac.h",
-    "shell/common/crash_reporter/crash_reporter_mac.mm",
-    "shell/common/crash_reporter/crash_reporter_win.cc",
-    "shell/common/crash_reporter/crash_reporter_win.h",
-    "shell/common/crash_reporter/linux/crash_dump_handler.cc",
-    "shell/common/crash_reporter/linux/crash_dump_handler.h",
-    "shell/common/crash_reporter/win/crash_service_main.cc",
-    "shell/common/crash_reporter/win/crash_service_main.h",
+    "shell/common/crash_keys.cc",
+    "shell/common/crash_keys.h",
     "shell/common/electron_command_line.cc",
     "shell/common/electron_command_line.h",
     "shell/common/electron_constants.cc",
     "shell/common/electron_constants.h",
+    "shell/common/electron_paths.h",
     "shell/common/gin_converters/accelerator_converter.cc",
     "shell/common/gin_converters/accelerator_converter.h",
     "shell/common/gin_converters/blink_converter.cc",
@@ -493,6 +486,8 @@ filenames = {
     "shell/common/gin_converters/net_converter.cc",
     "shell/common/gin_converters/net_converter.h",
     "shell/common/gin_converters/std_converter.h",
+    "shell/common/gin_converters/time_converter.cc",
+    "shell/common/gin_converters/time_converter.h",
     "shell/common/gin_converters/value_converter.cc",
     "shell/common/gin_converters/value_converter.h",
     "shell/common/gin_helper/arguments.cc",
@@ -567,6 +562,7 @@ filenames = {
     "shell/renderer/api/context_bridge/render_frame_function_store.h",
     "shell/renderer/api/electron_api_context_bridge.cc",
     "shell/renderer/api/electron_api_context_bridge.h",
+    "shell/renderer/api/electron_api_crash_reporter_renderer.cc",
     "shell/renderer/api/electron_api_renderer_ipc.cc",
     "shell/renderer/api/electron_api_spell_check_client.cc",
     "shell/renderer/api/electron_api_spell_check_client.h",

+ 0 - 12
lib/browser/api/crash-reporter.js

@@ -1,12 +0,0 @@
-'use strict';
-
-const CrashReporter = require('@electron/internal/common/crash-reporter');
-const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
-
-class CrashReporterMain extends CrashReporter {
-  init (options) {
-    return crashReporterInit(options);
-  }
-}
-
-module.exports = new CrashReporterMain();

+ 83 - 0
lib/browser/api/crash-reporter.ts

@@ -0,0 +1,83 @@
+import { app } from 'electron';
+
+const binding = process.electronBinding('crash_reporter');
+
+class CrashReporter {
+  start (options: Electron.CrashReporterStartOptions) {
+    const {
+      productName = app.name,
+      companyName = undefined,
+      extra = {},
+      globalExtra = {},
+      ignoreSystemCrashHandler = false,
+      submitURL = undefined,
+      uploadToServer = true,
+      rateLimit = false,
+      compress = false
+    } = options || {};
+
+    if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
+
+    const appVersion = app.getVersion();
+
+    if (companyName && globalExtra._companyName == null) globalExtra._companyName = companyName;
+
+    const globalExtraAmended = {
+      _productName: productName,
+      _version: appVersion,
+      ...globalExtra
+    };
+
+    binding.start(submitURL, uploadToServer,
+      ignoreSystemCrashHandler, rateLimit, compress, globalExtraAmended, extra, false);
+  }
+
+  getLastCrashReport () {
+    const reports = this.getUploadedReports()
+      .sort((a, b) => {
+        const ats = (a && a.date) ? new Date(a.date).getTime() : 0;
+        const bts = (b && b.date) ? new Date(b.date).getTime() : 0;
+        return bts - ats;
+      });
+
+    return (reports.length > 0) ? reports[0] : null;
+  }
+
+  getUploadedReports (): Electron.CrashReport[] {
+    return binding.getUploadedReports();
+  }
+
+  getCrashesDirectory () {
+    return app.getPath('crashDumps');
+  }
+
+  getUploadToServer () {
+    if (process.type === 'browser') {
+      return binding.getUploadToServer();
+    } else {
+      throw new Error('getUploadToServer can only be called from the main process');
+    }
+  }
+
+  setUploadToServer (uploadToServer: boolean) {
+    if (process.type === 'browser') {
+      return binding.setUploadToServer(uploadToServer);
+    } else {
+      throw new Error('setUploadToServer can only be called from the main process');
+    }
+  }
+
+  addExtraParameter (key: string, value: string) {
+    binding.addExtraParameter(key, value);
+  }
+
+  removeExtraParameter (key: string) {
+    binding.removeExtraParameter(key);
+  }
+
+  getParameters () {
+    return binding.getParameters();
+  }
+}
+
+export default new CrashReporter();

+ 0 - 25
lib/browser/crash-reporter-init.js

@@ -1,25 +0,0 @@
-'use strict';
-
-const { app } = require('electron');
-const path = require('path');
-
-const getTempDirectory = function () {
-  try {
-    return app.getPath('temp');
-  } catch {
-    // Delibrately laze-load the os module, this file is on the hot
-    // path when booting Electron and os takes between 5 - 8ms to load and we do not need it yet
-    return require('os').tmpdir();
-  }
-};
-
-exports.crashReporterInit = function (options) {
-  const productName = options.productName || app.name;
-  const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`);
-
-  return {
-    productName,
-    crashesDirectory,
-    appVersion: app.getVersion()
-  };
-};

+ 20 - 5
lib/browser/rpc-server.js

@@ -7,7 +7,6 @@ const eventBinding = process.electronBinding('event');
 const clipboard = process.electronBinding('clipboard');
 const features = process.electronBinding('features');
 
-const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
 const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
 const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
 const guestViewManager = require('@electron/internal/browser/guest-view-manager');
@@ -37,10 +36,6 @@ ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
   event.returnValue = null;
 });
 
-ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
-  return crashReporterInit(options);
-});
-
 ipcMainInternal.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
   return event.sender.getLastWebPreferences();
 });
@@ -135,3 +130,23 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event)
 ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) {
   event.sender.emit('preload-error', event, preloadPath, error);
 });
+
+ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_LAST_CRASH_REPORT', () => {
+  return electron.crashReporter.getLastCrashReport();
+});
+
+ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_UPLOADED_REPORTS', () => {
+  return electron.crashReporter.getUploadedReports();
+});
+
+ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_UPLOAD_TO_SERVER', () => {
+  return electron.crashReporter.getUploadToServer();
+});
+
+ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_SET_UPLOAD_TO_SERVER', (event, uploadToServer) => {
+  return electron.crashReporter.setUploadToServer(uploadToServer);
+});
+
+ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_CRASHES_DIRECTORY', () => {
+  return electron.crashReporter.getCrashesDirectory();
+});

+ 0 - 98
lib/common/crash-reporter.js

@@ -1,98 +0,0 @@
-'use strict';
-
-const binding = process.electronBinding('crash_reporter');
-
-class CrashReporter {
-  constructor () {
-    this.productName = null;
-    this.crashesDirectory = null;
-  }
-
-  init (options) {
-    throw new Error('Not implemented');
-  }
-
-  start (options) {
-    if (options == null) options = {};
-
-    const {
-      productName,
-      companyName,
-      extra = {},
-      ignoreSystemCrashHandler = false,
-      submitURL,
-      uploadToServer = true
-    } = options;
-
-    if (companyName == null) throw new Error('companyName is a required option to crashReporter.start');
-    if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
-
-    const ret = this.init({
-      submitURL,
-      productName
-    });
-
-    this.productName = ret.productName;
-    this.crashesDirectory = ret.crashesDirectory;
-
-    if (extra._productName == null) extra._productName = ret.productName;
-    if (extra._companyName == null) extra._companyName = companyName;
-    if (extra._version == null) extra._version = ret.appVersion;
-
-    binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra);
-  }
-
-  getLastCrashReport () {
-    const reports = this.getUploadedReports()
-      .sort((a, b) => {
-        const ats = (a && a.date) ? new Date(a.date).getTime() : 0;
-        const bts = (b && b.date) ? new Date(b.date).getTime() : 0;
-        return bts - ats;
-      });
-
-    return (reports.length > 0) ? reports[0] : null;
-  }
-
-  getUploadedReports () {
-    const crashDir = this.getCrashesDirectory();
-    if (!crashDir) {
-      throw new Error('crashReporter has not been started');
-    }
-
-    return binding.getUploadedReports(crashDir);
-  }
-
-  getCrashesDirectory () {
-    return this.crashesDirectory;
-  }
-
-  getUploadToServer () {
-    if (process.type === 'browser') {
-      return binding.getUploadToServer();
-    } else {
-      throw new Error('getUploadToServer can only be called from the main process');
-    }
-  }
-
-  setUploadToServer (uploadToServer) {
-    if (process.type === 'browser') {
-      return binding.setUploadToServer(uploadToServer);
-    } else {
-      throw new Error('setUploadToServer can only be called from the main process');
-    }
-  }
-
-  addExtraParameter (key, value) {
-    binding.addExtraParameter(key, value);
-  }
-
-  removeExtraParameter (key) {
-    binding.removeExtraParameter(key);
-  }
-
-  getParameters () {
-    return binding.getParameters();
-  }
-}
-
-module.exports = CrashReporter;

+ 0 - 12
lib/renderer/api/crash-reporter.js

@@ -1,12 +0,0 @@
-'use strict';
-
-const CrashReporter = require('@electron/internal/common/crash-reporter');
-const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
-
-class CrashReporterRenderer extends CrashReporter {
-  init (options) {
-    return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options);
-  }
-}
-
-module.exports = new CrashReporterRenderer();

+ 50 - 0
lib/renderer/api/crash-reporter.ts

@@ -0,0 +1,50 @@
+import { invokeSync } from '../ipc-renderer-internal-utils';
+import { deprecate } from 'electron';
+
+const binding = process.electronBinding('crash_reporter');
+
+export default {
+  start (options: Electron.CrashReporterStartOptions) {
+    deprecate.log('crashReporter.start is deprecated in the renderer process. Call it from the main process instead.');
+    for (const [k, v] of Object.entries(options.extra || {})) {
+      binding.addExtraParameter(k, String(v));
+    }
+  },
+
+  getLastCrashReport (): Electron.CrashReport | null {
+    deprecate.log('crashReporter.getLastCrashReport is deprecated in the renderer process. Call it from the main process instead.');
+    return invokeSync('ELECTRON_CRASH_REPORTER_GET_LAST_CRASH_REPORT');
+  },
+
+  getUploadedReports () {
+    deprecate.log('crashReporter.getUploadedReports is deprecated in the renderer process. Call it from the main process instead.');
+    return invokeSync('ELECTRON_CRASH_REPORTER_GET_UPLOADED_REPORTS');
+  },
+
+  getUploadToServer () {
+    deprecate.log('crashReporter.getUploadToServer is deprecated in the renderer process. Call it from the main process instead.');
+    return invokeSync('ELECTRON_CRASH_REPORTER_GET_UPLOAD_TO_SERVER');
+  },
+
+  setUploadToServer (uploadToServer: boolean) {
+    deprecate.log('crashReporter.setUploadToServer is deprecated in the renderer process. Call it from the main process instead.');
+    return invokeSync('ELECTRON_CRASH_REPORTER_SET_UPLOAD_TO_SERVER', uploadToServer);
+  },
+
+  getCrashesDirectory () {
+    deprecate.log('crashReporter.getCrashesDirectory is deprecated in the renderer process. Call it from the main process instead.');
+    return invokeSync('ELECTRON_CRASH_REPORTER_GET_CRASHES_DIRECTORY');
+  },
+
+  addExtraParameter (key: string, value: string) {
+    binding.addExtraParameter(key, value);
+  },
+
+  removeExtraParameter (key: string) {
+    binding.removeExtraParameter(key);
+  },
+
+  getParameters () {
+    return binding.getParameters();
+  }
+};

+ 3 - 2
package.json

@@ -9,6 +9,7 @@
     "@octokit/rest": "^16.3.2",
     "@primer/octicons": "^9.1.1",
     "@types/basic-auth": "^1.1.2",
+    "@types/busboy": "^0.2.3",
     "@types/chai": "^4.1.7",
     "@types/chai-as-promised": "^7.1.0",
     "@types/dirty-chai": "^2.0.0",
@@ -16,6 +17,7 @@
     "@types/fs-extra": "^5.0.5",
     "@types/mocha": "^5.2.6",
     "@types/node": "^12.0.10",
+    "@types/rimraf": "^3.0.0",
     "@types/semver": "^6.0.1",
     "@types/send": "^0.14.5",
     "@types/split": "^1.0.0",
@@ -139,7 +141,6 @@
     ]
   },
   "dependencies": {
-    "@types/multiparty": "^0.0.32",
     "@types/temp": "^0.8.34"
   }
-}
+}

+ 5 - 0
patches/chromium/.patches

@@ -93,3 +93,8 @@ web_contents.patch
 ui_gtk_public_header.patch
 layoutng_make_hittestresult_localpoint_for_inline_element.patch
 fix-ensure-edit-cmds-to-sent-focused-WebContents.patch
+crash_allow_setting_more_options.patch
+breakpad_disable_upload_compression.patch
+breakpad_treat_node_processes_as_browser_processes.patch
+upload_list_add_loadsync_method.patch
+breakpad_allow_getting_string_values_for_crash_keys.patch

+ 29 - 0
patches/chromium/breakpad_allow_getting_string_values_for_crash_keys.patch

@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jeremy Apthorp <[email protected]>
+Date: Tue, 5 May 2020 12:36:39 -0700
+Subject: breakpad: allow getting string values for crash keys
+
+Linux is currently recording both crashpad and breakpad keys on linux
+(because upstream is experimenting with crashpad-on-linux). We can fetch
+the string values for crashpad keys on win/mac, and they're easily
+available on linux too, this just exposes them.
+
+Should be upstreamed, or failing that, deleted once crashpad is enabled
+on linux. If removing this patch doesn't cause a compile failure, it's
+fine to delete!
+
+diff --git a/components/crash/core/common/crash_key.h b/components/crash/core/common/crash_key.h
+index 9d193ea962e919d4509eef296c900e47059761f4..225b8ce822a42d31d59bc89aab473710384c3010 100644
+--- a/components/crash/core/common/crash_key.h
++++ b/components/crash/core/common/crash_key.h
+@@ -212,6 +212,10 @@ class CrashKeyStringCombined : public internal::CrashKeyStringCombinedImpl {
+     crashpad_key_.Set(value);
+   }
+ 
++  const base::StringPiece value() const {
++    return crashpad_key_.value();
++  }
++
+  private:
+   CrashKeyStringBreakpad<MaxLength> breakpad_key_;
+   crashpad::StringAnnotation<MaxLength> crashpad_key_;

+ 49 - 0
patches/chromium/breakpad_disable_upload_compression.patch

@@ -0,0 +1,49 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jeremy Apthorp <[email protected]>
+Date: Wed, 29 Apr 2020 16:28:35 -0700
+Subject: breakpad: disable upload compression
+
+Our prior implementation of breakpad uploading did not compress files on
+upload. In order to maintain that behavior, this disables compression in
+//components/crash.
+
+Ideally, this would be made configurable.
+
+diff --git a/components/crash/core/app/breakpad_linux.cc b/components/crash/core/app/breakpad_linux.cc
+index 364af690879d79d25d118d5b2fdbaabe21a1c52d..390cca9edc26d3153c8dbc86024cb50097d7ac78 100644
+--- a/components/crash/core/app/breakpad_linux.cc
++++ b/components/crash/core/app/breakpad_linux.cc
+@@ -1334,6 +1334,7 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
+ 
+ #else  // defined(OS_CHROMEOS)
+ 
++  /*
+   // Compress |dumpfile| with gzip.
+   const pid_t gzip_child = sys_fork();
+   if (gzip_child < 0) {
+@@ -1377,13 +1378,16 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
+     WriteLog(msg, sizeof(msg) - 1);
+     sys__exit(1);
+   }
++  */
+ 
+   // The --header argument to wget looks like:
+   //   --header=Content-Encoding: gzip
+   //   --header=Content-Type: multipart/form-data; boundary=XYZ
+   // where the boundary has two fewer leading '-' chars
++  /*
+   static const char header_content_encoding[] =
+       "--header=Content-Encoding: gzip";
++  */
+   static const char header_msg[] =
+       "--header=Content-Type: multipart/form-data; boundary=";
+   const size_t header_content_type_size =
+@@ -1412,7 +1416,7 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
+   static const char kWgetBinary[] = "/usr/bin/wget";
+   const char* args[] = {
+     kWgetBinary,
+-    header_content_encoding,
++    //header_content_encoding,
+     header_content_type,
+     post_file,
+     upload_url,

+ 45 - 0
patches/chromium/breakpad_treat_node_processes_as_browser_processes.patch

@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jeremy Apthorp <[email protected]>
+Date: Thu, 30 Apr 2020 17:04:13 -0700
+Subject: breakpad: treat node processes as browser processes
+
+On Linux, to avoid the need to pass breakpad FDs to child node processes
+spawned by child_process.fork(), each child process must re-initialize
+breakpad independently, as a "browser" process. This patches
+//components/crash so that it will correctly report 'ptype=node' as a
+crash annotation.
+
+diff --git a/components/crash/core/app/breakpad_linux.cc b/components/crash/core/app/breakpad_linux.cc
+index 390cca9edc26d3153c8dbc86024cb50097d7ac78..2ae2429e6054f2d54e785c49a49fc243469bb72b 100644
+--- a/components/crash/core/app/breakpad_linux.cc
++++ b/components/crash/core/app/breakpad_linux.cc
+@@ -722,8 +722,13 @@ bool CrashDone(const MinidumpDescriptor& minidump,
+   log_path[log_path_len] = '\0';
+   info.log_filename = log_path;
+ #endif
+-  info.process_type = "browser";
+-  info.process_type_length = 7;
++  if (g_is_node) {
++    info.process_type = "node";
++    info.process_type_length = 4;
++  } else {
++    info.process_type = "browser";
++    info.process_type_length = 7;
++  }
+   info.distro = base::g_linux_distro;
+   info.distro_length = my_strlen(base::g_linux_distro);
+   info.upload = upload;
+@@ -2050,8 +2055,13 @@ void InitCrashReporter(const std::string& process_type) {
+       process_type == kWebViewSingleProcessType ||
+       process_type == kBrowserProcessType ||
+ #endif
++      process_type == "node" ||
+       process_type.empty();
+ 
++  if (process_type == "node") {
++    g_is_node = true;
++  }
++
+   std::string upload_url;
+   if (GetCrashReporterClient()->GetUploadUrl(&upload_url))
+     SetUploadURL(upload_url);

+ 201 - 0
patches/chromium/crash_allow_setting_more_options.patch

@@ -0,0 +1,201 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jeremy Apthorp <[email protected]>
+Date: Thu, 30 Apr 2020 10:08:06 -0700
+Subject: crash: allow setting more options
+
+This allows the client of //components/crash to set upload url,
+rate-limiting, compression and global annotations.
+
+This should be upstreamed.
+
+diff --git a/components/crash/core/app/breakpad_linux.cc b/components/crash/core/app/breakpad_linux.cc
+index 192b0a7f137f311abb6a991f997e11f5eba52256..364af690879d79d25d118d5b2fdbaabe21a1c52d 100644
+--- a/components/crash/core/app/breakpad_linux.cc
++++ b/components/crash/core/app/breakpad_linux.cc
+@@ -103,9 +103,18 @@ namespace {
+ // while we do have functions to deal with uint64_t's.
+ uint64_t g_crash_loop_before_time = 0;
+ #else
+-const char kUploadURL[] = "https://clients2.google.com/cr/report";
++const char kDefaultUploadURL[] = "https://clients2.google.com/cr/report";
++char* g_upload_url = nullptr;
+ #endif
+ 
++void SetUploadURL(const std::string& url) {
++  const size_t url_len = url.size() + 1;
++  DCHECK(!g_upload_url);
++  g_upload_url = new char[url_len];
++  strncpy(g_upload_url, url.c_str(), url_len);
++}
++
++bool g_is_node = false;
+ bool g_is_crash_reporter_enabled = false;
+ uint64_t g_process_start_time = 0;
+ pid_t g_pid = 0;
+@@ -1398,13 +1407,15 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
+   char* status_fd_path =
+       StringFromPrefixAndUint("/dev/fd/", upload_status_fd, allocator);
+ 
++  const char* upload_url = g_upload_url ? g_upload_url : kDefaultUploadURL;
++
+   static const char kWgetBinary[] = "/usr/bin/wget";
+   const char* args[] = {
+     kWgetBinary,
+     header_content_encoding,
+     header_content_type,
+     post_file,
+-    kUploadURL,
++    upload_url,
+     "--timeout=10",  // Set a timeout so we don't hang forever.
+     "--tries=1",     // Don't retry if the upload fails.
+     "-O",  // Output reply to the file descriptor path.
+@@ -2037,6 +2048,10 @@ void InitCrashReporter(const std::string& process_type) {
+ #endif
+       process_type.empty();
+ 
++  std::string upload_url;
++  if (GetCrashReporterClient()->GetUploadUrl(&upload_url))
++    SetUploadURL(upload_url);
++
+   if (is_browser_process) {
+     bool enable_breakpad = GetCrashReporterClient()->GetCollectStatsConsent() ||
+                            GetCrashReporterClient()->IsRunningUnattended();
+diff --git a/components/crash/core/app/crash_reporter_client.cc b/components/crash/core/app/crash_reporter_client.cc
+index e778f68af30f8c071d1360d06b553cc957160c5b..b7f6ca60ef9c2367989d39e1268bf9f9a517951c 100644
+--- a/components/crash/core/app/crash_reporter_client.cc
++++ b/components/crash/core/app/crash_reporter_client.cc
+@@ -148,6 +148,17 @@ bool CrashReporterClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) {
+   return false;
+ }
+ 
++bool CrashReporterClient::GetShouldRateLimit() {
++  return true;
++}
++
++bool CrashReporterClient::GetShouldCompressUploads() {
++  return true;
++}
++
++void CrashReporterClient::GetProcessSimpleAnnotations(std::map<std::string, std::string>* annotations) {
++}
++
+ #if defined(OS_ANDROID)
+ unsigned int CrashReporterClient::GetCrashDumpPercentage() {
+   return 100;
+@@ -196,6 +207,10 @@ void CrashReporterClient::GetSanitizationInformation(
+ }
+ #endif
+ 
++bool CrashReporterClient::GetUploadUrl(std::string* url) {
++  return false;
++}
++
+ bool CrashReporterClient::ShouldMonitorCrashHandlerExpensively() {
+   return false;
+ }
+diff --git a/components/crash/core/app/crash_reporter_client.h b/components/crash/core/app/crash_reporter_client.h
+index 9cc78fc2584061d26fd1e83b3ebf5ada0a12c138..aa63d7b95c37e4a143283450798b8bd500dc17a1 100644
+--- a/components/crash/core/app/crash_reporter_client.h
++++ b/components/crash/core/app/crash_reporter_client.h
+@@ -5,6 +5,7 @@
+ #ifndef COMPONENTS_CRASH_CORE_APP_CRASH_REPORTER_CLIENT_H_
+ #define COMPONENTS_CRASH_CORE_APP_CRASH_REPORTER_CLIENT_H_
+ 
++#include <map>
+ #include <string>
+ 
+ #include "base/strings/string16.h"
+@@ -153,6 +154,19 @@ class CrashReporterClient {
+   // that case, |breakpad_enabled| is set to the value enforced by policies.
+   virtual bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled);
+ 
++  // Returns true if crash uploads should be rate limited. If false, no
++  // throttling will be applied for uploads.
++  virtual bool GetShouldRateLimit();
++
++  // Returns true if crash uploads should be compressed with gzip. If false,
++  // reports will be uploaded uncompressed.
++  virtual bool GetShouldCompressUploads();
++
++  // Allows the client to add or edit global annotations passed to the crashpad
++  // handler.
++  virtual void GetProcessSimpleAnnotations(
++      std::map<std::string, std::string>* annotations);
++
+ #if defined(OS_ANDROID)
+   // Used by WebView to sample crashes without generating the unwanted dumps. If
+   // the returned value is less than 100, crash dumping will be sampled to that
+@@ -194,6 +208,9 @@ class CrashReporterClient {
+       bool* sanitize_stacks);
+ #endif
+ 
++  // Override the default upload url. Returns true if overridden.
++  virtual bool GetUploadUrl(std::string* url);
++
+   // This method should return true to configure a crash reporter capable of
+   // monitoring itself for its own crashes to do so, even if self-monitoring
+   // would be expensive. "Expensive" self-monitoring dedicates an additional
+diff --git a/components/crash/core/app/crashpad_mac.mm b/components/crash/core/app/crashpad_mac.mm
+index b579521d55860823722df2ee849f6b1628b3c950..f4f71e5174cf8fb706a2f8385252ba877d1a03a7 100644
+--- a/components/crash/core/app/crashpad_mac.mm
++++ b/components/crash/core/app/crashpad_mac.mm
+@@ -67,6 +67,8 @@ std::map<std::string, std::string> GetProcessSimpleAnnotations() {
+     }  // @autoreleasepool
+     return process_annotations;
+   }();
++  CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
++  crash_reporter_client->GetProcessSimpleAnnotations(&annotations);
+   return annotations;
+ }
+ 
+@@ -140,9 +142,17 @@ base::FilePath PlatformCrashpadInitialization(
+ #else
+       std::string url;
+ #endif
++      crash_reporter_client->GetUploadUrl(&url);
+ 
+       std::vector<std::string> arguments;
+ 
++      if (!crash_reporter_client->GetShouldRateLimit()) {
++        arguments.push_back("--no-rate-limit");
++      }
++      if (!crash_reporter_client->GetShouldCompressUploads()) {
++        arguments.push_back("--no-upload-gzip");
++      }
++
+       if (crash_reporter_client->ShouldMonitorCrashHandlerExpensively()) {
+         arguments.push_back("--monitor-self");
+       }
+diff --git a/components/crash/core/app/crashpad_win.cc b/components/crash/core/app/crashpad_win.cc
+index 669f5bea844d75f0e5c34b58994f4cfb8e856af0..8c1fb8fb8f2e02466b51ef08de25b056f8b2052d 100644
+--- a/components/crash/core/app/crashpad_win.cc
++++ b/components/crash/core/app/crashpad_win.cc
+@@ -84,12 +84,14 @@ base::FilePath PlatformCrashpadInitialization(
+ 
+     std::map<std::string, std::string> process_annotations;
+     GetPlatformCrashpadAnnotations(&process_annotations);
++    crash_reporter_client->GetProcessSimpleAnnotations(&process_annotations);
+ 
+ #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+     std::string url = "https://clients2.google.com/cr/report";
+ #else
+     std::string url;
+ #endif
++    crash_reporter_client->GetUploadUrl(&url);
+ 
+     // Allow the crash server to be overridden for testing. If the variable
+     // isn't present in the environment then the default URL will remain.
+@@ -126,6 +128,13 @@ base::FilePath PlatformCrashpadInitialization(
+ 
+     std::vector<std::string> arguments(start_arguments);
+ 
++    if (!crash_reporter_client->GetShouldRateLimit()) {
++      arguments.push_back("--no-rate-limit");
++    }
++    if (!crash_reporter_client->GetShouldCompressUploads()) {
++      arguments.push_back("--no-upload-gzip");
++    }
++
+     if (crash_reporter_client->ShouldMonitorCrashHandlerExpensively()) {
+       arguments.push_back("--monitor-self");
+       for (const std::string& start_argument : start_arguments) {

+ 38 - 0
patches/chromium/upload_list_add_loadsync_method.patch

@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jeremy Apthorp <[email protected]>
+Date: Fri, 1 May 2020 18:25:59 -0700
+Subject: upload_list: add LoadSync method
+
+This allows synchronous loading of the upload list, which is required by
+the crashReporter.getUploadedReports() API. The synchronous version is
+deprecated, and this API should be removed once the deprecated behavior
+is no longer supported.
+
+diff --git a/components/upload_list/upload_list.cc b/components/upload_list/upload_list.cc
+index ea718a40f80bc65eb9b25259d6e02c9322c54e48..9d9f9919efcefa83e6fd5b3e8447ec901cfa1388 100644
+--- a/components/upload_list/upload_list.cc
++++ b/components/upload_list/upload_list.cc
+@@ -70,6 +70,10 @@ void UploadList::Load(base::OnceClosure callback) {
+       base::BindOnce(&UploadList::OnLoadComplete, this));
+ }
+ 
++void UploadList::LoadSync() {
++  uploads_ = LoadUploadList();
++}
++
+ void UploadList::Clear(const base::Time& begin,
+                        const base::Time& end,
+                        base::OnceClosure callback) {
+diff --git a/components/upload_list/upload_list.h b/components/upload_list/upload_list.h
+index 20358339df63ae2bb8b955870c5daf51b65f19f7..7cf89626bea8ee9436f15366446f053a479ac438 100644
+--- a/components/upload_list/upload_list.h
++++ b/components/upload_list/upload_list.h
+@@ -73,6 +73,8 @@ class UploadList : public base::RefCountedThreadSafe<UploadList> {
+   // overwrite the previously supplied one, and the first will not be called.
+   void Load(base::OnceClosure callback);
+ 
++  void LoadSync();
++
+   // Clears any data associated with the upload list, where the upload time or
+   // capture time falls within the given range.
+   void Clear(const base::Time& begin,

+ 0 - 1
patches/node/.patches

@@ -22,7 +22,6 @@ fix_do_not_define_debugoptions_s_constructors_in_header.patch
 build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch
 refactor_allow_embedder_overriding_of_internal_fs_calls.patch
 chore_prevent_warn_non_context-aware_native_modules_being_loaded.patch
-inherit_electron_crashpad_pipe_name_in_child_process.patch
 chore_read_nobrowserglobals_from_global_not_process.patch
 chore_split_createenvironment_into_createenvironment_and.patch
 build_bring_back_node_with_ltcg_configuration.patch

+ 0 - 22
patches/node/inherit_electron_crashpad_pipe_name_in_child_process.patch

@@ -1,22 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Cheng Zhao <[email protected]>
-Date: Tue, 4 Jun 2019 17:42:11 +0900
-Subject: Inherit ELECTRON_CRASHPAD_PIPE_NAME in child process
-
-This is required for crashReporter to work correctly in node process.
-
-diff --git a/lib/child_process.js b/lib/child_process.js
-index d5d97c9da90281e0c24fe97a4914db12d685c7bc..a6d058c92dff927cd2fc6bcb2bb304e9ca497a31 100644
---- a/lib/child_process.js
-+++ b/lib/child_process.js
-@@ -107,6 +107,10 @@ function fork(modulePath /* , args, options */) {
-   options.env = Object.create(options.env || process.env)
-   options.env.ELECTRON_RUN_AS_NODE = 1;
- 
-+  if (process.platform === 'win32') {
-+    options.env.ELECTRON_CRASHPAD_PIPE_NAME = process.env.ELECTRON_CRASHPAD_PIPE_NAME;
-+  }
-+
-   if (!options.execPath && process.type && process.platform == 'darwin') {
-     options.execPath = process.helperExecPath;
-   }

+ 3 - 1
script/zip_manifests/dist_zip.mac.x64.manifest

@@ -3,11 +3,14 @@ Electron.app/Contents/
 Electron.app/Contents/Frameworks/
 Electron.app/Contents/Frameworks/Electron Framework.framework/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Electron Framework
+Electron.app/Contents/Frameworks/Electron Framework.framework/Helpers
 Electron.app/Contents/Frameworks/Electron Framework.framework/Libraries
 Electron.app/Contents/Frameworks/Electron Framework.framework/Resources
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework
+Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Helpers/
+Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Helpers/chrome_crashpad_handler
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib
@@ -31,7 +34,6 @@ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resourc
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/ca.lproj/locale.pak
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/chrome_100_percent.pak
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/chrome_200_percent.pak
-Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/crashpad_handler
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/cs.lproj/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/cs.lproj/locale.pak
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/da.lproj/

+ 1 - 0
script/zip_manifests/dist_zip.mac_mas.x64.manifest

@@ -3,6 +3,7 @@ Electron.app/Contents/
 Electron.app/Contents/Frameworks/
 Electron.app/Contents/Frameworks/Electron Framework.framework/
 Electron.app/Contents/Frameworks/Electron Framework.framework/Electron Framework
+Electron.app/Contents/Frameworks/Electron Framework.framework/Helpers
 Electron.app/Contents/Frameworks/Electron Framework.framework/Libraries
 Electron.app/Contents/Frameworks/Electron Framework.framework/Resources
 Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/

+ 1 - 1
shell/app/electron_content_client.cc

@@ -19,7 +19,7 @@
 #include "electron/buildflags/buildflags.h"
 #include "extensions/common/constants.h"
 #include "ppapi/buildflags/buildflags.h"
-#include "shell/browser/electron_paths.h"
+#include "shell/common/electron_paths.h"
 #include "shell/common/options_switches.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"

+ 209 - 0
shell/app/electron_crash_reporter_client.cc

@@ -0,0 +1,209 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/app/electron_crash_reporter_client.h"
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/crash/core/common/crash_keys.h"
+#include "components/upload_list/crash_upload_list.h"
+#include "content/public/common/content_switches.h"
+#include "electron/electron_version.h"
+#include "services/service_manager/embedder/switches.h"
+#include "shell/common/electron_paths.h"
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#include "components/version_info/version_info_values.h"
+#endif
+
+#if defined(OS_POSIX)
+#include "base/debug/dump_without_crashing.h"
+#endif
+
+namespace {
+
+ElectronCrashReporterClient* Instance() {
+  static base::NoDestructor<ElectronCrashReporterClient> crash_client;
+  return crash_client.get();
+}
+
+}  // namespace
+
+// static
+void ElectronCrashReporterClient::Create() {
+  crash_reporter::SetCrashReporterClient(Instance());
+
+  // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
+  // location to write crash dumps can be set.
+  std::unique_ptr<base::Environment> env(base::Environment::Create());
+  std::string alternate_crash_dump_location;
+  base::FilePath crash_dumps_dir_path;
+  if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) {
+    crash_dumps_dir_path =
+        base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location);
+  }
+  if (!crash_dumps_dir_path.empty()) {
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    base::PathService::Override(electron::DIR_CRASH_DUMPS,
+                                crash_dumps_dir_path);
+  }
+}
+
+// static
+ElectronCrashReporterClient* ElectronCrashReporterClient::Get() {
+  return Instance();
+}
+
+void ElectronCrashReporterClient::SetCollectStatsConsent(bool upload_allowed) {
+  collect_stats_consent_ = upload_allowed;
+}
+
+void ElectronCrashReporterClient::SetUploadUrl(const std::string& url) {
+  upload_url_ = url;
+}
+
+void ElectronCrashReporterClient::SetShouldRateLimit(bool rate_limit) {
+  rate_limit_ = rate_limit;
+}
+
+void ElectronCrashReporterClient::SetShouldCompressUploads(bool compress) {
+  compress_uploads_ = compress;
+}
+
+void ElectronCrashReporterClient::SetGlobalAnnotations(
+    const std::map<std::string, std::string>& annotations) {
+  global_annotations_ = annotations;
+}
+
+ElectronCrashReporterClient::ElectronCrashReporterClient() {}
+
+ElectronCrashReporterClient::~ElectronCrashReporterClient() {}
+
+#if defined(OS_LINUX)
+void ElectronCrashReporterClient::SetCrashReporterClientIdFromGUID(
+    const std::string& client_guid) {
+  crash_keys::SetMetricsClientIdFromGUID(client_guid);
+}
+void ElectronCrashReporterClient::GetProductNameAndVersion(
+    const char** product_name,
+    const char** version) {
+  DCHECK(product_name);
+  DCHECK(version);
+  *product_name = ELECTRON_PRODUCT_NAME;
+  *version = ELECTRON_VERSION_STRING;
+}
+
+void ElectronCrashReporterClient::GetProductNameAndVersion(
+    std::string* product_name,
+    std::string* version,
+    std::string* channel) {
+  const char* c_product_name;
+  const char* c_version;
+  GetProductNameAndVersion(&c_product_name, &c_version);
+  *product_name = c_product_name;
+  *version = c_version;
+  *channel = "";
+}
+
+base::FilePath ElectronCrashReporterClient::GetReporterLogFilename() {
+  return base::FilePath(CrashUploadList::kReporterLogFilename);
+}
+#endif
+
+#if defined(OS_WIN)
+void ElectronCrashReporterClient::GetProductNameAndVersion(
+    const base::string16& exe_path,
+    base::string16* product_name,
+    base::string16* version,
+    base::string16* special_build,
+    base::string16* channel_name) {
+  *product_name = base::UTF8ToUTF16(ELECTRON_PRODUCT_NAME);
+  *version = base::UTF8ToUTF16(ELECTRON_VERSION_STRING);
+}
+#endif
+
+#if defined(OS_WIN)
+bool ElectronCrashReporterClient::GetCrashDumpLocation(
+    base::string16* crash_dir_str) {
+  base::FilePath crash_dir;
+  if (!base::PathService::Get(electron::DIR_CRASH_DUMPS, &crash_dir))
+    return false;
+  *crash_dir_str = crash_dir.value();
+  return true;
+}
+#else
+bool ElectronCrashReporterClient::GetCrashDumpLocation(
+    base::FilePath* crash_dir) {
+  return base::PathService::Get(electron::DIR_CRASH_DUMPS, crash_dir);
+}
+#endif
+
+#if defined(OS_MACOSX) || defined(OS_LINUX)
+bool ElectronCrashReporterClient::GetCrashMetricsLocation(
+    base::FilePath* metrics_dir) {
+  return base::PathService::Get(electron::DIR_USER_DATA, metrics_dir);
+}
+#endif  // OS_MACOSX || OS_LINUX
+
+bool ElectronCrashReporterClient::IsRunningUnattended() {
+  return false;
+}
+
+bool ElectronCrashReporterClient::GetCollectStatsConsent() {
+  return collect_stats_consent_;
+}
+
+#if defined(OS_MACOSX)
+bool ElectronCrashReporterClient::ReportingIsEnforcedByPolicy(
+    bool* breakpad_enabled) {
+  return false;
+}
+#endif
+
+bool ElectronCrashReporterClient::GetShouldRateLimit() {
+  return rate_limit_;
+}
+
+bool ElectronCrashReporterClient::GetShouldCompressUploads() {
+  return compress_uploads_;
+}
+
+void ElectronCrashReporterClient::GetProcessSimpleAnnotations(
+    std::map<std::string, std::string>* annotations) {
+  *annotations = global_annotations_;
+  (*annotations)["prod"] = ELECTRON_PRODUCT_NAME;
+  (*annotations)["ver"] = ELECTRON_VERSION_STRING;
+}
+
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+bool ElectronCrashReporterClient::ShouldMonitorCrashHandlerExpensively() {
+  return false;
+}
+#endif  // OS_LINUX
+
+bool ElectronCrashReporterClient::GetUploadUrl(std::string* url) {
+  *url = upload_url_;
+  return true;
+}
+
+bool ElectronCrashReporterClient::EnableBreakpadForProcess(
+    const std::string& process_type) {
+  return process_type == switches::kRendererProcess ||
+         process_type == switches::kPpapiPluginProcess ||
+         process_type == service_manager::switches::kZygoteProcess ||
+         process_type == switches::kGpuProcess ||
+         process_type == switches::kUtilityProcess || process_type == "node";
+}

+ 96 - 0
shell/app/electron_crash_reporter_client.h

@@ -0,0 +1,96 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_APP_ELECTRON_CRASH_REPORTER_CLIENT_H_
+#define SHELL_APP_ELECTRON_CRASH_REPORTER_CLIENT_H_
+
+#include <map>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "build/build_config.h"
+#include "components/crash/core/app/crash_reporter_client.h"
+
+class ElectronCrashReporterClient : public crash_reporter::CrashReporterClient {
+ public:
+  static void Create();
+
+  static ElectronCrashReporterClient* Get();
+  void SetCollectStatsConsent(bool upload_allowed);
+  void SetUploadUrl(const std::string& url);
+  void SetShouldRateLimit(bool rate_limit);
+  void SetShouldCompressUploads(bool compress_uploads);
+  void SetGlobalAnnotations(
+      const std::map<std::string, std::string>& annotations);
+
+  // crash_reporter::CrashReporterClient implementation.
+#if defined(OS_LINUX)
+  void SetCrashReporterClientIdFromGUID(
+      const std::string& client_guid) override;
+  void GetProductNameAndVersion(const char** product_name,
+                                const char** version) override;
+  void GetProductNameAndVersion(std::string* product_name,
+                                std::string* version,
+                                std::string* channel) override;
+  base::FilePath GetReporterLogFilename() override;
+#endif
+
+#if defined(OS_WIN)
+  void GetProductNameAndVersion(const base::string16& exe_path,
+                                base::string16* product_name,
+                                base::string16* version,
+                                base::string16* special_build,
+                                base::string16* channel_name) override;
+#endif
+
+#if defined(OS_WIN)
+  bool GetCrashDumpLocation(base::string16* crash_dir) override;
+#else
+  bool GetCrashDumpLocation(base::FilePath* crash_dir) override;
+#endif
+
+#if defined(OS_MACOSX) || defined(OS_LINUX)
+  bool GetCrashMetricsLocation(base::FilePath* metrics_dir) override;
+#endif
+
+  bool IsRunningUnattended() override;
+
+  bool GetCollectStatsConsent() override;
+
+  bool GetShouldRateLimit() override;
+  bool GetShouldCompressUploads() override;
+
+  void GetProcessSimpleAnnotations(
+      std::map<std::string, std::string>* annotations) override;
+
+#if defined(OS_MACOSX)
+  bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled) override;
+#endif
+
+#if defined(OS_MACOSX) || defined(OS_LINUX)
+  bool ShouldMonitorCrashHandlerExpensively() override;
+#endif
+
+  bool EnableBreakpadForProcess(const std::string& process_type) override;
+
+  bool GetUploadUrl(std::string* url) override;
+
+ private:
+  friend class base::NoDestructor<ElectronCrashReporterClient>;
+
+  std::string upload_url_;
+  bool collect_stats_consent_;
+  bool rate_limit_ = false;
+  bool compress_uploads_ = false;
+  std::map<std::string, std::string> global_annotations_;
+
+  ElectronCrashReporterClient();
+  ~ElectronCrashReporterClient() override;
+
+  DISALLOW_COPY_AND_ASSIGN(ElectronCrashReporterClient);
+};
+
+#endif  // SHELL_APP_ELECTRON_CRASH_REPORTER_CLIENT_H_

+ 57 - 5
shell/app/electron_main.cc

@@ -7,6 +7,8 @@
 #include <algorithm>
 #include <cstdlib>
 #include <memory>
+#include <string>
+#include <utility>
 #include <vector>
 
 #if defined(OS_WIN)
@@ -21,11 +23,15 @@
 #include "base/process/launch.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/windows_version.h"
+#include "components/browser_watcher/exit_code_watcher_win.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/run_as_crashpad_handler_win.h"
 #include "content/public/app/sandbox_helper_win.h"
 #include "sandbox/win/src/sandbox_types.h"
 #include "shell/app/command_line_args.h"
 #include "shell/app/electron_main_delegate.h"
-#include "shell/common/crash_reporter/win/crash_service_main.h"
+#include "third_party/crashpad/crashpad/util/win/initial_client_data.h"
+
 #elif defined(OS_LINUX)  // defined(OS_WIN)
 #include <unistd.h>
 #include <cstdio>
@@ -51,6 +57,13 @@
 
 namespace {
 
+#if defined(OS_WIN)
+// Redefined here so we don't have to introduce a dependency on //content
+// from //electron:electron_app
+const char kUserDataDir[] = "user-data-dir";
+const char kProcessType[] = "type";
+#endif
+
 ALLOW_UNUSED_TYPE bool IsEnvSet(const char* name) {
 #if defined(OS_WIN)
   size_t required_size;
@@ -138,10 +151,49 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
 #endif
 
   base::CommandLine::Init(argv.size(), argv.data());
-  const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
-  if (cmd_line.GetSwitchValueASCII("type") ==
-      crash_reporter::kCrashpadProcess) {
-    return crash_service::Main(&argv);
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+
+  const std::string process_type =
+      command_line->GetSwitchValueASCII(kProcessType);
+
+  if (process_type == crash_reporter::switches::kCrashpadHandler) {
+    // Check if we should monitor the exit code of this process
+    std::unique_ptr<browser_watcher::ExitCodeWatcher> exit_code_watcher;
+
+    // Retrieve the client process from the command line
+    crashpad::InitialClientData initial_client_data;
+    if (initial_client_data.InitializeFromString(
+            command_line->GetSwitchValueASCII("initial-client-data"))) {
+      // Setup exit code watcher to monitor the parent process
+      HANDLE duplicate_handle = INVALID_HANDLE_VALUE;
+      if (DuplicateHandle(
+              ::GetCurrentProcess(), initial_client_data.client_process(),
+              ::GetCurrentProcess(), &duplicate_handle,
+              PROCESS_QUERY_INFORMATION, FALSE, DUPLICATE_SAME_ACCESS)) {
+        base::Process parent_process(duplicate_handle);
+        exit_code_watcher =
+            std::make_unique<browser_watcher::ExitCodeWatcher>();
+        if (exit_code_watcher->Initialize(std::move(parent_process))) {
+          exit_code_watcher->StartWatching();
+        }
+      }
+    }
+
+    // The handler process must always be passed the user data dir on the
+    // command line.
+    DCHECK(command_line->HasSwitch(kUserDataDir));
+
+    base::FilePath user_data_dir =
+        command_line->GetSwitchValuePath(kUserDataDir);
+    int crashpad_status = crash_reporter::RunAsCrashpadHandler(
+        *command_line, user_data_dir, kProcessType, kUserDataDir);
+    if (crashpad_status != 0 && exit_code_watcher) {
+      // Crashpad failed to initialize, explicitly stop the exit code watcher
+      // so the crashpad-handler process can exit with an error
+      exit_code_watcher->StopWatching();
+    }
+    return crashpad_status;
   }
 
   if (!electron::CheckCommandLineArguments(arguments.argc, arguments.argv))

+ 104 - 13
shell/app/electron_main_delegate.cc

@@ -15,11 +15,16 @@
 #include "base/command_line.h"
 #include "base/debug/stack_trace.h"
 #include "base/environment.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/mac/bundle_locations.h"
 #include "base/path_service.h"
+#include "base/strings/string_split.h"
 #include "chrome/common/chrome_paths.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
+#include "components/crash/core/app/crashpad.h"
+#include "components/crash/core/common/crash_key.h"
+#include "components/crash/core/common/crash_keys.h"
 #include "content/public/common/content_switches.h"
 #include "electron/buildflags/buildflags.h"
 #include "extensions/common/constants.h"
@@ -28,10 +33,14 @@
 #include "services/service_manager/sandbox/switches.h"
 #include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
 #include "shell/app/electron_content_client.h"
+#include "shell/app/electron_crash_reporter_client.h"
+#include "shell/browser/api/electron_api_crash_reporter.h"
 #include "shell/browser/electron_browser_client.h"
 #include "shell/browser/electron_gpu_client.h"
 #include "shell/browser/feature_list.h"
 #include "shell/browser/relauncher.h"
+#include "shell/common/crash_keys.h"
+#include "shell/common/electron_paths.h"
 #include "shell/common/options_switches.h"
 #include "shell/renderer/electron_renderer_client.h"
 #include "shell/renderer/electron_sandboxed_renderer_client.h"
@@ -46,9 +55,13 @@
 
 #if defined(OS_WIN)
 #include "base/win/win_util.h"
-#if defined(_WIN64)
-#include "shell/common/crash_reporter/crash_reporter_win.h"
+#include "chrome/child/v8_crashpad_support_win.h"
 #endif
+
+#if defined(OS_LINUX)
+#include "components/crash/core/app/breakpad_linux.h"
+#include "v8/include/v8-wasm-trap-handler-posix.h"
+#include "v8/include/v8.h"
 #endif
 
 namespace electron {
@@ -71,7 +84,7 @@ bool IsSandboxEnabled(base::CommandLine* command_line) {
 // and resources loaded.
 bool SubprocessNeedsResourceBundle(const std::string& process_type) {
   return
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#if defined(OS_LINUX)
       // The zygote process opens the resources for the renderers.
       process_type == service_manager::switches::kZygoteProcess ||
 #endif
@@ -98,6 +111,41 @@ void InvalidParameterHandler(const wchar_t*,
 
 }  // namespace
 
+// TODO(nornagon): move path provider overriding to its own file in
+// shell/common
+namespace electron {
+
+bool GetDefaultCrashDumpsPath(base::FilePath* path) {
+  base::FilePath cur;
+  if (!base::PathService::Get(DIR_USER_DATA, &cur))
+    return false;
+#if defined(OS_MACOSX) || defined(OS_WIN)
+  cur = cur.Append(FILE_PATH_LITERAL("Crashpad"));
+#else
+  cur = cur.Append(FILE_PATH_LITERAL("Crash Reports"));
+#endif
+  // TODO(bauerb): http://crbug.com/259796
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+  if (!base::PathExists(cur) && !base::CreateDirectory(cur))
+    return false;
+  *path = cur;
+  return true;
+}
+
+bool ElectronPathProvider(int key, base::FilePath* path) {
+  if (key == DIR_CRASH_DUMPS) {
+    return GetDefaultCrashDumpsPath(path);
+  }
+  return false;
+}
+
+void RegisterPathProvider() {
+  base::PathService::RegisterProvider(ElectronPathProvider, PATH_START,
+                                      PATH_END);
+}
+
+}  // namespace electron
+
 void LoadResourceBundle(const std::string& locale) {
   const bool initialized = ui::ResourceBundle::HasSharedInstance();
   if (initialized)
@@ -134,9 +182,7 @@ bool ElectronMainDelegate::BasicStartupComplete(int* exit_code) {
 
   logging::LoggingSettings settings;
 #if defined(OS_WIN)
-#if defined(_WIN64)
-  crash_reporter::CrashReporterWin::SetUnhandledExceptionFilter();
-#endif
+  v8_crashpad_support::SetUp();
 
   // On Windows the terminal returns immediately, so we add a new line to
   // prevent output in the same line as the prompt.
@@ -185,6 +231,8 @@ bool ElectronMainDelegate::BasicStartupComplete(int* exit_code) {
       tracing::TracingSamplerProfiler::CreateOnMainThread();
 
   chrome::RegisterPathProvider();
+  electron::RegisterPathProvider();
+
 #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
   ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes(
       kNonWildcardDomainNonPortSchemes, kNonWildcardDomainNonPortSchemesSize);
@@ -272,6 +320,10 @@ void ElectronMainDelegate::PreSandboxStartup() {
   std::string process_type =
       command_line->GetSwitchValueASCII(::switches::kProcessType);
 
+#if !defined(MAS_BUILD)
+  crash_reporter::InitializeCrashKeys();
+#endif
+
   // Initialize ResourceBundle which handles files loaded from external
   // sources. The language should have been passed in to us from the
   // browser process as a command line flag.
@@ -280,17 +332,40 @@ void ElectronMainDelegate::PreSandboxStartup() {
     LoadResourceBundle(locale);
   }
 
-  // Only append arguments for browser process.
-  if (!IsBrowserProcess(command_line))
-    return;
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(MAS_BUILD))
+  // In the main process, we wait for JS to call crashReporter.start() before
+  // initializing crashpad. If we're in the renderer, we want to initialize it
+  // immediately at boot.
+  if (!process_type.empty()) {
+    ElectronCrashReporterClient::Create();
+    crash_reporter::InitializeCrashpad(false, process_type);
+  }
+#endif
+
+#if defined(OS_LINUX)
+  if (process_type != service_manager::switches::kZygoteProcess &&
+      !process_type.empty()) {
+    ElectronCrashReporterClient::Create();
+    breakpad::InitCrashReporter(process_type);
+  }
+#endif
+
+#if !defined(MAS_BUILD)
+  crash_keys::SetCrashKeysFromCommandLine(*command_line);
+  crash_keys::SetPlatformCrashKey();
+#endif
+
+  if (IsBrowserProcess(command_line)) {
+    // Only append arguments for browser process.
 
-  // Allow file:// URIs to read other file:// URIs by default.
-  command_line->AppendSwitch(::switches::kAllowFileAccessFromFiles);
+    // Allow file:// URIs to read other file:// URIs by default.
+    command_line->AppendSwitch(::switches::kAllowFileAccessFromFiles);
 
 #if defined(OS_MACOSX)
-  // Enable AVFoundation.
-  command_line->AppendSwitch("enable-avfoundation");
+    // Enable AVFoundation.
+    command_line->AppendSwitch("enable-avfoundation");
 #endif
+  }
 }
 
 void ElectronMainDelegate::PreCreateMainMessageLoop() {
@@ -350,4 +425,20 @@ bool ElectronMainDelegate::ShouldLockSchemeRegistry() {
   return false;
 }
 
+#if defined(OS_LINUX)
+void ElectronMainDelegate::ZygoteForked() {
+  // Needs to be called after we have DIR_USER_DATA.  BrowserMain sets
+  // this up for the browser process in a different manner.
+  ElectronCrashReporterClient::Create();
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  std::string process_type =
+      command_line->GetSwitchValueASCII(::switches::kProcessType);
+  breakpad::InitCrashReporter(process_type);
+
+  // Reset the command line for the newly spawned process.
+  crash_keys::SetCrashKeysFromCommandLine(*command_line);
+}
+#endif  // defined(OS_LINUX)
+
 }  // namespace electron

+ 3 - 0
shell/app/electron_main_delegate.h

@@ -41,6 +41,9 @@ class ElectronMainDelegate : public content::ContentMainDelegate {
       const content::MainFunctionParams& main_function_params) override;
   bool ShouldCreateFeatureList() override;
   bool ShouldLockSchemeRegistry() override;
+#if defined(OS_LINUX)
+  void ZygoteForked() override;
+#endif
 
  private:
 #if defined(OS_MACOSX)

+ 71 - 18
shell/app/node_main.cc

@@ -4,29 +4,39 @@
 
 #include "shell/app/node_main.h"
 
+#include <map>
 #include <memory>
 #include <string>
 #include <utility>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/crash/core/app/crashpad.h"
+#include "content/public/common/content_switches.h"
 #include "electron/electron_version.h"
 #include "gin/array_buffer.h"
 #include "gin/public/isolate_holder.h"
 #include "gin/v8_initializer.h"
+#include "shell/app/electron_crash_reporter_client.h"
 #include "shell/app/uv_task_runner.h"
+#include "shell/browser/api/electron_api_crash_reporter.h"
 #include "shell/browser/javascript_environment.h"
 #include "shell/browser/node_debugger.h"
 #include "shell/common/api/electron_bindings.h"
-#include "shell/common/crash_reporter/crash_reporter.h"
+#include "shell/common/crash_keys.h"
 #include "shell/common/gin_helper/dictionary.h"
 #include "shell/common/node_bindings.h"
 #include "shell/common/node_includes.h"
 
-#if defined(_WIN64)
-#include "shell/common/crash_reporter/crash_reporter_win.h"
+#if defined(OS_LINUX)
+#include "components/crash/core/app/breakpad_linux.h"
+#endif
+
+#if defined(OS_WIN)
+#include "chrome/child/v8_crashpad_support_win.h"
 #endif
 
 namespace {
@@ -42,19 +52,64 @@ bool AllowWasmCodeGenerationCallback(v8::Local<v8::Context> context,
 
 namespace electron {
 
-#if !defined(OS_LINUX)
-void AddExtraParameter(const std::string& key, const std::string& value) {
-  crash_reporter::CrashReporter::GetInstance()->AddExtraParameter(key, value);
+#if defined(OS_LINUX)
+void CrashReporterStart(gin_helper::Dictionary options) {
+  std::string submit_url;
+  bool upload_to_server = true;
+  bool ignore_system_crash_handler = false;
+  bool rate_limit = false;
+  bool compress = false;
+  std::map<std::string, std::string> global_extra;
+  std::map<std::string, std::string> extra;
+  options.Get("submitURL", &submit_url);
+  options.Get("uploadToServer", &upload_to_server);
+  options.Get("ignoreSystemCrashHandler", &ignore_system_crash_handler);
+  options.Get("rateLimit", &rate_limit);
+  options.Get("compress", &compress);
+  options.Get("extra", &extra);
+  options.Get("globalExtra", &global_extra);
+
+  std::string product_name;
+  if (options.Get("productName", &product_name))
+    global_extra["_productName"] = product_name;
+  std::string company_name;
+  if (options.Get("companyName", &company_name))
+    global_extra["_companyName"] = company_name;
+  api::crash_reporter::Start(submit_url, upload_to_server,
+                             ignore_system_crash_handler, rate_limit, compress,
+                             global_extra, extra, true);
 }
+#endif
 
-void RemoveExtraParameter(const std::string& key) {
-  crash_reporter::CrashReporter::GetInstance()->RemoveExtraParameter(key);
-}
+v8::Local<v8::Value> GetParameters(v8::Isolate* isolate) {
+  std::map<std::string, std::string> keys;
+#if !defined(MAS_BUILD)
+  electron::crash_keys::GetCrashKeys(&keys);
 #endif
+  return gin::ConvertToV8(isolate, keys);
+}
 
 int NodeMain(int argc, char* argv[]) {
   base::CommandLine::Init(argc, argv);
 
+#if defined(OS_WIN)
+  v8_crashpad_support::SetUp();
+#endif
+
+#if !defined(MAS_BUILD)
+  ElectronCrashReporterClient::Create();
+#endif
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(MAS_BUILD))
+  crash_reporter::InitializeCrashpad(false, "node");
+#endif
+
+#if !defined(MAS_BUILD)
+  crash_keys::SetCrashKeysFromCommandLine(
+      *base::CommandLine::ForCurrentProcess());
+  crash_keys::SetPlatformCrashKey();
+#endif
+
   int exit_code = 1;
   {
     // Feed gin::PerIsolateData with a task runner.
@@ -68,10 +123,6 @@ int NodeMain(int argc, char* argv[]) {
     feature_list->InitializeFromCommandLine("", "");
     base::FeatureList::SetInstance(std::move(feature_list));
 
-#if defined(_WIN64)
-    crash_reporter::CrashReporterWin::SetUnhandledExceptionFilter();
-#endif
-
     // Explicitly register electron's builtin modules.
     NodeBindings::RegisterBuiltinModules();
 
@@ -122,13 +173,15 @@ int NodeMain(int argc, char* argv[]) {
 
     // Setup process.crashReporter.start in child node processes
     gin_helper::Dictionary reporter = gin::Dictionary::CreateEmpty(isolate);
-    reporter.SetMethod("start", &crash_reporter::CrashReporter::StartInstance);
-
-#if !defined(OS_LINUX)
-    reporter.SetMethod("addExtraParameter", &AddExtraParameter);
-    reporter.SetMethod("removeExtraParameter", &RemoveExtraParameter);
+#if defined(OS_LINUX)
+    reporter.SetMethod("start", &CrashReporterStart);
 #endif
 
+    reporter.SetMethod("getParameters", &GetParameters);
+    reporter.SetMethod("addExtraParameter", &electron::crash_keys::SetCrashKey);
+    reporter.SetMethod("removeExtraParameter",
+                       &electron::crash_keys::ClearCrashKey);
+
     process.Set("crashReporter", reporter);
 
     gin_helper::Dictionary versions;

+ 3 - 1
shell/browser/api/electron_api_app.cc

@@ -39,11 +39,11 @@
 #include "shell/browser/api/gpuinfo_manager.h"
 #include "shell/browser/electron_browser_context.h"
 #include "shell/browser/electron_browser_main_parts.h"
-#include "shell/browser/electron_paths.h"
 #include "shell/browser/login_handler.h"
 #include "shell/browser/relauncher.h"
 #include "shell/common/application_info.h"
 #include "shell/common/electron_command_line.h"
+#include "shell/common/electron_paths.h"
 #include "shell/common/gin_converters/callback_converter.h"
 #include "shell/common/gin_converters/file_path_converter.h"
 #include "shell/common/gin_converters/gurl_converter.h"
@@ -403,6 +403,8 @@ int GetPathConstant(const std::string& name) {
     return DIR_USER_CACHE;
   else if (name == "logs")
     return DIR_APP_LOGS;
+  else if (name == "crashDumps")
+    return DIR_CRASH_DUMPS;
   else if (name == "home")
     return base::DIR_HOME;
   else if (name == "temp")

+ 1 - 1
shell/browser/api/electron_api_app_mac.mm

@@ -6,7 +6,7 @@
 
 #include "base/path_service.h"
 #include "shell/browser/api/electron_api_app.h"
-#include "shell/browser/electron_paths.h"
+#include "shell/common/electron_paths.h"
 
 #import <Cocoa/Cocoa.h>
 

+ 1 - 17
shell/browser/api/electron_api_auto_updater.cc

@@ -9,28 +9,12 @@
 #include "shell/browser/native_window.h"
 #include "shell/browser/window_list.h"
 #include "shell/common/gin_converters/callback_converter.h"
+#include "shell/common/gin_converters/time_converter.h"
 #include "shell/common/gin_helper/dictionary.h"
 #include "shell/common/gin_helper/event_emitter_caller.h"
 #include "shell/common/gin_helper/object_template_builder.h"
 #include "shell/common/node_includes.h"
 
-namespace gin {
-
-template <>
-struct Converter<base::Time> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                   const base::Time& val) {
-    v8::MaybeLocal<v8::Value> date =
-        v8::Date::New(isolate->GetCurrentContext(), val.ToJsTime());
-    if (date.IsEmpty())
-      return v8::Null(isolate);
-    else
-      return date.ToLocalChecked();
-  }
-};
-
-}  // namespace gin
-
 namespace electron {
 
 namespace api {

+ 216 - 0
shell/browser/api/electron_api_crash_reporter.cc

@@ -0,0 +1,216 @@
+// Copyright (c) 2013 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "shell/browser/api/electron_api_crash_reporter.h"
+
+#include <limits>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/no_destructor.h"
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/crash_upload_list/crash_upload_list_crashpad.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/crash/core/app/crashpad.h"
+#include "components/crash/core/common/crash_key.h"
+#include "components/upload_list/crash_upload_list.h"
+#include "components/upload_list/text_log_upload_list.h"
+#include "content/public/common/content_switches.h"
+#include "gin/arguments.h"
+#include "gin/data_object_builder.h"
+#include "services/service_manager/embedder/switches.h"
+#include "shell/app/electron_crash_reporter_client.h"
+#include "shell/common/crash_keys.h"
+#include "shell/common/electron_paths.h"
+#include "shell/common/gin_converters/callback_converter.h"
+#include "shell/common/gin_converters/file_path_converter.h"
+#include "shell/common/gin_converters/time_converter.h"
+#include "shell/common/gin_helper/dictionary.h"
+#include "shell/common/node_includes.h"
+#include "third_party/crashpad/crashpad/client/crashpad_info.h"
+
+#if defined(OS_LINUX)
+#include "components/crash/core/app/breakpad_linux.h"
+#include "v8/include/v8-wasm-trap-handler-posix.h"
+#include "v8/include/v8.h"
+#endif
+
+namespace {
+
+#if defined(OS_LINUX)
+std::map<std::string, std::string>& GetGlobalCrashKeysMutable() {
+  static base::NoDestructor<std::map<std::string, std::string>>
+      global_crash_keys;
+  return *global_crash_keys;
+}
+#endif  // defined(OS_LINUX)
+
+bool g_crash_reporter_initialized = false;
+
+}  // namespace
+
+namespace electron {
+
+namespace api {
+
+namespace crash_reporter {
+
+bool IsCrashReporterEnabled() {
+  return g_crash_reporter_initialized;
+}
+
+#if defined(OS_LINUX)
+const std::map<std::string, std::string>& GetGlobalCrashKeys() {
+  return GetGlobalCrashKeysMutable();
+}
+#endif
+
+void Start(const std::string& submit_url,
+           bool upload_to_server,
+           bool ignore_system_crash_handler,
+           bool rate_limit,
+           bool compress,
+           const std::map<std::string, std::string>& global_extra,
+           const std::map<std::string, std::string>& extra,
+           bool is_node_process) {
+#if !defined(MAS_BUILD)
+  if (g_crash_reporter_initialized)
+    return;
+  g_crash_reporter_initialized = true;
+  ElectronCrashReporterClient::Create();
+  ElectronCrashReporterClient::Get()->SetUploadUrl(submit_url);
+  ElectronCrashReporterClient::Get()->SetCollectStatsConsent(upload_to_server);
+  ElectronCrashReporterClient::Get()->SetShouldRateLimit(rate_limit);
+  ElectronCrashReporterClient::Get()->SetShouldCompressUploads(compress);
+  ElectronCrashReporterClient::Get()->SetGlobalAnnotations(global_extra);
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  std::string process_type =
+      is_node_process
+          ? "node"
+          : command_line->GetSwitchValueASCII(::switches::kProcessType);
+#if defined(OS_LINUX)
+  auto& global_crash_keys = GetGlobalCrashKeysMutable();
+  for (const auto& pair : global_extra) {
+    global_crash_keys[pair.first] = pair.second;
+  }
+  for (const auto& pair : extra)
+    electron::crash_keys::SetCrashKey(pair.first, pair.second);
+  for (const auto& pair : global_extra)
+    electron::crash_keys::SetCrashKey(pair.first, pair.second);
+  breakpad::InitCrashReporter(process_type);
+#elif defined(OS_MACOSX)
+  for (const auto& pair : extra)
+    electron::crash_keys::SetCrashKey(pair.first, pair.second);
+  ::crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
+  if (ignore_system_crash_handler) {
+    crashpad::CrashpadInfo::GetCrashpadInfo()
+        ->set_system_crash_reporter_forwarding(crashpad::TriState::kDisabled);
+  }
+#elif defined(OS_WIN)
+  for (const auto& pair : extra)
+    electron::crash_keys::SetCrashKey(pair.first, pair.second);
+  base::FilePath user_data_dir;
+  base::PathService::Get(DIR_USER_DATA, &user_data_dir);
+  ::crash_reporter::InitializeCrashpadWithEmbeddedHandler(
+      process_type.empty(), process_type,
+      base::UTF16ToUTF8(user_data_dir.value()), base::FilePath());
+#endif
+#endif
+}
+
+}  // namespace crash_reporter
+
+}  // namespace api
+
+}  // namespace electron
+
+namespace {
+
+#if defined(MAS_BUILD)
+void GetUploadedReports(
+    base::OnceCallback<void(v8::Local<v8::Value>)> callback) {
+  std::move(callback).Run(v8::Array::New(v8::Isolate::GetCurrent()));
+}
+#else
+scoped_refptr<UploadList> CreateCrashUploadList() {
+#if defined(OS_MACOSX) || defined(OS_WIN)
+  return new CrashUploadListCrashpad();
+#else
+  base::FilePath crash_dir_path;
+  base::PathService::Get(electron::DIR_CRASH_DUMPS, &crash_dir_path);
+  base::FilePath upload_log_path =
+      crash_dir_path.AppendASCII(CrashUploadList::kReporterLogFilename);
+  return new TextLogUploadList(upload_log_path);
+#endif  // defined(OS_MACOSX) || defined(OS_WIN)
+}
+
+v8::Local<v8::Value> GetUploadedReports(v8::Isolate* isolate) {
+  auto list = CreateCrashUploadList();
+  // TODO(nornagon): switch to using Load() instead of LoadSync() once the
+  // synchronous version of getUploadedReports is deprecated so we can remove
+  // our patch.
+  {
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    list->LoadSync();
+  }
+  std::vector<UploadList::UploadInfo> uploads;
+  constexpr size_t kMaxUploadReportsToList = std::numeric_limits<size_t>::max();
+  list->GetUploads(kMaxUploadReportsToList, &uploads);
+  std::vector<v8::Local<v8::Object>> result;
+  for (const auto& upload : uploads) {
+    result.push_back(gin::DataObjectBuilder(isolate)
+                         .Set("date", upload.upload_time)
+                         .Set("id", upload.upload_id)
+                         .Build());
+  }
+  v8::Local<v8::Value> v8_result = gin::ConvertToV8(isolate, result);
+  return v8_result;
+}
+#endif
+
+void SetUploadToServer(bool upload) {
+#if !defined(MAS_BUILD)
+  ElectronCrashReporterClient::Get()->SetCollectStatsConsent(upload);
+#endif
+}
+
+bool GetUploadToServer() {
+#if defined(MAS_BUILD)
+  return false;
+#else
+  return ElectronCrashReporterClient::Get()->GetCollectStatsConsent();
+#endif
+}
+
+v8::Local<v8::Value> GetParameters(v8::Isolate* isolate) {
+  std::map<std::string, std::string> keys;
+#if !defined(MAS_BUILD)
+  electron::crash_keys::GetCrashKeys(&keys);
+#endif
+  return gin::ConvertToV8(isolate, keys);
+}
+
+void Initialize(v8::Local<v8::Object> exports,
+                v8::Local<v8::Value> unused,
+                v8::Local<v8::Context> context,
+                void* priv) {
+  gin_helper::Dictionary dict(context->GetIsolate(), exports);
+  dict.SetMethod("start", &electron::api::crash_reporter::Start);
+  dict.SetMethod("addExtraParameter", &electron::crash_keys::SetCrashKey);
+  dict.SetMethod("removeExtraParameter", &electron::crash_keys::ClearCrashKey);
+  dict.SetMethod("getParameters", &GetParameters);
+  dict.SetMethod("getUploadedReports", &GetUploadedReports);
+  dict.SetMethod("setUploadToServer", &SetUploadToServer);
+  dict.SetMethod("getUploadToServer", &GetUploadToServer);
+}
+
+}  // namespace
+
+NODE_LINKED_MODULE_CONTEXT_AWARE(atom_browser_crash_reporter, Initialize)

+ 40 - 0
shell/browser/api/electron_api_crash_reporter.h

@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Slack Technologies, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_BROWSER_API_ELECTRON_API_CRASH_REPORTER_H_
+#define SHELL_BROWSER_API_ELECTRON_API_CRASH_REPORTER_H_
+
+#include <map>
+#include <string>
+#include "base/files/file_path.h"
+
+namespace electron {
+
+namespace api {
+
+namespace crash_reporter {
+
+bool IsCrashReporterEnabled();
+
+#if defined(OS_LINUX)
+const std::map<std::string, std::string>& GetGlobalCrashKeys();
+#endif
+
+// JS bindings API; exposed publicly because it's also called from node_main.cc
+void Start(const std::string& submit_url,
+           bool upload_to_server,
+           bool ignore_system_crash_handler,
+           bool rate_limit,
+           bool compress,
+           const std::map<std::string, std::string>& global_extra,
+           const std::map<std::string, std::string>& extra,
+           bool is_node_process);
+
+}  // namespace crash_reporter
+
+}  // namespace api
+
+}  // namespace electron
+
+#endif  // SHELL_BROWSER_API_ELECTRON_API_CRASH_REPORTER_H_

+ 1 - 1
shell/browser/browser.cc

@@ -17,11 +17,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "shell/browser/browser_observer.h"
 #include "shell/browser/electron_browser_main_parts.h"
-#include "shell/browser/electron_paths.h"
 #include "shell/browser/login_handler.h"
 #include "shell/browser/native_window.h"
 #include "shell/browser/window_list.h"
 #include "shell/common/application_info.h"
+#include "shell/common/electron_paths.h"
 #include "shell/common/gin_helper/arguments.h"
 
 namespace electron {

+ 101 - 1
shell/browser/electron_browser_client.cc

@@ -11,6 +11,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/environment.h"
 #include "base/files/file_util.h"
@@ -24,6 +25,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_version.h"
 #include "components/net_log/chrome_net_log.h"
 #include "components/network_hints/common/network_hints.mojom.h"
@@ -37,6 +39,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/site_instance.h"
+#include "content/public/common/content_descriptors.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_names.mojom.h"
@@ -47,6 +50,7 @@
 #include "extensions/browser/extension_navigation_ui_data.h"
 #include "extensions/browser/extension_protocols.h"
 #include "extensions/common/constants.h"
+#include "extensions/common/switches.h"
 #include "net/base/escape.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "ppapi/buildflags/buildflags.h"
@@ -58,6 +62,7 @@
 #include "services/service_manager/public/cpp/binder_map.h"
 #include "shell/app/manifests.h"
 #include "shell/browser/api/electron_api_app.h"
+#include "shell/browser/api/electron_api_crash_reporter.h"
 #include "shell/browser/api/electron_api_protocol.h"
 #include "shell/browser/api/electron_api_session.h"
 #include "shell/browser/api/electron_api_web_contents.h"
@@ -67,7 +72,6 @@
 #include "shell/browser/electron_browser_context.h"
 #include "shell/browser/electron_browser_main_parts.h"
 #include "shell/browser/electron_navigation_throttle.h"
-#include "shell/browser/electron_paths.h"
 #include "shell/browser/electron_quota_permission_context.h"
 #include "shell/browser/electron_speech_recognition_manager_delegate.h"
 #include "shell/browser/font_defaults.h"
@@ -88,6 +92,7 @@
 #include "shell/browser/window_list.h"
 #include "shell/common/api/api.mojom.h"
 #include "shell/common/application_info.h"
+#include "shell/common/electron_paths.h"
 #include "shell/common/options_switches.h"
 #include "shell/common/platform_util.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
@@ -163,6 +168,14 @@
 #include "content/public/common/child_process_host.h"
 #endif
 
+#if defined(OS_LINUX)
+#include "base/debug/leak_annotations.h"
+#include "components/crash/content/browser/crash_handler_host_linux.h"
+#include "components/crash/core/app/breakpad_linux.h"
+#include "components/crash/core/app/crash_switches.h"
+#include "components/crash/core/app/crashpad.h"
+#endif
+
 using content::BrowserThread;
 
 namespace electron {
@@ -264,6 +277,64 @@ const extensions::Extension* GetEnabledExtensionFromEffectiveURL(
 }
 #endif  // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
 
+#if defined(OS_LINUX)
+breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost(
+    const std::string& process_type) {
+  base::FilePath dumps_path;
+  base::PathService::Get(electron::DIR_CRASH_DUMPS, &dumps_path);
+  {
+    ANNOTATE_SCOPED_MEMORY_LEAK;
+    breakpad::CrashHandlerHostLinux* crash_handler =
+        new breakpad::CrashHandlerHostLinux(process_type, dumps_path, true);
+    crash_handler->StartUploaderThread();
+    return crash_handler;
+  }
+}
+
+int GetCrashSignalFD(const base::CommandLine& command_line) {
+  // Extensions have the same process type as renderers.
+  if (command_line.HasSwitch(extensions::switches::kExtensionProcess)) {
+    static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
+    if (!crash_handler)
+      crash_handler = CreateCrashHandlerHost("extension");
+    return crash_handler->GetDeathSignalSocket();
+  }
+
+  std::string process_type =
+      command_line.GetSwitchValueASCII(::switches::kProcessType);
+
+  if (process_type == ::switches::kRendererProcess) {
+    static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
+    if (!crash_handler)
+      crash_handler = CreateCrashHandlerHost(process_type);
+    return crash_handler->GetDeathSignalSocket();
+  }
+
+  if (process_type == ::switches::kPpapiPluginProcess) {
+    static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
+    if (!crash_handler)
+      crash_handler = CreateCrashHandlerHost(process_type);
+    return crash_handler->GetDeathSignalSocket();
+  }
+
+  if (process_type == ::switches::kGpuProcess) {
+    static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
+    if (!crash_handler)
+      crash_handler = CreateCrashHandlerHost(process_type);
+    return crash_handler->GetDeathSignalSocket();
+  }
+
+  if (process_type == ::switches::kUtilityProcess) {
+    static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
+    if (!crash_handler)
+      crash_handler = CreateCrashHandlerHost(process_type);
+    return crash_handler->GetDeathSignalSocket();
+  }
+
+  return -1;
+}
+#endif  // defined(OS_LINUX)
+
 }  // namespace
 
 // static
@@ -648,6 +719,23 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
   std::string process_type =
       command_line->GetSwitchValueASCII(::switches::kProcessType);
 
+#if defined(OS_LINUX)
+  bool enable_crash_reporter = false;
+  enable_crash_reporter = breakpad::IsCrashReporterEnabled();
+  if (enable_crash_reporter) {
+    command_line->AppendSwitch(::switches::kEnableCrashReporter);
+    std::string switch_value;
+    for (const auto& pair : api::crash_reporter::GetGlobalCrashKeys()) {
+      if (!switch_value.empty())
+        switch_value += ",";
+      switch_value += pair.first;
+      switch_value += "=";
+      switch_value += pair.second;
+    }
+    command_line->AppendSwitchASCII(switches::kGlobalCrashKeys, switch_value);
+  }
+#endif
+
   if (process_type == ::switches::kUtilityProcess ||
       process_type == ::switches::kRendererProcess) {
     // Copy following switches to child process.
@@ -1533,6 +1621,18 @@ void ElectronBrowserClient::RegisterBrowserInterfaceBindersForFrame(
 #endif
 }
 
+#if defined(OS_LINUX)
+void ElectronBrowserClient::GetAdditionalMappedFilesForChildProcess(
+    const base::CommandLine& command_line,
+    int child_process_id,
+    content::PosixFileDescriptorInfo* mappings) {
+  int crash_signal_fd = GetCrashSignalFD(command_line);
+  if (crash_signal_fd >= 0) {
+    mappings->Share(service_manager::kCrashDumpSignal, crash_signal_fd);
+  }
+}
+#endif
+
 std::unique_ptr<content::LoginDelegate>
 ElectronBrowserClient::CreateLoginDelegate(
     const net::AuthChallengeInfo& auth_info,

+ 6 - 0
shell/browser/electron_browser_client.h

@@ -71,6 +71,12 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
       content::RenderFrameHost* render_frame_host,
       service_manager::BinderMapWithContext<content::RenderFrameHost*>* map)
       override;
+#if defined(OS_LINUX)
+  void GetAdditionalMappedFilesForChildProcess(
+      const base::CommandLine& command_line,
+      int child_process_id,
+      content::PosixFileDescriptorInfo* mappings) override;
+#endif
 
   std::string GetUserAgent() override;
   void SetUserAgent(const std::string& user_agent);

+ 1 - 1
shell/browser/electron_browser_context.cc

@@ -38,7 +38,6 @@
 #include "shell/browser/electron_browser_client.h"
 #include "shell/browser/electron_browser_main_parts.h"
 #include "shell/browser/electron_download_manager_delegate.h"
-#include "shell/browser/electron_paths.h"
 #include "shell/browser/electron_permission_manager.h"
 #include "shell/browser/net/resolve_proxy_helper.h"
 #include "shell/browser/pref_store_delegate.h"
@@ -47,6 +46,7 @@
 #include "shell/browser/web_view_manager.h"
 #include "shell/browser/zoom_level_delegate.h"
 #include "shell/common/application_info.h"
+#include "shell/common/electron_paths.h"
 #include "shell/common/options_switches.h"
 
 #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)

+ 1 - 1
shell/browser/electron_browser_main_parts.cc

@@ -37,7 +37,6 @@
 #include "shell/browser/browser_process_impl.h"
 #include "shell/browser/electron_browser_client.h"
 #include "shell/browser/electron_browser_context.h"
-#include "shell/browser/electron_paths.h"
 #include "shell/browser/electron_web_ui_controller_factory.h"
 #include "shell/browser/feature_list.h"
 #include "shell/browser/javascript_environment.h"
@@ -47,6 +46,7 @@
 #include "shell/common/api/electron_bindings.h"
 #include "shell/common/application_info.h"
 #include "shell/common/asar/asar_util.h"
+#include "shell/common/electron_paths.h"
 #include "shell/common/gin_helper/trackable_object.h"
 #include "shell/common/node_bindings.h"
 #include "shell/common/node_includes.h"

+ 1 - 1
shell/browser/electron_browser_main_parts_mac.mm

@@ -7,9 +7,9 @@
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
 #include "base/path_service.h"
-#include "shell/browser/electron_paths.h"
 #import "shell/browser/mac/electron_application.h"
 #include "shell/browser/mac/electron_application_delegate.h"
+#include "shell/common/electron_paths.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 namespace electron {

+ 1 - 1
shell/browser/ui/devtools_manager_delegate.cc

@@ -27,7 +27,7 @@
 #include "net/base/net_errors.h"
 #include "net/socket/stream_socket.h"
 #include "net/socket/tcp_server_socket.h"
-#include "shell/browser/electron_paths.h"
+#include "shell/common/electron_paths.h"
 #include "ui/base/resource/resource_bundle.h"
 
 namespace electron {

+ 0 - 2
shell/common/api/api.mojom

@@ -12,8 +12,6 @@ interface ElectronRenderer {
       blink.mojom.CloneableMessage arguments,
       int32 sender_id);
 
-  UpdateCrashpadPipeName(string pipe_name);
-
   // This is an API specific to the "remote" module, and will ultimately be
   // replaced by generic IPC once WeakRef is generally available.
   [EnableIf=enable_remote_module]

+ 0 - 67
shell/common/api/electron_api_crash_reporter.cc

@@ -1,67 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include <map>
-#include <string>
-
-#include "base/bind.h"
-#include "gin/data_object_builder.h"
-#include "shell/common/crash_reporter/crash_reporter.h"
-#include "shell/common/gin_converters/callback_converter.h"
-#include "shell/common/gin_converters/file_path_converter.h"
-#include "shell/common/gin_helper/dictionary.h"
-
-#include "shell/common/node_includes.h"
-
-using crash_reporter::CrashReporter;
-
-namespace gin {
-
-template <>
-struct Converter<CrashReporter::UploadReportResult> {
-  static v8::Local<v8::Value> ToV8(
-      v8::Isolate* isolate,
-      const CrashReporter::UploadReportResult& reports) {
-    return gin::DataObjectBuilder(isolate)
-        .Set("date",
-             v8::Date::New(isolate->GetCurrentContext(), reports.first * 1000.0)
-                 .ToLocalChecked())
-        .Set("id", reports.second)
-        .Build();
-  }
-};
-
-}  // namespace gin
-
-namespace {
-
-void Initialize(v8::Local<v8::Object> exports,
-                v8::Local<v8::Value> unused,
-                v8::Local<v8::Context> context,
-                void* priv) {
-  auto reporter = base::Unretained(CrashReporter::GetInstance());
-  gin_helper::Dictionary dict(context->GetIsolate(), exports);
-  dict.SetMethod("start", base::BindRepeating(&CrashReporter::Start, reporter));
-  dict.SetMethod(
-      "addExtraParameter",
-      base::BindRepeating(&CrashReporter::AddExtraParameter, reporter));
-  dict.SetMethod(
-      "removeExtraParameter",
-      base::BindRepeating(&CrashReporter::RemoveExtraParameter, reporter));
-  dict.SetMethod("getParameters",
-                 base::BindRepeating(&CrashReporter::GetParameters, reporter));
-  dict.SetMethod(
-      "getUploadedReports",
-      base::BindRepeating(&CrashReporter::GetUploadedReports, reporter));
-  dict.SetMethod(
-      "setUploadToServer",
-      base::BindRepeating(&CrashReporter::SetUploadToServer, reporter));
-  dict.SetMethod(
-      "getUploadToServer",
-      base::BindRepeating(&CrashReporter::GetUploadToServer, reporter));
-}
-
-}  // namespace
-
-NODE_LINKED_MODULE_CONTEXT_AWARE(atom_common_crash_reporter, Initialize)

+ 144 - 0
shell/common/crash_keys.cc

@@ -0,0 +1,144 @@
+// Copyright (c) 2020 Slack Technologies, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "shell/common/crash_keys.h"
+
+#include <deque>
+#include <utility>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/no_destructor.h"
+#include "base/strings/string_split.h"
+#include "components/crash/core/common/crash_key.h"
+#include "content/public/common/content_switches.h"
+#include "shell/common/electron_constants.h"
+#include "shell/common/options_switches.h"
+#include "third_party/crashpad/crashpad/client/annotation.h"
+
+namespace electron {
+
+namespace crash_keys {
+
+namespace {
+
+using ExtraCrashKeys = std::deque<crash_reporter::CrashKeyString<127>>;
+ExtraCrashKeys& GetExtraCrashKeys() {
+  static base::NoDestructor<ExtraCrashKeys> extra_keys;
+  return *extra_keys;
+}
+
+std::deque<std::string>& GetExtraCrashKeyNames() {
+  static base::NoDestructor<std::deque<std::string>> crash_key_names;
+  return *crash_key_names;
+}
+
+}  // namespace
+
+constexpr uint32_t kMaxCrashKeyNameLength = 40;
+#if defined(OS_LINUX)
+static_assert(kMaxCrashKeyNameLength <=
+                  crash_reporter::internal::kCrashKeyStorageKeySize,
+              "max crash key name length above what breakpad supports");
+#else
+static_assert(kMaxCrashKeyNameLength <= crashpad::Annotation::kNameMaxLength,
+              "max crash key name length above what crashpad supports");
+#endif
+
+void SetCrashKey(const std::string& key, const std::string& value) {
+  // Chrome DCHECK()s if we try to set an annotation with a name longer than
+  // the max.
+  // TODO(nornagon): warn the developer (via console.warn) when this happens.
+  if (key.size() >= kMaxCrashKeyNameLength)
+    return;
+  auto& crash_key_names = GetExtraCrashKeyNames();
+
+  auto iter = std::find(crash_key_names.begin(), crash_key_names.end(), key);
+  if (iter == crash_key_names.end()) {
+    crash_key_names.emplace_back(key);
+    GetExtraCrashKeys().emplace_back(crash_key_names.back().c_str());
+    iter = crash_key_names.end() - 1;
+  }
+  GetExtraCrashKeys()[iter - crash_key_names.begin()].Set(value);
+}
+
+void ClearCrashKey(const std::string& key) {
+  const auto& crash_key_names = GetExtraCrashKeyNames();
+
+  auto iter = std::find(crash_key_names.begin(), crash_key_names.end(), key);
+  if (iter != crash_key_names.end()) {
+    GetExtraCrashKeys()[iter - crash_key_names.begin()].Clear();
+  }
+}
+
+void GetCrashKeys(std::map<std::string, std::string>* keys) {
+  const auto& crash_key_names = GetExtraCrashKeyNames();
+  const auto& crash_keys = GetExtraCrashKeys();
+  int i = 0;
+  for (const auto& key : crash_key_names) {
+    const auto& value = crash_keys[i++];
+    if (value.is_set()) {
+      keys->emplace(key, value.value());
+    }
+  }
+}
+
+namespace {
+bool IsRunningAsNode() {
+#if BUILDFLAG(ENABLE_RUN_AS_NODE)
+  return base::Environment::Create()->HasVar(electron::kRunAsNode);
+#else
+  return false;
+#endif
+}
+}  // namespace
+
+void SetCrashKeysFromCommandLine(const base::CommandLine& command_line) {
+#if defined(OS_LINUX)
+  if (command_line.HasSwitch(switches::kGlobalCrashKeys)) {
+    std::vector<std::pair<std::string, std::string>> global_crash_keys;
+    base::SplitStringIntoKeyValuePairs(
+        command_line.GetSwitchValueASCII(switches::kGlobalCrashKeys), '=', ',',
+        &global_crash_keys);
+    for (const auto& pair : global_crash_keys) {
+      SetCrashKey(pair.first, pair.second);
+    }
+  }
+#endif
+
+  // NB. this is redundant with the 'ptype' key that //components/crash
+  // reports; it's present for backwards compatibility.
+  static crash_reporter::CrashKeyString<16> process_type_key("process_type");
+  if (IsRunningAsNode()) {
+    process_type_key.Set("node");
+  } else {
+    std::string process_type =
+        command_line.GetSwitchValueASCII(::switches::kProcessType);
+    if (process_type.empty()) {
+      process_type_key.Set("browser");
+    } else {
+      process_type_key.Set(process_type);
+    }
+  }
+}
+
+void SetPlatformCrashKey() {
+  // TODO(nornagon): this is redundant with the 'plat' key that
+  // //components/crash already includes. Remove it.
+  static crash_reporter::CrashKeyString<8> platform_key("platform");
+#if defined(OS_WIN)
+  platform_key.Set("win32");
+#elif defined(OS_MACOSX)
+  platform_key.Set("darwin");
+#elif defined(OS_LINUX)
+  platform_key.Set("linux");
+#else
+  platform_key.Set("unknown");
+#endif
+}
+
+}  // namespace crash_keys
+
+}  // namespace electron

+ 30 - 0
shell/common/crash_keys.h

@@ -0,0 +1,30 @@
+// Copyright (c) 2020 Slack Technologies, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_COMMON_CRASH_KEYS_H_
+#define SHELL_COMMON_CRASH_KEYS_H_
+
+#include <map>
+#include <string>
+
+namespace base {
+class CommandLine;
+}
+
+namespace electron {
+
+namespace crash_keys {
+
+void SetCrashKey(const std::string& key, const std::string& value);
+void ClearCrashKey(const std::string& key);
+void GetCrashKeys(std::map<std::string, std::string>* keys);
+
+void SetCrashKeysFromCommandLine(const base::CommandLine& command_line);
+void SetPlatformCrashKey();
+
+}  // namespace crash_keys
+
+}  // namespace electron
+
+#endif  // SHELL_COMMON_CRASH_KEYS_H_

+ 0 - 152
shell/common/crash_reporter/crash_reporter.cc

@@ -1,152 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "shell/common/crash_reporter/crash_reporter.h"
-
-#include <memory>
-
-#include "base/command_line.h"
-#include "base/environment.h"
-#include "base/files/file_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/threading/thread_restrictions.h"
-#include "content/public/common/content_switches.h"
-#include "electron/electron_version.h"
-#include "shell/browser/browser.h"
-#include "shell/common/electron_constants.h"
-#include "shell/common/gin_converters/file_path_converter.h"
-#include "shell/common/gin_helper/dictionary.h"
-
-namespace crash_reporter {
-
-const char kCrashpadProcess[] = "crash-handler";
-const char kCrashesDirectoryKey[] = "crashes-directory";
-
-CrashReporter::CrashReporter() {
-#if BUILDFLAG(ENABLE_RUN_AS_NODE)
-  bool run_as_node = base::Environment::Create()->HasVar(electron::kRunAsNode);
-#else
-  bool run_as_node = false;
-#endif
-
-  if (run_as_node) {
-    process_type_ = "node";
-  } else {
-    auto* cmd = base::CommandLine::ForCurrentProcess();
-    process_type_ = cmd->GetSwitchValueASCII(switches::kProcessType);
-  }
-  // process_type_ will be empty for browser process
-}
-
-CrashReporter::~CrashReporter() = default;
-
-bool CrashReporter::IsInitialized() {
-  return is_initialized_;
-}
-
-void CrashReporter::Start(const std::string& product_name,
-                          const std::string& company_name,
-                          const std::string& submit_url,
-                          const base::FilePath& crashes_dir,
-                          bool upload_to_server,
-                          bool skip_system_crash_handler,
-                          const StringMap& extra_parameters) {
-  is_initialized_ = true;
-  SetUploadParameters(extra_parameters);
-
-  Init(product_name, company_name, submit_url, crashes_dir, upload_to_server,
-       skip_system_crash_handler);
-}
-
-void CrashReporter::SetUploadParameters(const StringMap& parameters) {
-  upload_parameters_ = parameters;
-  upload_parameters_["process_type"] =
-      process_type_.empty() ? "browser" : process_type_;
-  upload_parameters_["prod"] = ELECTRON_PRODUCT_NAME;
-  upload_parameters_["ver"] = ELECTRON_VERSION_STRING;
-
-  // Setting platform dependent parameters.
-  SetUploadParameters();
-}
-
-void CrashReporter::SetUploadToServer(const bool upload_to_server) {}
-
-bool CrashReporter::GetUploadToServer() {
-  return true;
-}
-
-std::vector<CrashReporter::UploadReportResult>
-CrashReporter::GetUploadedReports(const base::FilePath& crashes_dir) {
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
-  std::string file_content;
-  std::vector<CrashReporter::UploadReportResult> result;
-  base::FilePath uploads_path =
-      crashes_dir.Append(FILE_PATH_LITERAL("uploads.log"));
-  if (base::ReadFileToString(uploads_path, &file_content)) {
-    std::vector<std::string> reports = base::SplitString(
-        file_content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-    for (const std::string& report : reports) {
-      std::vector<std::string> report_item = base::SplitString(
-          report, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-      int report_time = 0;
-      if (report_item.size() >= 2 &&
-          base::StringToInt(report_item[0], &report_time)) {
-        result.emplace_back(report_time, report_item[1]);
-      }
-    }
-  }
-  return result;
-}
-
-void CrashReporter::Init(const std::string& product_name,
-                         const std::string& company_name,
-                         const std::string& submit_url,
-                         const base::FilePath& crashes_dir,
-                         bool auto_submit,
-                         bool skip_system_crash_handler) {}
-
-void CrashReporter::SetUploadParameters() {}
-
-void CrashReporter::AddExtraParameter(const std::string& key,
-                                      const std::string& value) {}
-
-void CrashReporter::RemoveExtraParameter(const std::string& key) {}
-
-std::map<std::string, std::string> CrashReporter::GetParameters() const {
-  return upload_parameters_;
-}
-
-#if defined(OS_MACOSX) && defined(MAS_BUILD)
-// static
-CrashReporter* CrashReporter::GetInstance() {
-  static CrashReporter crash_reporter;
-  return &crash_reporter;
-}
-#endif
-
-void CrashReporter::StartInstance(const gin_helper::Dictionary& options) {
-  auto* reporter = GetInstance();
-  if (!reporter)
-    return;
-
-  std::string product_name;
-  options.Get("productName", &product_name);
-  std::string company_name;
-  options.Get("companyName", &company_name);
-  std::string submit_url;
-  options.Get("submitURL", &submit_url);
-  base::FilePath crashes_dir;
-  options.Get("crashesDirectory", &crashes_dir);
-  StringMap extra_parameters;
-  options.Get("extra", &extra_parameters);
-
-  extra_parameters["_productName"] = product_name;
-  extra_parameters["_companyName"] = company_name;
-
-  reporter->Start(product_name, company_name, submit_url, crashes_dir, true,
-                  false, extra_parameters);
-}
-
-}  // namespace crash_reporter

+ 0 - 79
shell/common/crash_reporter/crash_reporter.h

@@ -1,79 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_
-#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_
-
-#include <map>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/macros.h"
-
-namespace gin_helper {
-class Dictionary;
-}
-
-namespace crash_reporter {
-
-extern const char kCrashpadProcess[];
-extern const char kCrashesDirectoryKey[];
-
-class CrashReporter {
- public:
-  typedef std::map<std::string, std::string> StringMap;
-  typedef std::pair<int, std::string> UploadReportResult;  // upload-date, id
-
-  static CrashReporter* GetInstance();
-  // FIXME(zcbenz): We should not do V8 in this file, this method should only
-  // accept C++ struct as parameter, and atom_api_crash_reporter.cc is
-  // responsible for parsing the parameter from JavaScript.
-  static void StartInstance(const gin_helper::Dictionary& options);
-
-  bool IsInitialized();
-  void Start(const std::string& product_name,
-             const std::string& company_name,
-             const std::string& submit_url,
-             const base::FilePath& crashes_dir,
-             bool upload_to_server,
-             bool skip_system_crash_handler,
-             const StringMap& extra_parameters);
-
-  virtual std::vector<CrashReporter::UploadReportResult> GetUploadedReports(
-      const base::FilePath& crashes_dir);
-
-  virtual void SetUploadToServer(bool upload_to_server);
-  virtual bool GetUploadToServer();
-  virtual void AddExtraParameter(const std::string& key,
-                                 const std::string& value);
-  virtual void RemoveExtraParameter(const std::string& key);
-  virtual std::map<std::string, std::string> GetParameters() const;
-
- protected:
-  CrashReporter();
-  virtual ~CrashReporter();
-
-  virtual void Init(const std::string& product_name,
-                    const std::string& company_name,
-                    const std::string& submit_url,
-                    const base::FilePath& crashes_dir,
-                    bool upload_to_server,
-                    bool skip_system_crash_handler);
-  virtual void SetUploadParameters();
-
-  StringMap upload_parameters_;
-  std::string process_type_;
-
- private:
-  bool is_initialized_ = false;
-  void SetUploadParameters(const StringMap& parameters);
-
-  DISALLOW_COPY_AND_ASSIGN(CrashReporter);
-};
-
-}  // namespace crash_reporter
-
-#endif  // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_H_

+ 0 - 118
shell/common/crash_reporter/crash_reporter_crashpad.cc

@@ -1,118 +0,0 @@
-// Copyright (c) 2019 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "shell/common/crash_reporter/crash_reporter_crashpad.h"
-
-#include <algorithm>
-#include <memory>
-
-#include "base/files/file_util.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/threading/thread_restrictions.h"
-#include "third_party/crashpad/crashpad/client/settings.h"
-
-namespace crash_reporter {
-
-CrashReporterCrashpad::CrashReporterCrashpad() {}
-
-CrashReporterCrashpad::~CrashReporterCrashpad() {}
-
-bool CrashReporterCrashpad::GetUploadToServer() {
-  bool enabled = true;
-  if (database_) {
-    database_->GetSettings()->GetUploadsEnabled(&enabled);
-  }
-  return enabled;
-}
-
-void CrashReporterCrashpad::SetUploadToServer(const bool upload_to_server) {
-  if (database_) {
-    database_->GetSettings()->SetUploadsEnabled(upload_to_server);
-  }
-}
-
-void CrashReporterCrashpad::SetCrashKeyValue(base::StringPiece key,
-                                             base::StringPiece value) {
-  simple_string_dictionary_->SetKeyValue(key.data(), value.data());
-}
-
-void CrashReporterCrashpad::SetInitialCrashKeyValues() {
-  for (const auto& upload_parameter : upload_parameters_)
-    SetCrashKeyValue(upload_parameter.first, upload_parameter.second);
-}
-
-void CrashReporterCrashpad::AddExtraParameter(const std::string& key,
-                                              const std::string& value) {
-  if (simple_string_dictionary_) {
-    SetCrashKeyValue(key, value);
-  } else {
-    upload_parameters_[key] = value;
-  }
-}
-
-void CrashReporterCrashpad::RemoveExtraParameter(const std::string& key) {
-  if (simple_string_dictionary_)
-    simple_string_dictionary_->RemoveKey(key.data());
-  else
-    upload_parameters_.erase(key);
-}
-
-std::map<std::string, std::string> CrashReporterCrashpad::GetParameters()
-    const {
-  if (simple_string_dictionary_) {
-    std::map<std::string, std::string> ret;
-    crashpad::SimpleStringDictionary::Iterator iter(*simple_string_dictionary_);
-    for (;;) {
-      auto* const entry = iter.Next();
-      if (!entry)
-        break;
-      ret[entry->key] = entry->value;
-    }
-    return ret;
-  }
-  return upload_parameters_;
-}
-
-std::vector<CrashReporter::UploadReportResult>
-CrashReporterCrashpad::GetUploadedReports(const base::FilePath& crashes_dir) {
-  std::vector<CrashReporter::UploadReportResult> uploaded_reports;
-
-  {
-    base::ThreadRestrictions::ScopedAllowIO allow_io;
-    if (!base::PathExists(crashes_dir)) {
-      return uploaded_reports;
-    }
-  }
-  // Load crashpad database.
-  std::unique_ptr<crashpad::CrashReportDatabase> database =
-      crashpad::CrashReportDatabase::Initialize(crashes_dir);
-  DCHECK(database);
-
-  std::vector<crashpad::CrashReportDatabase::Report> completed_reports;
-  crashpad::CrashReportDatabase::OperationStatus status =
-      database->GetCompletedReports(&completed_reports);
-  if (status != crashpad::CrashReportDatabase::kNoError) {
-    return uploaded_reports;
-  }
-
-  for (const crashpad::CrashReportDatabase::Report& completed_report :
-       completed_reports) {
-    if (completed_report.uploaded) {
-      uploaded_reports.push_back(
-          UploadReportResult(static_cast<int>(completed_report.creation_time),
-                             completed_report.id));
-    }
-  }
-
-  auto sort_by_time = [](const UploadReportResult& a,
-                         const UploadReportResult& b) {
-    return a.first > b.first;
-  };
-  std::sort(uploaded_reports.begin(), uploaded_reports.end(), sort_by_time);
-  return uploaded_reports;
-}
-
-}  // namespace crash_reporter

+ 0 - 49
shell/common/crash_reporter/crash_reporter_crashpad.h

@@ -1,49 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_CRASHPAD_H_
-#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_CRASHPAD_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/strings/string_piece.h"
-#include "shell/common/crash_reporter/crash_reporter.h"
-#include "third_party/crashpad/crashpad/client/crash_report_database.h"
-#include "third_party/crashpad/crashpad/client/simple_string_dictionary.h"
-
-namespace crash_reporter {
-
-class CrashReporterCrashpad : public CrashReporter {
- public:
-  void SetUploadToServer(bool upload_to_server) override;
-  bool GetUploadToServer() override;
-  void AddExtraParameter(const std::string& key,
-                         const std::string& value) override;
-  void RemoveExtraParameter(const std::string& key) override;
-  std::map<std::string, std::string> GetParameters() const override;
-
- protected:
-  CrashReporterCrashpad();
-  ~CrashReporterCrashpad() override;
-
-  void SetUploadsEnabled(bool enable_uploads);
-  void SetCrashKeyValue(base::StringPiece key, base::StringPiece value);
-  void SetInitialCrashKeyValues();
-
-  std::vector<UploadReportResult> GetUploadedReports(
-      const base::FilePath& crashes_dir) override;
-
-  std::unique_ptr<crashpad::SimpleStringDictionary> simple_string_dictionary_;
-  std::unique_ptr<crashpad::CrashReportDatabase> database_;
-
-  DISALLOW_COPY_AND_ASSIGN(CrashReporterCrashpad);
-};
-
-}  // namespace crash_reporter
-
-#endif  // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_CRASHPAD_H_

+ 0 - 145
shell/common/crash_reporter/crash_reporter_linux.cc

@@ -1,145 +0,0 @@
-// Copyright (c) 2014 GitHub, Inc.
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "shell/common/crash_reporter/crash_reporter_linux.h"
-
-#include <sys/time.h>
-#include <unistd.h>
-
-#include <memory>
-
-#include <string>
-
-#include "base/debug/crash_logging.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/linux_util.h"
-#include "base/logging.h"
-#include "base/memory/singleton.h"
-#include "base/process/memory.h"
-#include "base/threading/thread_restrictions.h"
-#include "breakpad/src/client/linux/handler/exception_handler.h"
-#include "breakpad/src/common/linux/linux_libc_support.h"
-
-using google_breakpad::ExceptionHandler;
-using google_breakpad::MinidumpDescriptor;
-
-namespace crash_reporter {
-
-namespace {
-
-// Define a preferred limit on minidump sizes, because Crash Server currently
-// throws away any larger than 1.2MB (1.2 * 1024 * 1024).  A value of -1 means
-// no limit.
-static const off_t kMaxMinidumpFileSize = 1258291;
-
-}  // namespace
-
-CrashReporterLinux::CrashReporterLinux() : pid_(getpid()) {
-  // Set the base process start time value.
-  struct timeval tv;
-  if (!gettimeofday(&tv, nullptr)) {
-    uint64_t ret = tv.tv_sec;
-    ret *= 1000;
-    ret += tv.tv_usec / 1000;
-    process_start_time_ = ret;
-  }
-
-  {
-    base::ThreadRestrictions::ScopedAllowIO allow_io;
-    // Make base::g_linux_distro work.
-    base::SetLinuxDistro(base::GetLinuxDistro());
-  }
-}
-
-CrashReporterLinux::~CrashReporterLinux() = default;
-
-void CrashReporterLinux::Init(const std::string& product_name,
-                              const std::string& company_name,
-                              const std::string& submit_url,
-                              const base::FilePath& crashes_dir,
-                              bool upload_to_server,
-                              bool skip_system_crash_handler) {
-  EnableCrashDumping(crashes_dir);
-
-  upload_url_ = submit_url;
-  upload_to_server_ = upload_to_server;
-
-  crash_keys_ = std::make_unique<CrashKeyStorage>();
-  for (StringMap::const_iterator iter = upload_parameters_.begin();
-       iter != upload_parameters_.end(); ++iter)
-    crash_keys_->SetKeyValue(iter->first.c_str(), iter->second.c_str());
-}
-
-void CrashReporterLinux::SetUploadParameters() {
-  upload_parameters_["platform"] = "linux";
-}
-
-void CrashReporterLinux::SetUploadToServer(const bool upload_to_server) {
-  upload_to_server_ = upload_to_server;
-}
-
-bool CrashReporterLinux::GetUploadToServer() {
-  return upload_to_server_;
-}
-
-void CrashReporterLinux::EnableCrashDumping(const base::FilePath& crashes_dir) {
-  {
-    base::ThreadRestrictions::ScopedAllowIO allow_io;
-    base::CreateDirectory(crashes_dir);
-  }
-  std::string log_file = crashes_dir.Append("uploads.log").value();
-  strncpy(g_crash_log_path, log_file.c_str(), sizeof(g_crash_log_path));
-
-  MinidumpDescriptor minidump_descriptor(crashes_dir.value());
-  minidump_descriptor.set_size_limit(kMaxMinidumpFileSize);
-
-  breakpad_ = std::make_unique<ExceptionHandler>(minidump_descriptor, nullptr,
-                                                 CrashDone, this,
-                                                 true,  // Install handlers.
-                                                 -1);
-}
-
-bool CrashReporterLinux::CrashDone(const MinidumpDescriptor& minidump,
-                                   void* context,
-                                   const bool succeeded) {
-  CrashReporterLinux* self = static_cast<CrashReporterLinux*>(context);
-
-  // WARNING: this code runs in a compromised context. It may not call into
-  // libc nor allocate memory normally.
-  if (!succeeded) {
-    const char msg[] = "Failed to generate minidump.";
-    WriteLog(msg, sizeof(msg) - 1);
-    return false;
-  }
-
-  DCHECK(!minidump.IsFD());
-
-  BreakpadInfo info = {0};
-  info.filename = minidump.path();
-  info.fd = minidump.fd();
-  info.distro = base::g_linux_distro;
-  info.distro_length = my_strlen(base::g_linux_distro);
-  info.upload = self->upload_to_server_;
-  info.process_start_time = self->process_start_time_;
-  info.oom_size = base::g_oom_size;
-  info.pid = self->pid_;
-  info.upload_url = self->upload_url_.c_str();
-  info.crash_keys = self->crash_keys_.get();
-  HandleCrashDump(info);
-  return true;
-}
-
-// static
-CrashReporterLinux* CrashReporterLinux::GetInstance() {
-  return base::Singleton<CrashReporterLinux>::get();
-}
-
-// static
-CrashReporter* CrashReporter::GetInstance() {
-  return CrashReporterLinux::GetInstance();
-}
-
-}  // namespace crash_reporter

+ 0 - 65
shell/common/crash_reporter/crash_reporter_linux.h

@@ -1,65 +0,0 @@
-// Copyright (c) 2014 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
-#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
-
-#include <memory>
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "shell/common/crash_reporter/crash_reporter.h"
-#include "shell/common/crash_reporter/linux/crash_dump_handler.h"
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
-namespace google_breakpad {
-class ExceptionHandler;
-class MinidumpDescriptor;
-}  // namespace google_breakpad
-
-namespace crash_reporter {
-
-class CrashReporterLinux : public CrashReporter {
- public:
-  static CrashReporterLinux* GetInstance();
-
-  void Init(const std::string& product_name,
-            const std::string& company_name,
-            const std::string& submit_url,
-            const base::FilePath& crashes_dir,
-            bool upload_to_server,
-            bool skip_system_crash_handler) override;
-  void SetUploadToServer(bool upload_to_server) override;
-  void SetUploadParameters() override;
-  bool GetUploadToServer() override;
-
- private:
-  friend struct base::DefaultSingletonTraits<CrashReporterLinux>;
-
-  CrashReporterLinux();
-  ~CrashReporterLinux() override;
-
-  void EnableCrashDumping(const base::FilePath& crashes_dir);
-
-  static bool CrashDone(const google_breakpad::MinidumpDescriptor& minidump,
-                        void* context,
-                        const bool succeeded);
-
-  std::unique_ptr<google_breakpad::ExceptionHandler> breakpad_;
-  std::unique_ptr<CrashKeyStorage> crash_keys_;
-
-  uint64_t process_start_time_ = 0;
-  pid_t pid_ = 0;
-  std::string upload_url_;
-  bool upload_to_server_ = true;
-
-  DISALLOW_COPY_AND_ASSIGN(CrashReporterLinux);
-};
-}  // namespace crash_reporter
-
-#endif  // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_

+ 0 - 43
shell/common/crash_reporter/crash_reporter_mac.h

@@ -1,43 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
-#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_
-
-#include <memory>
-#include <string>
-
-#include "shell/common/crash_reporter/crash_reporter_crashpad.h"
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
-namespace crash_reporter {
-
-class CrashReporterMac : public CrashReporterCrashpad {
- public:
-  static CrashReporterMac* GetInstance();
-
-  void Init(const std::string& product_name,
-            const std::string& company_name,
-            const std::string& submit_url,
-            const base::FilePath& crashes_dir,
-            bool upload_to_server,
-            bool skip_system_crash_handler) override;
-  void SetUploadParameters() override;
-
- private:
-  friend struct base::DefaultSingletonTraits<CrashReporterMac>;
-
-  CrashReporterMac();
-  ~CrashReporterMac() override;
-
-  DISALLOW_COPY_AND_ASSIGN(CrashReporterMac);
-};
-
-}  // namespace crash_reporter
-
-#endif  // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_MAC_H_

+ 0 - 84
shell/common/crash_reporter/crash_reporter_mac.mm

@@ -1,84 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "shell/common/crash_reporter/crash_reporter_mac.h"
-
-#include <algorithm>
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/mac/bundle_locations.h"
-#include "base/mac/mac_util.h"
-#include "base/memory/singleton.h"
-#include "third_party/crashpad/crashpad/client/crashpad_client.h"
-#include "third_party/crashpad/crashpad/client/crashpad_info.h"
-
-namespace crash_reporter {
-
-CrashReporterMac::CrashReporterMac() {}
-
-CrashReporterMac::~CrashReporterMac() {}
-
-void CrashReporterMac::Init(const std::string& product_name,
-                            const std::string& company_name,
-                            const std::string& submit_url,
-                            const base::FilePath& crashes_dir,
-                            bool upload_to_server,
-                            bool skip_system_crash_handler) {
-  // check whether crashpad has been initialized.
-  // Only need to initialize once.
-  if (simple_string_dictionary_)
-    return;
-
-  if (process_type_.empty()) {  // browser process
-    @autoreleasepool {
-      base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath();
-      base::FilePath handler_path =
-          framework_bundle_path.Append("Resources").Append("crashpad_handler");
-
-      std::vector<std::string> args = {
-          "--no-rate-limit",
-          "--no-upload-gzip",  // not all servers accept gzip
-      };
-
-      crashpad::CrashpadClient crashpad_client;
-      crashpad_client.StartHandler(handler_path, crashes_dir, crashes_dir,
-                                   submit_url, StringMap(), args, true, false);
-    }  // @autoreleasepool
-  }
-
-  crashpad::CrashpadInfo* crashpad_info =
-      crashpad::CrashpadInfo::GetCrashpadInfo();
-  if (skip_system_crash_handler) {
-    crashpad_info->set_system_crash_reporter_forwarding(
-        crashpad::TriState::kDisabled);
-  }
-
-  simple_string_dictionary_.reset(new crashpad::SimpleStringDictionary());
-  crashpad_info->set_simple_annotations(simple_string_dictionary_.get());
-
-  SetInitialCrashKeyValues();
-  if (process_type_.empty()) {  // browser process
-    database_ = crashpad::CrashReportDatabase::Initialize(crashes_dir);
-    SetUploadToServer(upload_to_server);
-  }
-}
-
-void CrashReporterMac::SetUploadParameters() {
-  upload_parameters_["platform"] = "darwin";
-}
-
-// static
-CrashReporterMac* CrashReporterMac::GetInstance() {
-  return base::Singleton<CrashReporterMac>::get();
-}
-
-// static
-CrashReporter* CrashReporter::GetInstance() {
-  return CrashReporterMac::GetInstance();
-}
-
-}  // namespace crash_reporter

+ 0 - 150
shell/common/crash_reporter/crash_reporter_win.cc

@@ -1,150 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "shell/common/crash_reporter/crash_reporter_win.h"
-
-#include <memory>
-#include <vector>
-
-#include "base/environment.h"
-#include "base/memory/singleton.h"
-#include "base/path_service.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "electron/shell/common/api/api.mojom.h"
-#include "mojo/public/cpp/bindings/associated_remote.h"
-#include "shell/browser/ui/inspectable_web_contents_impl.h"
-#include "shell/common/electron_constants.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
-#include "third_party/crashpad/crashpad/client/crashpad_client.h"
-#include "third_party/crashpad/crashpad/client/crashpad_info.h"
-
-#if defined(_WIN64)
-#include "gin/public/debug.h"
-#endif
-
-namespace {
-
-#if defined(_WIN64)
-int CrashForException(EXCEPTION_POINTERS* info) {
-  auto* reporter = crash_reporter::CrashReporterWin::GetInstance();
-  if (reporter->IsInitialized()) {
-    reporter->GetCrashpadClient().DumpAndCrash(info);
-    return EXCEPTION_CONTINUE_SEARCH;
-  }
-
-  // When there is exception and we do not have crashReporter set up, we just
-  // let the execution continue and crash, which is the default behavior.
-  //
-  // We must not return EXCEPTION_CONTINUE_SEARCH here, as it would end up with
-  // busy loop when there is no exception handler in the program.
-  return EXCEPTION_CONTINUE_EXECUTION;
-}
-#endif
-
-}  // namespace
-
-namespace crash_reporter {
-
-CrashReporterWin::CrashReporterWin() {}
-
-CrashReporterWin::~CrashReporterWin() {}
-
-#if defined(_WIN64)
-void CrashReporterWin::SetUnhandledExceptionFilter() {
-  gin::Debug::SetUnhandledExceptionCallback(&CrashForException);
-}
-#endif
-
-void CrashReporterWin::Init(const std::string& product_name,
-                            const std::string& company_name,
-                            const std::string& submit_url,
-                            const base::FilePath& crashes_dir,
-                            bool upload_to_server,
-                            bool skip_system_crash_handler) {
-  // check whether crashpad has been initialized.
-  // Only need to initialize once.
-  if (simple_string_dictionary_)
-    return;
-  if (process_type_.empty()) {  // browser process
-    base::FilePath handler_path;
-    base::PathService::Get(base::FILE_EXE, &handler_path);
-
-    std::vector<std::string> args = {
-        "--no-rate-limit",
-        "--no-upload-gzip",  // not all servers accept gzip
-    };
-    args.push_back(base::StringPrintf("--type=%s", kCrashpadProcess));
-    args.push_back(
-        base::StringPrintf("--%s=%s", kCrashesDirectoryKey,
-                           base::UTF16ToUTF8(crashes_dir.value()).c_str()));
-    crashpad_client_.StartHandler(handler_path, crashes_dir, crashes_dir,
-                                  submit_url, StringMap(), args, true, false);
-    UpdatePipeName();
-  } else {
-    std::unique_ptr<base::Environment> env(base::Environment::Create());
-    std::string pipe_name_utf8;
-    if (env->GetVar(electron::kCrashpadPipeName, &pipe_name_utf8)) {
-      base::string16 pipe_name = base::UTF8ToUTF16(pipe_name_utf8);
-      if (!crashpad_client_.SetHandlerIPCPipe(pipe_name))
-        LOG(ERROR) << "Failed to set handler IPC pipe name: " << pipe_name;
-    } else {
-      LOG(ERROR) << "Unable to get pipe name for crashpad";
-    }
-  }
-  crashpad::CrashpadInfo* crashpad_info =
-      crashpad::CrashpadInfo::GetCrashpadInfo();
-  if (skip_system_crash_handler) {
-    crashpad_info->set_system_crash_reporter_forwarding(
-        crashpad::TriState::kDisabled);
-  }
-  simple_string_dictionary_.reset(new crashpad::SimpleStringDictionary());
-  crashpad_info->set_simple_annotations(simple_string_dictionary_.get());
-
-  SetInitialCrashKeyValues();
-  if (process_type_.empty()) {  // browser process
-    database_ = crashpad::CrashReportDatabase::Initialize(crashes_dir);
-    SetUploadToServer(upload_to_server);
-  }
-}
-
-void CrashReporterWin::SetUploadParameters() {
-  upload_parameters_["platform"] = "win32";
-}
-
-crashpad::CrashpadClient& CrashReporterWin::GetCrashpadClient() {
-  return crashpad_client_;
-}
-
-void CrashReporterWin::UpdatePipeName() {
-  std::string pipe_name =
-      base::UTF16ToUTF8(crashpad_client_.GetHandlerIPCPipe());
-  std::unique_ptr<base::Environment> env(base::Environment::Create());
-  env->SetVar(electron::kCrashpadPipeName, pipe_name);
-
-  // Notify all WebContents of the pipe name.
-  const auto& pages = electron::InspectableWebContentsImpl::GetAll();
-  for (auto* page : pages) {
-    auto* frame_host = page->GetWebContents()->GetMainFrame();
-    if (!frame_host)
-      continue;
-
-    mojo::AssociatedRemote<electron::mojom::ElectronRenderer> electron_renderer;
-    frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
-        &electron_renderer);
-    electron_renderer->UpdateCrashpadPipeName(pipe_name);
-  }
-}
-
-// static
-CrashReporterWin* CrashReporterWin::GetInstance() {
-  return base::Singleton<CrashReporterWin>::get();
-}
-
-// static
-CrashReporter* CrashReporter::GetInstance() {
-  return CrashReporterWin::GetInstance();
-}
-
-}  // namespace crash_reporter

+ 0 - 52
shell/common/crash_reporter/crash_reporter_win.h

@@ -1,52 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#ifndef SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_
-#define SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_
-
-#include <memory>
-#include <string>
-
-#include "shell/common/crash_reporter/crash_reporter_crashpad.h"
-#include "third_party/crashpad/crashpad/client/crashpad_client.h"
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
-namespace crash_reporter {
-
-class CrashReporterWin : public CrashReporterCrashpad {
- public:
-  static CrashReporterWin* GetInstance();
-#if defined(_WIN64)
-  static void SetUnhandledExceptionFilter();
-#endif
-
-  void Init(const std::string& product_name,
-            const std::string& company_name,
-            const std::string& submit_url,
-            const base::FilePath& crashes_dir,
-            bool upload_to_server,
-            bool skip_system_crash_handler) override;
-  void SetUploadParameters() override;
-
-  crashpad::CrashpadClient& GetCrashpadClient();
-
- private:
-  friend struct base::DefaultSingletonTraits<CrashReporterWin>;
-  CrashReporterWin();
-  ~CrashReporterWin() override;
-
-  void UpdatePipeName();
-
-  crashpad::CrashpadClient crashpad_client_;
-
-  DISALLOW_COPY_AND_ASSIGN(CrashReporterWin);
-};
-
-}  // namespace crash_reporter
-
-#endif  // SHELL_COMMON_CRASH_REPORTER_CRASH_REPORTER_WIN_H_

+ 0 - 753
shell/common/crash_reporter/linux/crash_dump_handler.cc

@@ -1,753 +0,0 @@
-// Copyright (c) 2014 GitHub, Inc.
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-// For linux_syscall_support.h. This makes it safe to call embedded system
-// calls when in seccomp mode.
-
-#include "shell/common/crash_reporter/linux/crash_dump_handler.h"
-
-#include <poll.h>
-
-#include <algorithm>
-
-#include "base/posix/eintr_wrapper.h"
-#include "breakpad/src/client/linux/minidump_writer/directory_reader.h"
-#include "breakpad/src/common/linux/linux_libc_support.h"
-#include "breakpad/src/common/memory_allocator.h"
-
-#include "third_party/lss/linux_syscall_support.h"
-
-// Some versions of gcc are prone to warn about unused return values. In cases
-// where we either a) know the call cannot fail, or b) there is nothing we
-// can do when a call fails, we mark the return code as ignored. This avoids
-// spurious compiler warnings.
-#define IGNORE_RET(x) \
-  do {                \
-    if (x)            \
-      ;               \
-  } while (0)
-
-namespace crash_reporter {
-
-namespace {
-
-// String buffer size to use to convert a uint64_t to string.
-const size_t kUint64StringSize = 21;
-
-// Writes the value |v| as 16 hex characters to the memory pointed at by
-// |output|.
-void write_uint64_hex(char* output, uint64_t v) {
-  static const char hextable[] = "0123456789abcdef";
-
-  for (int i = 15; i >= 0; --i) {
-    output[i] = hextable[v & 15];
-    v >>= 4;
-  }
-}
-
-// uint64_t version of my_int_len() from
-// breakpad/src/common/linux/linux_libc_support.h. Return the length of the
-// given, non-negative integer when expressed in base 10.
-unsigned my_uint64_len(uint64_t i) {
-  if (!i)
-    return 1;
-
-  unsigned len = 0;
-  while (i) {
-    len++;
-    i /= 10;
-  }
-
-  return len;
-}
-
-// uint64_t version of my_uitos() from
-// breakpad/src/common/linux/linux_libc_support.h. Convert a non-negative
-// integer to a string (not null-terminated).
-void my_uint64tos(char* output, uint64_t i, unsigned i_len) {
-  for (unsigned index = i_len; index; --index, i /= 10)
-    output[index - 1] = '0' + (i % 10);
-}
-
-// Converts a struct timeval to milliseconds.
-uint64_t kernel_timeval_to_ms(struct kernel_timeval* tv) {
-  uint64_t ret = tv->tv_sec;  // Avoid overflow by explicitly using a uint64_t.
-  ret *= 1000;
-  ret += tv->tv_usec / 1000;
-  return ret;
-}
-
-bool my_isxdigit(char c) {
-  return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f');
-}
-
-size_t LengthWithoutTrailingSpaces(const char* str, size_t len) {
-  while (len > 0 && str[len - 1] == ' ') {
-    len--;
-  }
-  return len;
-}
-
-// MIME substrings.
-const char g_rn[] = "\r\n";
-const char g_form_data_msg[] = "Content-Disposition: form-data; name=\"";
-const char g_quote_msg[] = "\"";
-const char g_dashdash_msg[] = "--";
-const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\"";
-const char g_content_type_msg[] = "Content-Type: application/octet-stream";
-
-// MimeWriter manages an iovec for writing MIMEs to a file.
-class MimeWriter {
- public:
-  static const int kIovCapacity = 30;
-  static const size_t kMaxCrashChunkSize = 64;
-
-  MimeWriter(int fd, const char* const mime_boundary);
-  ~MimeWriter();
-
-  // Append boundary.
-  virtual void AddBoundary();
-
-  // Append end of file boundary.
-  virtual void AddEnd();
-
-  // Append key/value pair with specified sizes.
-  virtual void AddPairData(const char* msg_type,
-                           size_t msg_type_size,
-                           const char* msg_data,
-                           size_t msg_data_size);
-
-  // Append key/value pair.
-  void AddPairString(const char* msg_type, const char* msg_data) {
-    AddPairData(msg_type, my_strlen(msg_type), msg_data, my_strlen(msg_data));
-  }
-
-  // Append key/value pair, splitting value into chunks no larger than
-  // |chunk_size|. |chunk_size| cannot be greater than |kMaxCrashChunkSize|.
-  // The msg_type string will have a counter suffix to distinguish each chunk.
-  virtual void AddPairDataInChunks(const char* msg_type,
-                                   size_t msg_type_size,
-                                   const char* msg_data,
-                                   size_t msg_data_size,
-                                   size_t chunk_size,
-                                   bool strip_trailing_spaces);
-
-  // Add binary file contents to be uploaded with the specified filename.
-  virtual void AddFileContents(const char* filename_msg,
-                               uint8_t* file_data,
-                               size_t file_size);
-
-  // Flush any pending iovecs to the output file.
-  void Flush() {
-    IGNORE_RET(sys_writev(fd_, iov_, iov_index_));
-    iov_index_ = 0;
-  }
-
- protected:
-  void AddItem(const void* base, size_t size);
-  // Minor performance trade-off for easier-to-maintain code.
-  void AddString(const char* str) { AddItem(str, my_strlen(str)); }
-  void AddItemWithoutTrailingSpaces(const void* base, size_t size);
-
-  struct kernel_iovec iov_[kIovCapacity];
-  int iov_index_ = 0;
-
-  // Output file descriptor.
-  int fd_ = -1;
-
-  const char* const mime_boundary_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MimeWriter);
-};
-
-MimeWriter::MimeWriter(int fd, const char* const mime_boundary)
-    : fd_(fd), mime_boundary_(mime_boundary) {}
-
-MimeWriter::~MimeWriter() = default;
-
-void MimeWriter::AddBoundary() {
-  AddString(mime_boundary_);
-  AddString(g_rn);
-}
-
-void MimeWriter::AddEnd() {
-  AddString(mime_boundary_);
-  AddString(g_dashdash_msg);
-  AddString(g_rn);
-}
-
-void MimeWriter::AddPairData(const char* msg_type,
-                             size_t msg_type_size,
-                             const char* msg_data,
-                             size_t msg_data_size) {
-  AddString(g_form_data_msg);
-  AddItem(msg_type, msg_type_size);
-  AddString(g_quote_msg);
-  AddString(g_rn);
-  AddString(g_rn);
-  AddItem(msg_data, msg_data_size);
-  AddString(g_rn);
-}
-
-void MimeWriter::AddPairDataInChunks(const char* msg_type,
-                                     size_t msg_type_size,
-                                     const char* msg_data,
-                                     size_t msg_data_size,
-                                     size_t chunk_size,
-                                     bool strip_trailing_spaces) {
-  if (chunk_size > kMaxCrashChunkSize)
-    return;
-
-  unsigned i = 0;
-  size_t done = 0, msg_length = msg_data_size;
-
-  while (msg_length) {
-    char num[kUint64StringSize];
-    const unsigned num_len = my_uint_len(++i);
-    my_uitos(num, i, num_len);
-
-    size_t chunk_len = std::min(chunk_size, msg_length);
-
-    AddString(g_form_data_msg);
-    AddItem(msg_type, msg_type_size);
-    AddItem(num, num_len);
-    AddString(g_quote_msg);
-    AddString(g_rn);
-    AddString(g_rn);
-    if (strip_trailing_spaces) {
-      AddItemWithoutTrailingSpaces(msg_data + done, chunk_len);
-    } else {
-      AddItem(msg_data + done, chunk_len);
-    }
-    AddString(g_rn);
-    AddBoundary();
-    Flush();
-
-    done += chunk_len;
-    msg_length -= chunk_len;
-  }
-}
-
-void MimeWriter::AddFileContents(const char* filename_msg,
-                                 uint8_t* file_data,
-                                 size_t file_size) {
-  AddString(g_form_data_msg);
-  AddString(filename_msg);
-  AddString(g_rn);
-  AddString(g_content_type_msg);
-  AddString(g_rn);
-  AddString(g_rn);
-  AddItem(file_data, file_size);
-  AddString(g_rn);
-}
-
-void MimeWriter::AddItem(const void* base, size_t size) {
-  // Check if the iovec is full and needs to be flushed to output file.
-  if (iov_index_ == kIovCapacity) {
-    Flush();
-  }
-  iov_[iov_index_].iov_base = const_cast<void*>(base);
-  iov_[iov_index_].iov_len = size;
-  ++iov_index_;
-}
-
-void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) {
-  AddItem(base,
-          LengthWithoutTrailingSpaces(static_cast<const char*>(base), size));
-}
-
-void LoadDataFromFD(google_breakpad::PageAllocator* allocator,
-                    int fd,
-                    bool close_fd,
-                    uint8_t** file_data,
-                    size_t* size) {
-  struct kernel_stat st;
-  if (sys_fstat(fd, &st) != 0) {
-    static const char msg[] = "Cannot upload crash dump: stat failed\n";
-    WriteLog(msg, sizeof(msg) - 1);
-    if (close_fd)
-      IGNORE_RET(sys_close(fd));
-    return;
-  }
-
-  *file_data = reinterpret_cast<uint8_t*>(allocator->Alloc(st.st_size));
-  if (!(*file_data)) {
-    static const char msg[] = "Cannot upload crash dump: cannot alloc\n";
-    WriteLog(msg, sizeof(msg) - 1);
-    if (close_fd)
-      IGNORE_RET(sys_close(fd));
-    return;
-  }
-  my_memset(*file_data, 0xf, st.st_size);
-
-  *size = st.st_size;
-  int byte_read = sys_read(fd, *file_data, *size);
-  if (byte_read == -1) {
-    static const char msg[] = "Cannot upload crash dump: read failed\n";
-    WriteLog(msg, sizeof(msg) - 1);
-    if (close_fd)
-      IGNORE_RET(sys_close(fd));
-    return;
-  }
-
-  if (close_fd)
-    IGNORE_RET(sys_close(fd));
-}
-
-void LoadDataFromFile(google_breakpad::PageAllocator* allocator,
-                      const char* filename,
-                      int* fd,
-                      uint8_t** file_data,
-                      size_t* size) {
-  // WARNING: this code runs in a compromised context. It may not call into
-  // libc nor allocate memory normally.
-  *fd = sys_open(filename, O_RDONLY, 0);
-  *size = 0;
-
-  if (*fd < 0) {
-    static const char msg[] = "Cannot upload crash dump: failed to open\n";
-    WriteLog(msg, sizeof(msg) - 1);
-    return;
-  }
-
-  LoadDataFromFD(allocator, *fd, true, file_data, size);
-}
-
-// Spawn the appropriate upload process for the current OS:
-// - generic Linux invokes wget.
-// - ChromeOS invokes crash_reporter.
-// |dumpfile| is the path to the dump data file.
-// |mime_boundary| is only used on Linux.
-// |exe_buf| is only used on CrOS and is the crashing process' name.
-void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
-                                  const char* dumpfile,
-                                  const char* mime_boundary,
-                                  const char* exe_buf,
-                                  google_breakpad::PageAllocator* allocator) {
-  // The --header argument to wget looks like:
-  //   --header=Content-Type: multipart/form-data; boundary=XYZ
-  // where the boundary has two fewer leading '-' chars
-  static const char header_msg[] =
-      "--header=Content-Type: multipart/form-data; boundary=";
-  char* const header = reinterpret_cast<char*>(
-      allocator->Alloc(sizeof(header_msg) - 1 + strlen(mime_boundary) - 2 + 1));
-  memcpy(header, header_msg, sizeof(header_msg) - 1);
-  memcpy(header + sizeof(header_msg) - 1, mime_boundary + 2,
-         strlen(mime_boundary) - 2);
-  // We grab the NUL byte from the end of |mime_boundary|.
-
-  // The --post-file argument to wget looks like:
-  //   --post-file=/tmp/...
-  static const char post_file_msg[] = "--post-file=";
-  char* const post_file = reinterpret_cast<char*>(
-      allocator->Alloc(sizeof(post_file_msg) - 1 + strlen(dumpfile) + 1));
-  memcpy(post_file, post_file_msg, sizeof(post_file_msg) - 1);
-  memcpy(post_file + sizeof(post_file_msg) - 1, dumpfile, strlen(dumpfile));
-
-  static const char kWgetBinary[] = "/usr/bin/wget";
-  const char* args[] = {
-      kWgetBinary,    header,  post_file, info.upload_url,
-      "--timeout=60",  // Set a timeout so we don't hang forever.
-      "--tries=1",     // Don't retry if the upload fails.
-      "--quiet",       // Be silent.
-      "-O",            // output reply to /dev/null.
-      "/dev/fd/3",    nullptr,
-  };
-  static const char msg[] =
-      "Cannot upload crash dump: cannot exec "
-      "/usr/bin/wget\n";
-  execve(args[0], const_cast<char**>(args), environ);
-  WriteLog(msg, sizeof(msg) - 1);
-  sys__exit(1);
-}
-
-// Runs in the helper process to wait for the upload process running
-// ExecUploadProcessOrTerminate() to finish. Returns the number of bytes written
-// to |fd| and save the written contents to |buf|.
-// |buf| needs to be big enough to hold |bytes_to_read| + 1 characters.
-size_t WaitForCrashReportUploadProcess(int fd,
-                                       size_t bytes_to_read,
-                                       char* buf) {
-  size_t bytes_read = 0;
-
-  // Upload should finish in about 10 seconds. Add a few more 500 ms
-  // internals to account for process startup time.
-  for (size_t wait_count = 0; wait_count < 24; ++wait_count) {
-    struct kernel_pollfd poll_fd;
-    poll_fd.fd = fd;
-    poll_fd.events = POLLIN | POLLPRI | POLLERR;
-    int ret = sys_poll(&poll_fd, 1, 500);
-    if (ret < 0) {
-      // Error
-      break;
-    } else if (ret > 0) {
-      // There is data to read.
-      ssize_t len = HANDLE_EINTR(
-          sys_read(fd, buf + bytes_read, bytes_to_read - bytes_read));
-      if (len < 0)
-        break;
-      bytes_read += len;
-      if (bytes_read == bytes_to_read)
-        break;
-    }
-    // |ret| == 0 -> timed out, continue waiting.
-    // or |bytes_read| < |bytes_to_read| still, keep reading.
-  }
-  buf[bytes_to_read] = 0;  // Always NUL terminate the buffer.
-  return bytes_read;
-}
-
-// |buf| should be |expected_len| + 1 characters in size and NULL terminated.
-bool IsValidCrashReportId(const char* buf,
-                          size_t bytes_read,
-                          size_t expected_len) {
-  if (bytes_read != expected_len)
-    return false;
-  for (size_t i = 0; i < bytes_read; ++i) {
-    if (!my_isxdigit(buf[i]) && buf[i] != '-')
-      return false;
-  }
-  return true;
-}
-
-// |buf| should be |expected_len| + 1 characters in size and NULL terminated.
-void HandleCrashReportId(const char* buf,
-                         size_t bytes_read,
-                         size_t expected_len) {
-  if (!IsValidCrashReportId(buf, bytes_read, expected_len)) {
-    static const char msg[] = "Failed to get crash dump id.";
-    WriteLog(msg, sizeof(msg) - 1);
-    WriteNewline();
-
-    static const char id_msg[] = "Report Id: ";
-    WriteLog(id_msg, sizeof(id_msg) - 1);
-    WriteLog(buf, bytes_read);
-    WriteNewline();
-    return;
-  }
-
-  // Write crash dump id to stderr.
-  static const char msg[] = "Crash dump id: ";
-  WriteLog(msg, sizeof(msg) - 1);
-  WriteLog(buf, my_strlen(buf));
-  WriteNewline();
-
-  // Write crash dump id to crash log as: seconds_since_epoch,crash_id
-  struct kernel_timeval tv;
-  if (!sys_gettimeofday(&tv, nullptr)) {
-    uint64_t time = kernel_timeval_to_ms(&tv) / 1000;
-    char time_str[kUint64StringSize];
-    const unsigned time_len = my_uint64_len(time);
-    my_uint64tos(time_str, time, time_len);
-
-    const int kLogOpenFlags = O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC;
-    int log_fd = sys_open(g_crash_log_path, kLogOpenFlags, 0600);
-    if (log_fd > 0) {
-      sys_write(log_fd, time_str, time_len);
-      sys_write(log_fd, ",", 1);
-      sys_write(log_fd, buf, my_strlen(buf));
-      sys_write(log_fd, "\n", 1);
-      IGNORE_RET(sys_close(log_fd));
-    }
-  }
-}
-
-}  // namespace
-
-char g_crash_log_path[256];
-
-void HandleCrashDump(const BreakpadInfo& info) {
-  int dumpfd;
-  bool keep_fd = false;
-  size_t dump_size;
-  uint8_t* dump_data;
-  google_breakpad::PageAllocator allocator;
-  const char* exe_buf = nullptr;
-
-  if (info.fd != -1) {
-    // Dump is provided with an open FD.
-    keep_fd = true;
-    dumpfd = info.fd;
-
-    // The FD is pointing to the end of the file.
-    // Rewind, we'll read the data next.
-    if (lseek(dumpfd, 0, SEEK_SET) == -1) {
-      static const char msg[] =
-          "Cannot upload crash dump: failed to "
-          "reposition minidump FD\n";
-      WriteLog(msg, sizeof(msg) - 1);
-      IGNORE_RET(sys_close(dumpfd));
-      return;
-    }
-    LoadDataFromFD(&allocator, info.fd, false, &dump_data, &dump_size);
-  } else {
-    // Dump is provided with a path.
-    keep_fd = false;
-    LoadDataFromFile(&allocator, info.filename, &dumpfd, &dump_data,
-                     &dump_size);
-  }
-
-  // We need to build a MIME block for uploading to the server. Since we are
-  // going to fork and run wget, it needs to be written to a temp file.
-  const int ufd = sys_open("/dev/urandom", O_RDONLY, 0);
-  if (ufd < 0) {
-    static const char msg[] =
-        "Cannot upload crash dump because /dev/urandom"
-        " is missing\n";
-    WriteLog(msg, sizeof(msg) - 1);
-    return;
-  }
-
-  static const char temp_file_template[] =
-      "/tmp/chromium-upload-XXXXXXXXXXXXXXXX";
-  char temp_file[sizeof(temp_file_template)];
-  int temp_file_fd = -1;
-  if (keep_fd) {
-    temp_file_fd = dumpfd;
-    // Rewind the destination, we are going to overwrite it.
-    if (lseek(dumpfd, 0, SEEK_SET) == -1) {
-      static const char msg[] =
-          "Cannot upload crash dump: failed to "
-          "reposition minidump FD (2)\n";
-      WriteLog(msg, sizeof(msg) - 1);
-      IGNORE_RET(sys_close(dumpfd));
-      return;
-    }
-  } else {
-    if (info.upload) {
-      memcpy(temp_file, temp_file_template, sizeof(temp_file_template));
-
-      for (unsigned i = 0; i < 10; ++i) {
-        uint64_t t;
-        sys_read(ufd, &t, sizeof(t));
-        write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t);
-
-        temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
-        if (temp_file_fd >= 0)
-          break;
-      }
-
-      if (temp_file_fd < 0) {
-        static const char msg[] =
-            "Failed to create temporary file in /tmp: "
-            "cannot upload crash dump\n";
-        WriteLog(msg, sizeof(msg) - 1);
-        IGNORE_RET(sys_close(ufd));
-        return;
-      }
-    } else {
-      temp_file_fd = sys_open(info.filename, O_WRONLY, 0600);
-      if (temp_file_fd < 0) {
-        static const char msg[] = "Failed to save crash dump: failed to open\n";
-        WriteLog(msg, sizeof(msg) - 1);
-        IGNORE_RET(sys_close(ufd));
-        return;
-      }
-    }
-  }
-
-  // The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL.
-  char mime_boundary[28 + 16 + 1];
-  my_memset(mime_boundary, '-', 28);
-  uint64_t boundary_rand;
-  sys_read(ufd, &boundary_rand, sizeof(boundary_rand));
-  write_uint64_hex(mime_boundary + 28, boundary_rand);
-  mime_boundary[28 + 16] = 0;
-  IGNORE_RET(sys_close(ufd));
-
-  // The MIME block looks like this:
-  //   BOUNDARY \r\n
-  //   Content-Disposition: form-data; name="prod" \r\n \r\n
-  //   Chrome_Linux \r\n
-  //   BOUNDARY \r\n
-  //   Content-Disposition: form-data; name="ver" \r\n \r\n
-  //   1.2.3.4 \r\n
-  //   BOUNDARY \r\n
-  //
-  //   zero or one:
-  //   Content-Disposition: form-data; name="ptime" \r\n \r\n
-  //   abcdef \r\n
-  //   BOUNDARY \r\n
-  //
-  //   zero or one:
-  //   Content-Disposition: form-data; name="ptype" \r\n \r\n
-  //   abcdef \r\n
-  //   BOUNDARY \r\n
-  //
-  //   zero or one:
-  //   Content-Disposition: form-data; name="lsb-release" \r\n \r\n
-  //   abcdef \r\n
-  //   BOUNDARY \r\n
-  //
-  //   zero or one:
-  //   Content-Disposition: form-data; name="oom-size" \r\n \r\n
-  //   1234567890 \r\n
-  //   BOUNDARY \r\n
-  //
-  //   zero or more (up to CrashKeyStorage::num_entries = 64):
-  //   Content-Disposition: form-data; name=crash-key-name \r\n
-  //   crash-key-value \r\n
-  //   BOUNDARY \r\n
-  //
-  //   Content-Disposition: form-data; name="dump"; filename="dump" \r\n
-  //   Content-Type: application/octet-stream \r\n \r\n
-  //   <dump contents>
-  //   \r\n BOUNDARY -- \r\n
-
-  MimeWriter writer(temp_file_fd, mime_boundary);
-  {
-    writer.AddBoundary();
-    if (info.pid > 0) {
-      char pid_value_buf[kUint64StringSize];
-      uint64_t pid_value_len = my_uint64_len(info.pid);
-      my_uint64tos(pid_value_buf, info.pid, pid_value_len);
-      static const char pid_key_name[] = "pid";
-      writer.AddPairData(pid_key_name, sizeof(pid_key_name) - 1, pid_value_buf,
-                         pid_value_len);
-      writer.AddBoundary();
-    }
-    writer.Flush();
-  }
-
-  if (info.process_start_time > 0) {
-    struct kernel_timeval tv;
-    if (!sys_gettimeofday(&tv, nullptr)) {
-      uint64_t time = kernel_timeval_to_ms(&tv);
-      if (time > info.process_start_time) {
-        time -= info.process_start_time;
-        char time_str[kUint64StringSize];
-        const unsigned time_len = my_uint64_len(time);
-        my_uint64tos(time_str, time, time_len);
-
-        static const char process_time_msg[] = "ptime";
-        writer.AddPairData(process_time_msg, sizeof(process_time_msg) - 1,
-                           time_str, time_len);
-        writer.AddBoundary();
-        writer.Flush();
-      }
-    }
-  }
-
-  if (info.distro_length) {
-    static const char distro_msg[] = "lsb-release";
-    writer.AddPairString(distro_msg, info.distro);
-    writer.AddBoundary();
-    writer.Flush();
-  }
-
-  if (info.oom_size) {
-    char oom_size_str[kUint64StringSize];
-    const unsigned oom_size_len = my_uint64_len(info.oom_size);
-    my_uint64tos(oom_size_str, info.oom_size, oom_size_len);
-    static const char oom_size_msg[] = "oom-size";
-    writer.AddPairData(oom_size_msg, sizeof(oom_size_msg) - 1, oom_size_str,
-                       oom_size_len);
-    writer.AddBoundary();
-    writer.Flush();
-  }
-
-  if (info.crash_keys) {
-    CrashKeyStorage::Iterator crash_key_iterator(*info.crash_keys);
-    const CrashKeyStorage::Entry* entry;
-    while ((entry = crash_key_iterator.Next())) {
-      writer.AddPairString(entry->key, entry->value);
-      writer.AddBoundary();
-      writer.Flush();
-    }
-  }
-
-  writer.AddFileContents(g_dump_msg, dump_data, dump_size);
-  writer.AddEnd();
-  writer.Flush();
-
-  IGNORE_RET(sys_close(temp_file_fd));
-
-  if (!info.upload)
-    return;
-
-  const pid_t child = sys_fork();
-  if (!child) {
-    // Spawned helper process.
-    //
-    // This code is called both when a browser is crashing (in which case,
-    // nothing really matters any more) and when a renderer/plugin crashes, in
-    // which case we need to continue.
-    //
-    // Since we are a multithreaded app, if we were just to fork(), we might
-    // grab file descriptors which have just been created in another thread and
-    // hold them open for too long.
-    //
-    // Thus, we have to loop and try and close everything.
-    const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0);
-    if (fd < 0) {
-      for (unsigned i = 3; i < 8192; ++i)
-        IGNORE_RET(sys_close(i));
-    } else {
-      google_breakpad::DirectoryReader reader(fd);
-      const char* name;
-      while (reader.GetNextEntry(&name)) {
-        int i;
-        if (my_strtoui(&i, name) && i > 2 && i != fd)
-          IGNORE_RET(sys_close(i));
-        reader.PopEntry();
-      }
-
-      IGNORE_RET(sys_close(fd));
-    }
-
-    IGNORE_RET(sys_setsid());
-
-    // Leave one end of a pipe in the upload process and watch for it getting
-    // closed by the upload process exiting.
-    int fds[2];
-    if (sys_pipe(fds) >= 0) {
-      const pid_t upload_child = sys_fork();
-      if (!upload_child) {
-        // Upload process.
-        IGNORE_RET(sys_close(fds[0]));
-        IGNORE_RET(sys_dup2(fds[1], 3));
-        ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf,
-                                     &allocator);
-      }
-
-      // Helper process.
-      if (upload_child > 0) {
-        IGNORE_RET(sys_close(fds[1]));
-
-        const size_t kCrashIdLength = 36;
-        char id_buf[kCrashIdLength + 1];
-        size_t bytes_read =
-            WaitForCrashReportUploadProcess(fds[0], kCrashIdLength, id_buf);
-        HandleCrashReportId(id_buf, bytes_read, kCrashIdLength);
-
-        if (sys_waitpid(upload_child, nullptr, WNOHANG) == 0) {
-          // Upload process is still around, kill it.
-          sys_kill(upload_child, SIGKILL);
-        }
-      }
-    }
-
-    // Helper process.
-    IGNORE_RET(sys_unlink(info.filename));
-    IGNORE_RET(sys_unlink(temp_file));
-    sys__exit(0);
-  }
-
-  // Main browser process.
-  if (child <= 0)
-    return;
-  (void)HANDLE_EINTR(sys_waitpid(child, nullptr, 0));
-}
-
-size_t WriteLog(const char* buf, size_t nbytes) {
-  return sys_write(2, buf, nbytes);
-}
-
-size_t WriteNewline() {
-  return WriteLog("\n", 1);
-}
-
-}  // namespace crash_reporter

+ 0 - 46
shell/common/crash_reporter/linux/crash_dump_handler.h

@@ -1,46 +0,0 @@
-// Copyright (c) 2014 GitHub, Inc.
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#ifndef SHELL_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_
-#define SHELL_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "base/macros.h"
-#include "breakpad/src/common/simple_string_dictionary.h"
-
-namespace crash_reporter {
-
-typedef google_breakpad::NonAllocatingMap<256, 256, 64> CrashKeyStorage;
-
-// BreakpadInfo describes a crash report.
-// The minidump information can either be contained in a file descriptor (fd) or
-// in a file (whose path is in filename).
-struct BreakpadInfo {
-  int fd;                       // File descriptor to the Breakpad dump data.
-  const char* filename;         // Path to the Breakpad dump data.
-  const char* distro;           // Linux distro string.
-  unsigned distro_length;       // Length of |distro|.
-  bool upload;                  // Whether to upload or save crash dump.
-  uint64_t process_start_time;  // Uptime of the crashing process.
-  size_t oom_size;              // Amount of memory requested if OOM.
-  uint64_t pid;                 // PID where applicable.
-  const char* upload_url;       // URL to upload the minidump.
-  CrashKeyStorage* crash_keys;
-};
-
-void HandleCrashDump(const BreakpadInfo& info);
-
-size_t WriteLog(const char* buf, size_t nbytes);
-size_t WriteNewline();
-
-// Global variable storing the path of upload log.
-extern char g_crash_log_path[256];
-
-}  // namespace crash_reporter
-
-#endif  // SHELL_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_

+ 0 - 80
shell/common/crash_reporter/win/crash_service_main.cc

@@ -1,80 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "shell/common/crash_reporter/win/crash_service_main.h"
-
-#include <string>
-
-#include "base/at_exit.h"
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "shell/common/crash_reporter/crash_reporter.h"
-#include "third_party/crashpad/crashpad/handler/handler_main.h"
-
-namespace crash_service {
-
-namespace {
-
-const wchar_t kStandardLogFile[] = L"operation_log.txt";
-
-void InvalidParameterHandler(const wchar_t*,
-                             const wchar_t*,
-                             const wchar_t*,
-                             unsigned int,
-                             uintptr_t) {
-  // noop.
-}
-
-bool CreateCrashServiceDirectory(const base::FilePath& temp_dir) {
-  if (!base::PathExists(temp_dir)) {
-    if (!base::CreateDirectory(temp_dir))
-      return false;
-  }
-  return true;
-}
-
-void RemoveArgs(std::vector<char*>* args) {
-  args->erase(
-      std::remove_if(args->begin(), args->end(), [](const std::string& str) {
-        return base::StartsWith(str, "--type", base::CompareCase::SENSITIVE) ||
-               base::StartsWith(
-                   str,
-                   std::string("--") + crash_reporter::kCrashesDirectoryKey,
-                   base::CompareCase::INSENSITIVE_ASCII);
-      }));
-}
-
-}  // namespace.
-
-int Main(std::vector<char*>* args) {
-  // Ignore invalid parameter errors.
-  _set_invalid_parameter_handler(InvalidParameterHandler);
-
-  // Initialize all Chromium things.
-  base::AtExitManager exit_manager;
-  base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-  // We use/create a directory under the user's temp folder, for logging.
-  base::FilePath operating_dir(
-      cmd_line->GetSwitchValueNative(crash_reporter::kCrashesDirectoryKey));
-  CreateCrashServiceDirectory(operating_dir);
-  base::FilePath log_file_path = operating_dir.Append(kStandardLogFile);
-
-  // Logging to stderr (to help with debugging failures on the
-  // buildbots) and to a file.
-  logging::LoggingSettings settings;
-  settings.logging_dest = logging::LOG_TO_ALL;
-  settings.log_file_path = log_file_path.value().c_str();
-  logging::InitLogging(settings);
-  // Logging with pid, tid and timestamp.
-  logging::SetLogItems(true, true, true, false);
-
-  // Crashpad cannot handle unknown arguments, so we need to remove it
-  RemoveArgs(args);
-  return crashpad::HandlerMain(args->size(), args->data(), nullptr);
-}
-
-}  // namespace crash_service

+ 0 - 17
shell/common/crash_reporter/win/crash_service_main.h

@@ -1,17 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#ifndef SHELL_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
-#define SHELL_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_
-
-#include <vector>
-
-namespace crash_service {
-
-// Program entry, should be called by main();
-int Main(std::vector<char*>* args);
-
-}  // namespace crash_service
-
-#endif  // SHELL_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_MAIN_H_

+ 0 - 4
shell/common/electron_constants.cc

@@ -27,10 +27,6 @@ const char kSecureProtocolDescription[] =
     "The connection to this site is using a strong protocol version "
     "and cipher suite.";
 
-#if defined(OS_WIN)
-const char kCrashpadPipeName[] = "ELECTRON_CRASHPAD_PIPE_NAME";
-#endif
-
 #if BUILDFLAG(ENABLE_RUN_AS_NODE)
 const char kRunAsNode[] = "ELECTRON_RUN_AS_NODE";
 #endif

+ 0 - 5
shell/common/electron_constants.h

@@ -28,11 +28,6 @@ extern const char kValidCertificateDescription[];
 extern const char kSecureProtocol[];
 extern const char kSecureProtocolDescription[];
 
-#if defined(OS_WIN)
-// Crashpad pipe name.
-extern const char kCrashpadPipeName[];
-#endif
-
 #if BUILDFLAG(ENABLE_RUN_AS_NODE)
 extern const char kRunAsNode[];
 #endif

+ 5 - 3
shell/browser/electron_paths.h → shell/common/electron_paths.h

@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SHELL_BROWSER_ELECTRON_PATHS_H_
-#define SHELL_BROWSER_ELECTRON_PATHS_H_
+#ifndef SHELL_COMMON_ELECTRON_PATHS_H_
+#define SHELL_COMMON_ELECTRON_PATHS_H_
 
 #include "base/base_paths.h"
 
@@ -30,6 +30,8 @@ enum {
   DIR_APP_DATA,  // Application Data directory under the user profile.
 #endif
 
+  DIR_CRASH_DUMPS,  // c.f. chrome::DIR_CRASH_DUMPS
+
   PATH_END,  // End of new paths. Those that follow redirect to base::DIR_*
 
 #if !defined(OS_LINUX)
@@ -47,4 +49,4 @@ static_assert(PATH_START < PATH_END, "invalid PATH boundaries");
 
 }  // namespace electron
 
-#endif  // SHELL_BROWSER_ELECTRON_PATHS_H_
+#endif  // SHELL_COMMON_ELECTRON_PATHS_H_

+ 21 - 0
shell/common/gin_converters/time_converter.cc

@@ -0,0 +1,21 @@
+// Copyright (c) 2020 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "shell/common/gin_converters/time_converter.h"
+
+#include "base/time/time.h"
+
+namespace gin {
+
+v8::Local<v8::Value> Converter<base::Time>::ToV8(v8::Isolate* isolate,
+                                                 const base::Time& val) {
+  v8::Local<v8::Value> date;
+  if (v8::Date::New(isolate->GetCurrentContext(), val.ToJsTime())
+          .ToLocal(&date))
+    return date;
+  else
+    return v8::Null(isolate);
+}
+
+}  // namespace gin

+ 25 - 0
shell/common/gin_converters/time_converter.h

@@ -0,0 +1,25 @@
+// Copyright (c) 2020 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_COMMON_GIN_CONVERTERS_TIME_CONVERTER_H_
+#define SHELL_COMMON_GIN_CONVERTERS_TIME_CONVERTER_H_
+
+#include <utility>
+
+#include "gin/converter.h"
+
+namespace base {
+class Time;
+}
+
+namespace gin {
+
+template <>
+struct Converter<base::Time> {
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const base::Time& val);
+};
+
+}  // namespace gin
+
+#endif  // SHELL_COMMON_GIN_CONVERTERS_TIME_CONVERTER_H_

+ 2 - 1
shell/common/node_bindings.cc

@@ -36,6 +36,7 @@
   V(atom_browser_auto_updater)       \
   V(atom_browser_browser_view)       \
   V(atom_browser_content_tracing)    \
+  V(atom_browser_crash_reporter)     \
   V(atom_browser_debugger)           \
   V(atom_browser_dialog)             \
   V(atom_browser_download_item)      \
@@ -59,7 +60,6 @@
   V(atom_common_asar)                \
   V(atom_common_clipboard)           \
   V(atom_common_command_line)        \
-  V(atom_common_crash_reporter)      \
   V(atom_common_features)            \
   V(atom_common_native_image)        \
   V(atom_common_native_theme)        \
@@ -68,6 +68,7 @@
   V(atom_common_shell)               \
   V(atom_common_v8_util)             \
   V(atom_renderer_context_bridge)    \
+  V(atom_renderer_crash_reporter)    \
   V(atom_renderer_ipc)               \
   V(atom_renderer_web_frame)
 

+ 2 - 0
shell/common/options_switches.cc

@@ -280,6 +280,8 @@ const char kEnableSpellcheck[] = "enable-spellcheck";
 const char kEnableRemoteModule[] = "enable-remote-module";
 #endif
 
+const char kGlobalCrashKeys[] = "global-crash-keys";
+
 }  // namespace switches
 
 }  // namespace electron

+ 2 - 0
shell/common/options_switches.h

@@ -147,6 +147,8 @@ extern const char kEnableSpellcheck[];
 extern const char kEnableRemoteModule[];
 #endif
 
+extern const char kGlobalCrashKeys[];
+
 }  // namespace switches
 
 }  // namespace electron

+ 42 - 0
shell/renderer/api/electron_api_crash_reporter_renderer.cc

@@ -0,0 +1,42 @@
+// Copyright (c) 2020 Slack Technologies, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "shell/common/crash_keys.h"
+#include "shell/common/gin_helper/dictionary.h"
+#include "shell/common/node_includes.h"
+
+namespace {
+
+v8::Local<v8::Value> GetParameters(v8::Isolate* isolate) {
+  std::map<std::string, std::string> keys;
+#if !defined(MAS_BUILD)
+  electron::crash_keys::GetCrashKeys(&keys);
+#endif
+  return gin::ConvertToV8(isolate, keys);
+}
+
+#if defined(MAS_BUILD)
+void SetCrashKeyStub(const std::string& key, const std::string& value) {}
+void ClearCrashKeyStub(const std::string& key) {}
+#endif
+
+void Initialize(v8::Local<v8::Object> exports,
+                v8::Local<v8::Value> unused,
+                v8::Local<v8::Context> context,
+                void* priv) {
+  gin_helper::Dictionary dict(context->GetIsolate(), exports);
+#if defined(MAS_BUILD)
+  dict.SetMethod("addExtraParameter", &SetCrashKeyStub);
+  dict.SetMethod("removeExtraParameter", &ClearCrashKeyStub);
+#else
+  dict.SetMethod("addExtraParameter", &electron::crash_keys::SetCrashKey);
+  dict.SetMethod("removeExtraParameter", &electron::crash_keys::ClearCrashKey);
+#endif
+  dict.SetMethod("getParameters", &GetParameters);
+}
+
+}  // namespace
+
+NODE_LINKED_MODULE_CONTEXT_AWARE(atom_renderer_crash_reporter, Initialize)

+ 0 - 8
shell/renderer/electron_api_service_impl.cc

@@ -203,14 +203,6 @@ void ElectronApiServiceImpl::DereferenceRemoteJSCallback(
 }
 #endif
 
-void ElectronApiServiceImpl::UpdateCrashpadPipeName(
-    const std::string& pipe_name) {
-#if defined(OS_WIN)
-  std::unique_ptr<base::Environment> env(base::Environment::Create());
-  env->SetVar(kCrashpadPipeName, pipe_name);
-#endif
-}
-
 void ElectronApiServiceImpl::TakeHeapSnapshot(
     mojo::ScopedHandle file,
     TakeHeapSnapshotCallback callback) {

+ 0 - 1
shell/renderer/electron_api_service_impl.h

@@ -37,7 +37,6 @@ class ElectronApiServiceImpl : public mojom::ElectronRenderer,
   void DereferenceRemoteJSCallback(const std::string& context_id,
                                    int32_t object_id) override;
 #endif
-  void UpdateCrashpadPipeName(const std::string& pipe_name) override;
   void TakeHeapSnapshot(mojo::ScopedHandle file,
                         TakeHeapSnapshotCallback callback) override;
 

+ 498 - 344
spec-main/api-crash-reporter-spec.ts

@@ -1,18 +1,19 @@
 import { expect } from 'chai';
 import * as childProcess from 'child_process';
-import * as fs from 'fs';
 import * as http from 'http';
-import * as multiparty from 'multiparty';
+import * as Busboy from 'busboy';
 import * as path from 'path';
 import { ifdescribe, ifit } from './spec-helpers';
-import * as temp from 'temp';
-import * as url from 'url';
-import { ipcMain, app, BrowserWindow, crashReporter, BrowserWindowConstructorOptions } from 'electron';
+import { app, crashReporter } from 'electron';
 import { AddressInfo } from 'net';
-import { closeWindow, closeAllWindows } from './window-helpers';
 import { EventEmitter } from 'events';
+import * as fs from 'fs';
+import * as v8 from 'v8';
+import * as uuid from 'uuid';
+import * as rimraf from 'rimraf';
 
-temp.track();
+const isWindowsOnArm = process.platform === 'win32' && process.arch === 'arm64';
+const isLinuxOnArm = process.platform === 'linux' && process.arch.includes('arm');
 
 const afterTest: ((() => void) | (() => Promise<void>))[] = [];
 async function cleanup () {
@@ -23,417 +24,570 @@ async function cleanup () {
   afterTest.length = 0;
 }
 
-// TODO(alexeykuzmin): [Ch66] This test fails on Linux. Fix it and enable back.
-ifdescribe(!process.mas && !process.env.DISABLE_CRASH_REPORTER_TESTS && process.platform !== 'linux')('crashReporter module', function () {
-  let originalTempDirectory: string;
-  let tempDirectory = null;
-  const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
+type CrashInfo = {
+  prod: string
+  ver: string
+  process_type: string // eslint-disable-line camelcase
+  ptype: string
+  platform: string
+  _productName: string
+  _version: string
+  upload_file_minidump: Buffer // eslint-disable-line camelcase
+  mainProcessSpecific: 'mps' | undefined
+  rendererSpecific: 'rs' | undefined
+  globalParam: 'globalValue' | undefined
+  addedThenRemoved: 'to-be-removed' | undefined
+  longParam: string | undefined
+}
+
+function checkCrash (expectedProcessType: string, fields: CrashInfo) {
+  expect(String(fields.prod)).to.equal('Electron', 'prod');
+  expect(String(fields.ver)).to.equal(process.versions.electron, 'ver');
+  expect(String(fields.ptype)).to.equal(expectedProcessType, 'ptype');
+  expect(String(fields.process_type)).to.equal(expectedProcessType, 'process_type');
+  expect(String(fields.platform)).to.equal(process.platform, 'platform');
+  expect(String(fields._productName)).to.equal('Zombies', '_productName');
+  expect(String(fields._version)).to.equal(app.getVersion(), '_version');
+  expect(fields.upload_file_minidump).to.be.an.instanceOf(Buffer);
+
+  // TODO(nornagon): minidumps are sometimes (not always) turning up empty on
+  // 32-bit Linux.  Figure out why.
+  if (!(process.platform === 'linux' && process.arch === 'ia32')) {
+    expect(fields.upload_file_minidump.length).to.be.greaterThan(0);
+  }
+}
+
+const startRemoteControlApp = async () => {
+  const appPath = path.join(__dirname, 'fixtures', 'apps', 'remote-control');
+  const appProcess = childProcess.spawn(process.execPath, [appPath]);
+  appProcess.stderr.on('data', d => {
+    process.stderr.write(d);
+  });
+  const port = await new Promise<number>(resolve => {
+    appProcess.stdout.on('data', d => {
+      const m = /Listening: (\d+)/.exec(d.toString());
+      if (m && m[1] != null) {
+        resolve(Number(m[1]));
+      }
+    });
+  });
+  function remoteEval (js: string): any {
+    return new Promise((resolve, reject) => {
+      const req = http.request({
+        host: '127.0.0.1',
+        port,
+        method: 'POST'
+      }, res => {
+        const chunks = [] as Buffer[];
+        res.on('data', chunk => { chunks.push(chunk); });
+        res.on('end', () => {
+          // NOTE: v8.deserialize has existed since node 8.0.0, but apparently
+          // the typings we're using don't recognize it.
+          const ret = (v8 as any).deserialize(Buffer.concat(chunks));
+          if (Object.prototype.hasOwnProperty.call(ret, 'error')) {
+            reject(new Error(`remote error: ${ret.error}\n\nTriggered at:`));
+          } else {
+            resolve(ret.result);
+          }
+        });
+      });
+      req.write(js);
+      req.end();
+    });
+  }
+  function remotely (script: Function, ...args: any[]): Promise<any> {
+    return remoteEval(`(${script})(...${JSON.stringify(args)})`);
+  }
+  afterTest.push(() => { appProcess.kill('SIGINT'); });
+  return { remoteEval, remotely };
+};
+
+const startServer = async () => {
+  const crashes: CrashInfo[] = [];
+  function getCrashes () { return crashes; }
+  const emitter = new EventEmitter();
+  function waitForCrash (): Promise<CrashInfo> {
+    return new Promise(resolve => {
+      emitter.once('crash', (crash) => {
+        resolve(crash);
+      });
+    });
+  }
+
+  const server = http.createServer((req, res) => {
+    const busboy = new Busboy({ headers: req.headers });
+    const fields = {} as Record<string, any>;
+    const files = {} as Record<string, Buffer>;
+    busboy.on('file', (fieldname, file) => {
+      const chunks = [] as Array<Buffer>;
+      file.on('data', (chunk) => {
+        chunks.push(chunk);
+      });
+      file.on('end', () => {
+        files[fieldname] = Buffer.concat(chunks);
+      });
+    });
+    busboy.on('field', (fieldname, val) => {
+      fields[fieldname] = val;
+    });
+    busboy.on('finish', () => {
+      // breakpad id must be 16 hex digits.
+      const reportId = Math.random().toString(16).split('.')[1].padStart(16, '0');
+      res.end(reportId, async () => {
+        req.socket.destroy();
+        emitter.emit('crash', { ...fields, ...files });
+      });
+    });
+    req.pipe(busboy);
+  });
+
+  await new Promise(resolve => {
+    server.listen(0, '127.0.0.1', () => { resolve(); });
+  });
+
+  const port = (server.address() as AddressInfo).port;
+
+  afterTest.push(() => { server.close(); });
 
-  before(() => {
-    tempDirectory = temp.mkdirSync('electronCrashReporterSpec-');
-    originalTempDirectory = app.getPath('temp');
-    app.setPath('temp', tempDirectory);
+  return { getCrashes, port, waitForCrash };
+};
+
+function runApp (appPath: string, args: Array<string> = []) {
+  const appProcess = childProcess.spawn(process.execPath, [appPath, ...args]);
+  return new Promise(resolve => {
+    appProcess.once('exit', resolve);
   });
+}
 
-  after(() => {
-    app.setPath('temp', originalTempDirectory);
+function runCrashApp (crashType: string, port: number, extraArgs: Array<string> = []) {
+  const appPath = path.join(__dirname, 'fixtures', 'apps', 'crash');
+  return runApp(appPath, [
+    `--crash-type=${crashType}`,
+    `--crash-reporter-url=http://127.0.0.1:${port}`,
+    ...extraArgs
+  ]);
+}
+
+function waitForNewFileInDir (dir: string): Promise<string[]> {
+  function readdirIfPresent (dir: string): string[] {
     try {
-      temp.cleanupSync();
+      return fs.readdirSync(dir);
     } catch (e) {
-      // ignore.
-      console.warn(e.stack);
+      return [];
     }
+  }
+  const initialFiles = readdirIfPresent(dir);
+  return new Promise(resolve => {
+    const ivl = setInterval(() => {
+      const newCrashFiles = readdirIfPresent(dir).filter(f => !initialFiles.includes(f));
+      if (newCrashFiles.length) {
+        clearInterval(ivl);
+        resolve(newCrashFiles);
+      }
+    }, 1000);
   });
+}
 
+// TODO(nornagon): Fix tests on linux/arm.
+ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_TESTS)('crashReporter module', function () {
   afterEach(cleanup);
 
-  it('should send minidump when node processes crash', async () => {
-    const { port, waitForCrash } = await startServer();
-
-    const crashesDir = path.join(app.getPath('temp'), `${app.name} Crashes`);
-    const version = app.getVersion();
-    const crashPath = path.join(fixtures, 'module', 'crash.js');
-    childProcess.fork(crashPath, [port.toString(), version, crashesDir], { silent: true });
-    const crash = await waitForCrash();
-    checkCrash('node', crash);
-  });
+  describe('should send minidump', () => {
+    it('when renderer crashes', async () => {
+      const { port, waitForCrash } = await startServer();
+      runCrashApp('renderer', port);
+      const crash = await waitForCrash();
+      checkCrash('renderer', crash);
+      expect(crash.mainProcessSpecific).to.be.undefined();
+    });
 
-  const generateSpecs = (description: string, browserWindowOpts: BrowserWindowConstructorOptions) => {
-    describe(description, () => {
-      let w: BrowserWindow;
+    it('when sandboxed renderer crashes', async () => {
+      const { port, waitForCrash } = await startServer();
+      runCrashApp('sandboxed-renderer', port);
+      const crash = await waitForCrash();
+      checkCrash('renderer', crash);
+      expect(crash.mainProcessSpecific).to.be.undefined();
+    });
 
-      beforeEach(() => {
-        w = new BrowserWindow(Object.assign({ show: false }, browserWindowOpts));
-      });
+    // TODO(nornagon): Minidump generation in main/node process on Linux/Arm is
+    // broken (//components/crash prints "Failed to generate minidump"). Figure
+    // out why.
+    ifit(!isLinuxOnArm)('when main process crashes', async () => {
+      const { port, waitForCrash } = await startServer();
+      runCrashApp('main', port);
+      const crash = await waitForCrash();
+      checkCrash('browser', crash);
+      expect(crash.mainProcessSpecific).to.equal('mps');
+    });
 
-      afterEach(async () => {
-        await closeWindow(w);
-        w = null as unknown as BrowserWindow;
-      });
+    ifit(!isLinuxOnArm)('when a node process crashes', async () => {
+      const { port, waitForCrash } = await startServer();
+      runCrashApp('node', port);
+      const crash = await waitForCrash();
+      checkCrash('node', crash);
+      expect(crash.mainProcessSpecific).to.be.undefined();
+      expect(crash.rendererSpecific).to.be.undefined();
+    });
 
-      it('should send minidump when renderer crashes', async () => {
+    describe('with extra parameters', () => {
+      it('when renderer crashes', async () => {
         const { port, waitForCrash } = await startServer();
-        w.loadFile(path.join(fixtures, 'api', 'crash.html'), { query: { port: port.toString() } });
+        runCrashApp('renderer', port, ['--set-extra-parameters-in-renderer']);
         const crash = await waitForCrash();
         checkCrash('renderer', crash);
+        expect(crash.mainProcessSpecific).to.be.undefined();
+        expect(crash.rendererSpecific).to.equal('rs');
+        expect(crash.addedThenRemoved).to.be.undefined();
       });
 
-      ifit(!browserWindowOpts.webPreferences!.sandbox)('should send minidump when node processes crash', async function () {
+      it('when sandboxed renderer crashes', async () => {
         const { port, waitForCrash } = await startServer();
-        const crashesDir = path.join(app.getPath('temp'), `${app.name} Crashes`);
-        const version = app.getVersion();
-        const crashPath = path.join(fixtures, 'module', 'crash.js');
-        w.loadFile(path.join(fixtures, 'api', 'crash_child.html'), { query: { port: port.toString(), crashesDir, crashPath, version } });
+        runCrashApp('sandboxed-renderer', port, ['--set-extra-parameters-in-renderer']);
         const crash = await waitForCrash();
-        expect(String((crash as any).newExtra)).to.equal('newExtra');
-        expect((crash as any).removeExtra).to.be.undefined();
-        checkCrash('node', crash);
+        checkCrash('renderer', crash);
+        expect(crash.mainProcessSpecific).to.be.undefined();
+        expect(crash.rendererSpecific).to.equal('rs');
+        expect(crash.addedThenRemoved).to.be.undefined();
       });
+    });
+  });
 
-      describe('when uploadToServer is false', () => {
-        after(() => { crashReporter.setUploadToServer(true); });
-
-        it('should not send minidump', async () => {
-          const { port, getCrashes } = await startServer();
-          crashReporter.setUploadToServer(false);
+  ifdescribe(!isLinuxOnArm)('extra parameter limits', () => {
+    it('should truncate extra values longer than 127 characters', async () => {
+      const { port, waitForCrash } = await startServer();
+      const { remotely } = await startRemoteControlApp();
+      remotely((port: number) => {
+        require('electron').crashReporter.start({
+          submitURL: `http://127.0.0.1:${port}`,
+          ignoreSystemCrashHandler: true,
+          extra: { 'longParam': 'a'.repeat(130) }
+        });
+        setTimeout(() => process.crash());
+      }, port);
+      const crash = await waitForCrash();
+      expect(crash).to.have.property('longParam', 'a'.repeat(127));
+    });
 
-          let crashesDir = crashReporter.getCrashesDirectory();
-          const existingDumpFiles = new Set();
-          // crashpad puts the dump files in the "completed" subdirectory
-          if (process.platform === 'darwin') {
-            crashesDir = path.join(crashesDir, 'completed');
-          } else {
-            crashesDir = path.join(crashesDir, 'reports');
+    it('should omit extra keys with names longer than the maximum', async () => {
+      const kKeyLengthMax = 39;
+      const { port, waitForCrash } = await startServer();
+      const { remotely } = await startRemoteControlApp();
+      remotely((port: number, kKeyLengthMax: number) => {
+        require('electron').crashReporter.start({
+          submitURL: `http://127.0.0.1:${port}`,
+          ignoreSystemCrashHandler: true,
+          extra: {
+            ['a'.repeat(kKeyLengthMax + 10)]: 'value',
+            ['b'.repeat(kKeyLengthMax)]: 'value',
+            'not-long': 'not-long-value'
           }
+        });
+        require('electron').crashReporter.addExtraParameter('c'.repeat(kKeyLengthMax + 10), 'value');
+        setTimeout(() => process.crash());
+      }, port, kKeyLengthMax);
+      const crash = await waitForCrash();
+      expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax + 10));
+      expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax));
+      expect(crash).to.have.property('b'.repeat(kKeyLengthMax), 'value');
+      expect(crash).to.have.property('not-long', 'not-long-value');
+      expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax + 10));
+      expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax));
+    });
+  });
 
-          const crashUrl = url.format({
-            protocol: 'file',
-            pathname: path.join(fixtures, 'api', 'crash.html'),
-            search: `?port=${port}&skipUpload=1`
-          });
-          w.loadURL(crashUrl);
-
-          await new Promise(resolve => {
-            ipcMain.once('list-existing-dumps', (event) => {
-              fs.readdir(crashesDir, (err, files) => {
-                if (!err) {
-                  for (const file of files) {
-                    if (/\.dmp$/.test(file)) {
-                      existingDumpFiles.add(file);
-                    }
-                  }
-                }
-                event.returnValue = null; // allow the renderer to crash
-                resolve();
-              });
-            });
-          });
+  describe('globalExtra', () => {
+    ifit(!isLinuxOnArm)('should be sent with main process dumps', async () => {
+      const { port, waitForCrash } = await startServer();
+      runCrashApp('main', port, ['--add-global-param=globalParam:globalValue']);
+      const crash = await waitForCrash();
+      expect(crash.globalParam).to.equal('globalValue');
+    });
 
-          const dumpFileCreated = async () => {
-            async function getDumps () {
-              const files = await fs.promises.readdir(crashesDir);
-              return files.filter((file) => /\.dmp$/.test(file) && !existingDumpFiles.has(file));
-            }
-            for (let i = 0; i < 30; i++) {
-              const dumps = await getDumps();
-              if (dumps.length) {
-                return path.join(crashesDir, dumps[0]);
-              }
-              await new Promise(resolve => setTimeout(resolve, 1000));
-            }
-          };
-
-          const dumpFile = await dumpFileCreated();
-          expect(dumpFile).to.be.a('string');
-
-          // dump file should not be deleted when not uploading, so we wait
-          // 1s and assert it still exists
-          await new Promise(resolve => setTimeout(resolve, 1000));
-          expect(fs.existsSync(dumpFile!)).to.be.true();
-
-          // the server should not have received any crashes.
-          expect(getCrashes()).to.be.empty();
-        });
-      });
+    it('should be sent with renderer process dumps', async () => {
+      const { port, waitForCrash } = await startServer();
+      runCrashApp('renderer', port, ['--add-global-param=globalParam:globalValue']);
+      const crash = await waitForCrash();
+      expect(crash.globalParam).to.equal('globalValue');
+    });
 
-      it('should send minidump with updated extra parameters', async function () {
-        const { port, waitForCrash } = await startServer();
+    it('should be sent with sandboxed renderer process dumps', async () => {
+      const { port, waitForCrash } = await startServer();
+      runCrashApp('sandboxed-renderer', port, ['--add-global-param=globalParam:globalValue']);
+      const crash = await waitForCrash();
+      expect(crash.globalParam).to.equal('globalValue');
+    });
 
-        const crashUrl = url.format({
-          protocol: 'file',
-          pathname: path.join(fixtures, 'api', 'crash-restart.html'),
-          search: `?port=${port}`
-        });
-        w.loadURL(crashUrl);
-        const crash = await waitForCrash();
-        checkCrash('renderer', crash);
-      });
+    ifit(!isLinuxOnArm)('should not be overridden by extra in main process', async () => {
+      const { port, waitForCrash } = await startServer();
+      runCrashApp('main', port, ['--add-global-param=mainProcessSpecific:global']);
+      const crash = await waitForCrash();
+      expect(crash.mainProcessSpecific).to.equal('global');
     });
-  };
 
-  generateSpecs('without sandbox', {
-    webPreferences: {
-      nodeIntegration: true
-    }
+    ifit(!isLinuxOnArm)('should not be overridden by extra in renderer process', async () => {
+      const { port, waitForCrash } = await startServer();
+      runCrashApp('main', port, ['--add-global-param=rendererSpecific:global']);
+      const crash = await waitForCrash();
+      expect(crash.rendererSpecific).to.equal('global');
+    });
   });
-  generateSpecs('with sandbox', {
-    webPreferences: {
-      sandbox: true,
-      preload: path.join(fixtures, 'module', 'preload-sandbox.js')
-    }
+
+  // TODO(nornagon): also test crashing main / sandboxed renderers.
+  ifit(!isWindowsOnArm)('should not send a minidump when uploadToServer is false', async () => {
+    const { port, waitForCrash, getCrashes } = await startServer();
+    waitForCrash().then(() => expect.fail('expected not to receive a dump'));
+    await runCrashApp('renderer', port, ['--no-upload']);
+    // wait a sec in case the crash reporter is about to upload a crash
+    await new Promise(resolve => setTimeout(resolve, 1000));
+    expect(getCrashes()).to.have.length(0);
   });
 
-  describe('start(options)', () => {
-    it('requires that the companyName and submitURL options be specified', () => {
+  describe('start() option validation', () => {
+    it('requires that the submitURL option be specified', () => {
       expect(() => {
-        crashReporter.start({ companyName: 'Missing submitURL' } as any);
+        crashReporter.start({} as any);
       }).to.throw('submitURL is a required option to crashReporter.start');
-      expect(() => {
-        crashReporter.start({ submitURL: 'Missing companyName' } as any);
-      }).to.throw('companyName is a required option to crashReporter.start');
     });
-    it('can be called multiple times', () => {
-      expect(() => {
-        crashReporter.start({
-          companyName: 'Umbrella Corporation',
-          submitURL: 'http://127.0.0.1/crashes'
-        });
 
-        crashReporter.start({
-          companyName: 'Umbrella Corporation 2',
-          submitURL: 'http://127.0.0.1/more-crashes'
-        });
-      }).to.not.throw();
-    });
-  });
-
-  describe('getCrashesDirectory', () => {
-    it('correctly returns the directory', () => {
-      const crashesDir = crashReporter.getCrashesDirectory();
-      const dir = path.join(app.getPath('temp'), 'Electron Test Main Crashes');
-      expect(crashesDir).to.equal(dir);
+    it('can be called twice', async () => {
+      const { remotely } = await startRemoteControlApp();
+      await expect(remotely(() => {
+        const { crashReporter } = require('electron');
+        crashReporter.start({ submitURL: 'http://127.0.0.1' });
+        crashReporter.start({ submitURL: 'http://127.0.0.1' });
+      })).to.be.fulfilled();
     });
   });
 
   describe('getUploadedReports', () => {
-    it('returns an array of reports', () => {
-      const reports = crashReporter.getUploadedReports();
+    it('returns an array of reports', async () => {
+      const { remotely } = await startRemoteControlApp();
+      await remotely(() => {
+        require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
+      });
+      const reports = await remotely(() => require('electron').crashReporter.getUploadedReports());
       expect(reports).to.be.an('array');
     });
   });
 
-  // TODO(alexeykuzmin): This suite should explicitly
-  // generate several crash reports instead of hoping
-  // that there will be enough of them already.
-  describe('getLastCrashReport', () => {
-    it('correctly returns the most recent report', () => {
-      const reports = crashReporter.getUploadedReports();
-      expect(reports).to.be.an('array');
-      expect(reports).to.have.lengthOf.at.least(2,
-        'There are not enough reports for this test');
-
-      const lastReport = crashReporter.getLastCrashReport();
-      expect(lastReport).to.be.an('object');
-      expect(lastReport.date).to.be.an.instanceOf(Date);
-
-      // Let's find the newest report.
-      const { report: newestReport } = reports.reduce((acc, cur) => {
-        const timestamp = new Date(cur.date).getTime();
-        return (timestamp > acc.timestamp)
-          ? { report: cur, timestamp: timestamp }
-          : acc;
-      }, { timestamp: -Infinity } as { timestamp: number, report?: any });
-      expect(newestReport).to.be.an('object');
-
-      expect(lastReport.date.getTime()).to.be.equal(
-        newestReport.date.getTime(),
-        'Last report is not the newest.');
+  // TODO(nornagon): re-enable on woa
+  ifdescribe(!isWindowsOnArm)('getLastCrashReport', () => {
+    it('returns the last uploaded report', async () => {
+      const { remotely } = await startRemoteControlApp();
+      const { port, waitForCrash } = await startServer();
+
+      // 0. clear the crash reports directory.
+      const dir = await remotely(() => require('electron').app.getPath('crashDumps'));
+      try {
+        rimraf.sync(dir);
+        fs.mkdirSync(dir);
+      } catch (e) { /* ignore */ }
+
+      // 1. start the crash reporter.
+      await remotely((port: number) => {
+        require('electron').crashReporter.start({
+          submitURL: `http://127.0.0.1:${port}`,
+          ignoreSystemCrashHandler: true
+        });
+      }, [port]);
+      // 2. generate a crash in the renderer.
+      remotely(() => {
+        const { BrowserWindow } = require('electron');
+        const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
+        bw.loadURL('about:blank');
+        bw.webContents.executeJavaScript('process.crash()');
+      });
+      await waitForCrash();
+      // 3. get the crash from getLastCrashReport.
+      const firstReport = await remotely(() => require('electron').crashReporter.getLastCrashReport());
+      expect(firstReport).to.not.be.null();
+      expect(firstReport.date).to.be.an.instanceOf(Date);
+      expect((+new Date()) - (+firstReport.date)).to.be.lessThan(30000);
     });
   });
 
   describe('getUploadToServer()', () => {
-    it('returns true when uploadToServer is set to true', function () {
-      crashReporter.start({
-        companyName: 'Umbrella Corporation',
-        submitURL: 'http://127.0.0.1/crashes',
-        uploadToServer: true
-      });
-      expect(crashReporter.getUploadToServer()).to.be.true();
+    it('returns true when uploadToServer is set to true (by default)', async () => {
+      const { remotely } = await startRemoteControlApp();
+
+      await remotely(() => { require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' }); });
+      const uploadToServer = await remotely(() => require('electron').crashReporter.getUploadToServer());
+      expect(uploadToServer).to.be.true();
     });
-    it('returns false when uploadToServer is set to false', function () {
-      crashReporter.start({
-        companyName: 'Umbrella Corporation',
-        submitURL: 'http://127.0.0.1/crashes',
-        uploadToServer: true
-      });
-      crashReporter.setUploadToServer(false);
-      expect(crashReporter.getUploadToServer()).to.be.false();
+
+    it('returns false when uploadToServer is set to false in init', async () => {
+      const { remotely } = await startRemoteControlApp();
+      await remotely(() => { require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false }); });
+      const uploadToServer = await remotely(() => require('electron').crashReporter.getUploadToServer());
+      expect(uploadToServer).to.be.false();
     });
-  });
 
-  describe('setUploadToServer(uploadToServer)', () => {
-    afterEach(closeAllWindows);
-    it('throws an error when called from the renderer process', async () => {
-      const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
-      w.loadURL('about:blank');
-      await expect(
-        w.webContents.executeJavaScript(`require('electron').crashReporter.setUploadToServer(true)`)
-      ).to.eventually.be.rejected();
-      await expect(
-        w.webContents.executeJavaScript(`require('electron').crashReporter.getUploadToServer()`)
-      ).to.eventually.be.rejected();
-    });
-    it('sets uploadToServer false when called with false', function () {
-      crashReporter.start({
-        companyName: 'Umbrella Corporation',
-        submitURL: 'http://127.0.0.1/crashes',
-        uploadToServer: true
-      });
-      crashReporter.setUploadToServer(false);
-      expect(crashReporter.getUploadToServer()).to.be.false();
-    });
-    it('sets uploadToServer true when called with true', function () {
-      crashReporter.start({
-        companyName: 'Umbrella Corporation',
-        submitURL: 'http://127.0.0.1/crashes',
-        uploadToServer: false
-      });
-      crashReporter.setUploadToServer(true);
-      expect(crashReporter.getUploadToServer()).to.be.true();
+    it('is updated by setUploadToServer', async () => {
+      const { remotely } = await startRemoteControlApp();
+      await remotely(() => { require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' }); });
+      await remotely(() => { require('electron').crashReporter.setUploadToServer(false); });
+      expect(await remotely(() => require('electron').crashReporter.getUploadToServer())).to.be.false();
+      await remotely(() => { require('electron').crashReporter.setUploadToServer(true); });
+      expect(await remotely(() => require('electron').crashReporter.getUploadToServer())).to.be.true();
     });
   });
 
-  describe('Parameters', () => {
-    it('returns all of the current parameters', () => {
-      crashReporter.start({
-        companyName: 'Umbrella Corporation',
-        submitURL: 'http://127.0.0.1/crashes'
+  describe('getParameters', () => {
+    it('returns all of the current parameters', async () => {
+      const { remotely } = await startRemoteControlApp();
+      await remotely(() => {
+        require('electron').crashReporter.start({
+          submitURL: 'http://127.0.0.1',
+          extra: { 'extra1': 'hi' }
+        });
       });
-
-      const parameters = crashReporter.getParameters();
-      expect(parameters).to.be.an('object');
+      const parameters = await remotely(() => require('electron').crashReporter.getParameters());
+      expect(parameters).to.have.property('extra1', 'hi');
     });
-    it('adds a parameter to current parameters', function () {
-      crashReporter.start({
-        companyName: 'Umbrella Corporation',
-        submitURL: 'http://127.0.0.1/crashes'
-      });
 
-      crashReporter.addExtraParameter('hello', 'world');
-      expect(crashReporter.getParameters()).to.have.property('hello');
-    });
-    it('removes a parameter from current parameters', function () {
-      crashReporter.start({
-        companyName: 'Umbrella Corporation',
-        submitURL: 'http://127.0.0.1/crashes'
+    it('reflects added and removed parameters', async () => {
+      const { remotely } = await startRemoteControlApp();
+      await remotely(() => {
+        require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
+        require('electron').crashReporter.addExtraParameter('hello', 'world');
       });
+      {
+        const parameters = await remotely(() => require('electron').crashReporter.getParameters());
+        expect(parameters).to.have.property('hello', 'world');
+      }
 
-      crashReporter.addExtraParameter('hello', 'world');
-      expect(crashReporter.getParameters()).to.have.property('hello');
+      await remotely(() => { require('electron').crashReporter.removeExtraParameter('hello'); });
 
-      crashReporter.removeExtraParameter('hello');
-      expect(crashReporter.getParameters()).to.not.have.property('hello');
+      {
+        const parameters = await remotely(() => require('electron').crashReporter.getParameters());
+        expect(parameters).not.to.have.property('hello');
+      }
     });
-  });
 
-  describe('when not started', () => {
-    it('does not prevent process from crashing', (done) => {
-      const appPath = path.join(fixtures, 'api', 'cookie-app');
-      const appProcess = childProcess.spawn(process.execPath, [appPath]);
-      appProcess.once('exit', () => {
-        done();
+    it('can be called in the renderer', async () => {
+      const { remotely } = await startRemoteControlApp();
+      const rendererParameters = await remotely(async () => {
+        const { crashReporter, BrowserWindow } = require('electron');
+        crashReporter.start({ submitURL: 'http://' });
+        const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
+        bw.loadURL('about:blank');
+        await bw.webContents.executeJavaScript(`require('electron').crashReporter.addExtraParameter('hello', 'world')`);
+        return bw.webContents.executeJavaScript(`require('electron').crashReporter.getParameters()`);
       });
+      if (process.platform === 'linux') {
+        // On Linux, 'getParameters' will also include the global parameters,
+        // because breakpad doesn't support global parameters.
+        expect(rendererParameters).to.have.property('hello', 'world');
+      } else {
+        expect(rendererParameters).to.deep.equal({ hello: 'world' });
+      }
+    });
+
+    it('can be called in a node child process', async () => {
+      function slurp (stream: NodeJS.ReadableStream): Promise<string> {
+        return new Promise((resolve, reject) => {
+          const chunks: Buffer[] = [];
+          stream.on('data', chunk => { chunks.push(chunk); });
+          stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
+          stream.on('error', e => reject(e));
+        });
+      }
+      const child = childProcess.fork(path.join(__dirname, 'fixtures', 'module', 'print-crash-parameters.js'), [], { silent: true });
+      const output = await slurp(child.stdout!);
+      expect(JSON.parse(output)).to.deep.equal({ hello: 'world' });
     });
   });
-});
 
-type CrashInfo = {
-  prod: string
-  ver: string
-  process_type: string // eslint-disable-line camelcase
-  platform: string
-  extra1: string
-  extra2: string
-  extra3: undefined
-  _productName: string
-  _companyName: string
-  _version: string
-}
+  describe('crash dumps directory', () => {
+    it('is set by default', () => {
+      expect(app.getPath('crashDumps')).to.be.a('string');
+    });
 
-async function waitForCrashReport () {
-  for (let times = 0; times < 10; times++) {
-    if (crashReporter.getLastCrashReport() != null) {
-      return;
-    }
-    await new Promise(resolve => setTimeout(resolve, 100));
-  }
-  throw new Error('No crash report available');
-}
+    it('is inside the user data dir', () => {
+      expect(app.getPath('crashDumps')).to.include(app.getPath('userData'));
+    });
 
-async function checkReport (reportId: string) {
-  await waitForCrashReport();
-  expect(crashReporter.getLastCrashReport().id).to.equal(reportId);
-  expect(crashReporter.getUploadedReports()).to.be.an('array').that.is.not.empty();
-  expect(crashReporter.getUploadedReports()[0].id).to.equal(reportId);
-}
+    it('matches getCrashesDirectory', async () => {
+      expect(app.getPath('crashDumps')).to.equal(require('electron').crashReporter.getCrashesDirectory());
+    });
 
-function checkCrash (expectedProcessType: string, fields: CrashInfo) {
-  expect(String(fields.prod)).to.equal('Electron');
-  expect(String(fields.ver)).to.equal(process.versions.electron);
-  expect(String(fields.process_type)).to.equal(expectedProcessType);
-  expect(String(fields.platform)).to.equal(process.platform);
-  expect(String(fields.extra1)).to.equal('extra1');
-  expect(String(fields.extra2)).to.equal('extra2');
-  expect(fields.extra3).to.be.undefined();
-  expect(String(fields._productName)).to.equal('Zombies');
-  expect(String(fields._companyName)).to.equal('Umbrella Corporation');
-  expect(String(fields._version)).to.equal(app.getVersion());
-}
+    function crash (processType: string, remotely: Function) {
+      if (processType === 'main') {
+        return remotely(() => {
+          setTimeout(() => { process.crash(); });
+        });
+      } else if (processType === 'renderer') {
+        return remotely(() => {
+          const { BrowserWindow } = require('electron');
+          const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
+          bw.loadURL('about:blank');
+          bw.webContents.executeJavaScript('process.crash()');
+        });
+      } else if (processType === 'sandboxed-renderer') {
+        const preloadPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'sandbox-preload.js');
+        return remotely((preload: string) => {
+          const { BrowserWindow } = require('electron');
+          const bw = new BrowserWindow({ show: false, webPreferences: { sandbox: true, preload } });
+          bw.loadURL('about:blank');
+        }, preloadPath);
+      } else if (processType === 'node') {
+        const crashScriptPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'node-crash.js');
+        return remotely((crashScriptPath: string) => {
+          const { app } = require('electron');
+          const childProcess = require('child_process');
+          const version = app.getVersion();
+          const url = 'http://127.0.0.1';
+          childProcess.fork(crashScriptPath, [url, version], { silent: true });
+        }, crashScriptPath);
+      }
+    }
 
-let crashReporterPort = 0;
-const startServer = async () => {
-  const crashes: CrashInfo[] = [];
-  function getCrashes () { return crashes; }
-  const emitter = new EventEmitter();
-  function waitForCrash (): Promise<CrashInfo> {
-    return new Promise(resolve => {
-      emitter.once('crash', (crash) => {
-        resolve(crash);
-      });
-    });
-  }
+    for (const crashingProcess of ['main', 'renderer', 'sandboxed-renderer', 'node']) {
+      // TODO(nornagon): breakpad on linux disables itself when uploadToServer
+      // is false, so we should figure out a different way to test the crash
+      // dump dir on linux.
+      ifdescribe(process.platform !== 'linux')(`when ${crashingProcess} crashes`, () => {
+        it('stores crashes in the crash dump directory when uploadToServer: false', async () => {
+          const { remotely } = await startRemoteControlApp();
+          const crashesDir = await remotely(() => {
+            const { crashReporter } = require('electron');
+            crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
+            return crashReporter.getCrashesDirectory();
+          });
+          const reportsDir = process.platform === 'darwin' ? path.join(crashesDir, 'completed') : path.join(crashesDir, 'reports');
+          const newFileAppeared = waitForNewFileInDir(reportsDir);
+          crash(crashingProcess, remotely);
+          const newFiles = await newFileAppeared;
+          expect(newFiles).to.have.length(1);
+          expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
+        });
 
-  const server = http.createServer((req, res) => {
-    const form = new multiparty.Form();
-    form.parse(req, (error, fields) => {
-      crashes.push(fields);
-      if (error) throw error;
-      const reportId = 'abc-123-def-456-abc-789-abc-123-abcd';
-      res.end(reportId, async () => {
-        await checkReport(reportId);
-        req.socket.destroy();
-        emitter.emit('crash', fields);
+        it('respects an overridden crash dump directory', async () => {
+          const { remotely } = await startRemoteControlApp();
+          const crashesDir = path.join(app.getPath('temp'), uuid.v4());
+          const remoteCrashesDir = await remotely((crashesDir: string) => {
+            const { crashReporter, app } = require('electron');
+            app.setPath('crashDumps', crashesDir);
+            crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
+            return crashReporter.getCrashesDirectory();
+          }, crashesDir);
+          expect(remoteCrashesDir).to.equal(crashesDir);
+
+          const reportsDir = process.platform === 'darwin' ? path.join(crashesDir, 'completed') : path.join(crashesDir, 'reports');
+          const newFileAppeared = waitForNewFileInDir(reportsDir);
+          crash(crashingProcess, remotely);
+          const newFiles = await newFileAppeared;
+          expect(newFiles).to.have.length(1, `Files that appeared: ${JSON.stringify(newFiles)}`);
+          expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
+        });
       });
-    });
-  });
-
-  await new Promise(resolve => {
-    server.listen(crashReporterPort, '127.0.0.1', () => { resolve(); });
+    }
   });
 
-  const port = (server.address() as AddressInfo).port;
-
-  if (crashReporterPort === 0) {
-    // We can only start the crash reporter once, and after that these
-    // parameters are fixed.
-    crashReporter.start({
-      companyName: 'Umbrella Corporation',
-      submitURL: 'http://127.0.0.1:' + port
+  describe('when not started', () => {
+    it('does not prevent process from crashing', async () => {
+      const appPath = path.join(__dirname, '..', 'spec', 'fixtures', 'api', 'cookie-app');
+      await runApp(appPath);
     });
-    crashReporterPort = port;
-  }
-
-  afterTest.push(() => { server.close(); });
-
-  return { getCrashes, port, waitForCrash };
-};
+  });
+});

+ 63 - 0
spec-main/fixtures/apps/crash/main.js

@@ -0,0 +1,63 @@
+const { app, BrowserWindow, crashReporter } = require('electron');
+const path = require('path');
+const childProcess = require('child_process');
+
+app.setVersion('0.1.0');
+
+const url = app.commandLine.getSwitchValue('crash-reporter-url');
+const uploadToServer = !app.commandLine.hasSwitch('no-upload');
+const setExtraParameters = app.commandLine.hasSwitch('set-extra-parameters-in-renderer');
+const addGlobalParam = app.commandLine.getSwitchValue('add-global-param').split(':');
+
+crashReporter.start({
+  productName: 'Zombies',
+  companyName: 'Umbrella Corporation',
+  uploadToServer,
+  submitURL: url,
+  ignoreSystemCrashHandler: true,
+  extra: {
+    mainProcessSpecific: 'mps'
+  },
+  globalExtra: addGlobalParam[0] ? { [addGlobalParam[0]]: addGlobalParam[1] } : {}
+});
+
+app.whenReady().then(() => {
+  const crashType = app.commandLine.getSwitchValue('crash-type');
+
+  if (crashType === 'main') {
+    process.crash();
+  } else if (crashType === 'renderer') {
+    const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
+    w.loadURL('about:blank');
+    if (setExtraParameters) {
+      w.webContents.executeJavaScript(`
+        require('electron').crashReporter.addExtraParameter('rendererSpecific', 'rs');
+        require('electron').crashReporter.addExtraParameter('addedThenRemoved', 'to-be-removed');
+        require('electron').crashReporter.removeExtraParameter('addedThenRemoved');
+      `);
+    }
+    w.webContents.executeJavaScript('process.crash()');
+    w.webContents.on('crashed', () => process.exit(0));
+  } else if (crashType === 'sandboxed-renderer') {
+    const w = new BrowserWindow({
+      show: false,
+      webPreferences: {
+        sandbox: true,
+        preload: path.resolve(__dirname, 'sandbox-preload.js')
+      }
+    });
+    w.loadURL(`about:blank?set_extra=${setExtraParameters ? 1 : 0}`);
+    w.webContents.on('crashed', () => process.exit(0));
+  } else if (crashType === 'node') {
+    const crashesDir = path.join(app.getPath('temp'), `${app.name} Crashes`);
+    const version = app.getVersion();
+    const crashPath = path.join(__dirname, 'node-crash.js');
+    const child = childProcess.fork(crashPath, [url, version, crashesDir], { silent: true });
+    child.on('exit', () => process.exit(0));
+  } else {
+    console.error(`Unrecognized crash type: '${crashType}'`);
+    process.exit(1);
+  }
+});
+
+setTimeout(() => app.exit(), 30000);

+ 10 - 0
spec-main/fixtures/apps/crash/node-crash.js

@@ -0,0 +1,10 @@
+if (process.platform === 'linux') {
+  process.crashReporter.start({
+    submitURL: process.argv[2],
+    productName: 'Zombies',
+    globalExtra: {
+      _version: process.argv[3]
+    }
+  });
+}
+process.nextTick(() => process.crash());

+ 4 - 0
spec-main/fixtures/apps/crash/package.json

@@ -0,0 +1,4 @@
+{
+  "name": "crash",
+  "main": "main.js"
+}

+ 10 - 0
spec-main/fixtures/apps/crash/sandbox-preload.js

@@ -0,0 +1,10 @@
+const { crashReporter } = require('electron');
+
+const params = new URLSearchParams(location.search);
+if (params.get('set_extra') === '1') {
+  crashReporter.addExtraParameter('rendererSpecific', 'rs');
+  crashReporter.addExtraParameter('addedThenRemoved', 'to-be-removed');
+  crashReporter.removeExtraParameter('addedThenRemoved');
+}
+
+process.crash();

+ 24 - 0
spec-main/fixtures/apps/remote-control/main.js

@@ -0,0 +1,24 @@
+const http = require('http');
+const v8 = require('v8');
+
+const server = http.createServer((req, res) => {
+  const chunks = [];
+  req.on('data', chunk => { chunks.push(chunk); });
+  req.on('end', () => {
+    const js = Buffer.concat(chunks).toString('utf8');
+    (async () => {
+      try {
+        const result = await Promise.resolve(eval(js)); // eslint-disable-line no-eval
+        res.end(v8.serialize({ result }));
+      } catch (e) {
+        res.end(v8.serialize({ error: e.stack }));
+      }
+    })();
+  });
+}).listen(0, '127.0.0.1', () => {
+  process.stdout.write(`Listening: ${server.address().port}\n`);
+});
+
+setTimeout(() => {
+  process.exit(0);
+}, 30000);

+ 4 - 0
spec-main/fixtures/apps/remote-control/package.json

@@ -0,0 +1,4 @@
+{
+  "name": "remote-control",
+  "main": "main.js"
+}

+ 2 - 0
spec-main/fixtures/module/print-crash-parameters.js

@@ -0,0 +1,2 @@
+process.crashReporter.addExtraParameter('hello', 'world');
+process.stdout.write(JSON.stringify(process.crashReporter.getParameters()) + '\n');

+ 1 - 0
spec-main/package.json

@@ -10,6 +10,7 @@
     "ws": "^7.2.1"
   },
   "dependencies": {
+    "busboy": "^0.3.1",
     "chai-as-promised": "^7.1.1",
     "dirty-chai": "^2.0.1",
     "pdfjs-dist": "^2.2.228"

+ 19 - 0
spec-main/yarn.lock

@@ -34,6 +34,13 @@ big.js@^5.2.2:
   resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
   integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
 
+busboy@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b"
+  integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==
+  dependencies:
+    dicer "0.3.0"
+
 chai-as-promised@^7.1.1:
   version "7.1.1"
   resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0"
@@ -46,6 +53,13 @@ check-error@^1.0.2:
   resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
   integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
 
[email protected]:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872"
+  integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==
+  dependencies:
+    streamsearch "0.1.2"
+
 dirty-chai@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/dirty-chai/-/dirty-chai-2.0.1.tgz#6b2162ef17f7943589da840abc96e75bda01aff3"
@@ -126,6 +140,11 @@ schema-utils@^0.4.0:
     ajv "^6.1.0"
     ajv-keywords "^3.1.0"
 
[email protected]:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
+  integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
+
 uri-js@^4.2.2:
   version "4.2.2"
   resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"

+ 8 - 17
spec/fixtures/module/crash.js

@@ -1,19 +1,10 @@
-process.crashReporter.start({
-  productName: 'Zombies',
-  companyName: 'Umbrella Corporation',
-  crashesDirectory: process.argv[4],
-  submitURL: `http://127.0.0.1:${process.argv[2]}`,
-  extra: {
-    extra1: 'extra1',
-    extra2: 'extra2',
-    _version: process.argv[3]
-  }
-});
-
-if (process.platform !== 'linux') {
-  process.crashReporter.addExtraParameter('newExtra', 'newExtra');
-  process.crashReporter.addExtraParameter('removeExtra', 'removeExtra');
-  process.crashReporter.removeExtraParameter('removeExtra');
+if (process.platform === 'linux') {
+  process.crashReporter.start({
+    submitURL: process.argv[2],
+    productName: 'Zombies',
+    globalExtra: {
+      _version: process.argv[3]
+    }
+  });
 }
-
 process.nextTick(() => process.crash());

+ 2 - 2
spec/package.json

@@ -19,7 +19,6 @@
     "mocha": "^5.2.0",
     "mocha-junit-reporter": "^1.18.0",
     "mocha-multi-reporters": "^1.1.7",
-    "multiparty": "^4.2.1",
     "send": "^0.16.2",
     "split": "^1.0.1",
     "temp": "^0.9.0",
@@ -30,6 +29,7 @@
     "yargs": "^12.0.5"
   },
   "dependencies": {
-    "mocha-appveyor-reporter": "^0.4.2"
+    "mocha-appveyor-reporter": "^0.4.2",
+    "rimraf": "^3.0.2"
   }
 }

+ 8 - 56
spec/yarn.lock

@@ -381,13 +381,6 @@ fast-json-stable-stringify@^2.0.0:
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
   integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
 
[email protected]:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
-  integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
-  dependencies:
-    pend "~1.2.0"
-
 [email protected]:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
@@ -525,17 +518,6 @@ http-errors@~1.6.2:
     setprototypeof "1.1.0"
     statuses ">= 1.4.0 < 2"
 
-http-errors@~1.7.0:
-  version "1.7.2"
-  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
-  integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
-  dependencies:
-    depd "~1.1.2"
-    inherits "2.0.3"
-    setprototypeof "1.1.1"
-    statuses ">= 1.5.0 < 2"
-    toidentifier "1.0.0"
-
 http-signature@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
@@ -791,16 +773,6 @@ ms@^2.1.1:
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
   integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
 
-multiparty@^4.2.1:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/multiparty/-/multiparty-4.2.1.tgz#d9b6c46d8b8deab1ee70c734b0af771dd46e0b13"
-  integrity sha512-AvESCnNoQlZiOfP9R4mxN8M9csy2L16EIbWIkt3l4FuGti9kXBS8QVzlfyg4HEnarJhrzZilgNFlZtqmoiAIIA==
-  dependencies:
-    fd-slicer "1.1.0"
-    http-errors "~1.7.0"
-    safe-buffer "5.1.2"
-    uid-safe "2.1.5"
-
 [email protected], nan@^2.12.1:
   version "2.14.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
@@ -920,11 +892,6 @@ pause-stream@^0.0.11:
   dependencies:
     through "~2.3"
 
-pend@~1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
-  integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
-
 performance-now@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
@@ -958,11 +925,6 @@ qs@~6.5.2:
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
   integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
 
-random-bytes@~1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
-  integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=
-
 range-parser@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
@@ -1012,6 +974,13 @@ require-main-filename@^1.0.1:
   resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
   integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
 
+rimraf@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+  dependencies:
+    glob "^7.1.3"
+
 rimraf@~2.6.2:
   version "2.6.3"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
@@ -1073,11 +1042,6 @@ [email protected]:
   resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
   integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
 
[email protected]:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
-  integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
-
 shebang-command@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
@@ -1117,7 +1081,7 @@ sshpk@^1.7.0:
     safer-buffer "^2.0.2"
     tweetnacl "~0.14.0"
 
-"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2":
+"statuses@>= 1.4.0 < 2":
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
   integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
@@ -1190,11 +1154,6 @@ through@2, through@^2.3.8, through@~2.3, through@~2.3.4:
   resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
   integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
 
[email protected]:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
-  integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
-
 tough-cookie@~2.4.3:
   version "2.4.3"
   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
@@ -1220,13 +1179,6 @@ type-detect@^4.0.0, type-detect@^4.0.5:
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
   integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
 
[email protected]:
-  version "2.1.5"
-  resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a"
-  integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==
-  dependencies:
-    random-bytes "~1.0.0"
-
 uri-js@^4.2.2:
   version "4.2.2"
   resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"

+ 34 - 7
yarn.lock

@@ -156,6 +156,13 @@
     "@types/connect" "*"
     "@types/node" "*"
 
+"@types/busboy@^0.2.3":
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/@types/busboy/-/busboy-0.2.3.tgz#6697ad29873246c530f09a3ff5a40861824230d5"
+  integrity sha1-ZpetKYcyRsUw8Jo/9aQIYYJCMNU=
+  dependencies:
+    "@types/node" "*"
+
 "@types/chai-as-promised@*":
   version "7.1.1"
   resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.1.tgz#004c27a4ac640e9590e25d8b0980cb0a6609bfd8"
@@ -195,6 +202,11 @@
   resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
   integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
 
+"@types/events@*":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
+  integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
+
 "@types/express-serve-static-core@*":
   version "4.16.7"
   resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz#50ba6f8a691c08a3dd9fa7fba25ef3133d298049"
@@ -219,6 +231,15 @@
   dependencies:
     "@types/node" "*"
 
+"@types/glob@*":
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
+  integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
+  dependencies:
+    "@types/events" "*"
+    "@types/minimatch" "*"
+    "@types/node" "*"
+
 "@types/json-schema@^7.0.3":
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
@@ -241,18 +262,16 @@
   resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
   integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==
 
+"@types/minimatch@*":
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
+  integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
+
 "@types/mocha@^5.2.6":
   version "5.2.7"
   resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
   integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
 
-"@types/multiparty@^0.0.32":
-  version "0.0.32"
-  resolved "https://registry.yarnpkg.com/@types/multiparty/-/multiparty-0.0.32.tgz#99226df6050dae605fa6f9489d6c0b5ab61fdc00"
-  integrity sha512-zuQEcL9pHiW3u1fgkOWE6T/RwskrFZ3W63aKQPDs7EokIjtbsGL7aVlI4tI86qjL4B3hcFxDRtIGxwRwMTS2Dw==
-  dependencies:
-    "@types/node" "*"
-
 "@types/node@*", "@types/node@^12.0.10":
   version "12.6.1"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.1.tgz#d5544f6de0aae03eefbb63d5120f6c8be0691946"
@@ -273,6 +292,14 @@
   resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
   integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
 
+"@types/rimraf@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.0.tgz#b9d03f090ece263671898d57bb7bb007023ac19f"
+  integrity sha512-7WhJ0MdpFgYQPXlF4Dx+DhgvlPCfz/x5mHaeDQAKhcenvQP1KCpLQ18JklAqeGMYSAT2PxLpzd0g2/HE7fj7hQ==
+  dependencies:
+    "@types/glob" "*"
+    "@types/node" "*"
+
 "@types/semver@^6.0.1":
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.0.1.tgz#a984b405c702fa5a7ec6abc56b37f2ba35ef5af6"