diff --git a/Cargo.lock b/Cargo.lock
index a20dd52ee60a3b26d2bb29e75180912be7150172..7cfee63b3d691b34b7652be24bb20a9047baad27 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3275,6 +3275,18 @@ dependencies = [
  "semver",
 ]
 
+[[package]]
+name = "mozwer_s"
+version = "0.1.0"
+dependencies = [
+ "libc",
+ "rust-ini",
+ "serde",
+ "serde_json",
+ "uuid",
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "mp4parse"
 version = "0.11.5"
diff --git a/Cargo.toml b/Cargo.toml
index 6820a424eafa358440b623fb6e67b5e268581dd7..69ab011e3c50dd968feb3305aa20aebe69d4530a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@ members = [
   "security/manager/ssl/osclientcerts",
   "testing/geckodriver",
   "toolkit/crashreporter/rust_minidump_writer_linux",
+  "toolkit/crashreporter/mozwer-rust",
   "toolkit/library/gtest/rust",
   "toolkit/library/rust/",
   "toolkit/mozapps/defaultagent/rust",
diff --git a/toolkit/crashreporter/moz.build b/toolkit/crashreporter/moz.build
index 12126a66954ff097d2e2c9e42d4676d2ab724d1b..a287d645b2f6dff9ab5bece40ac7eadbba7ad157 100644
--- a/toolkit/crashreporter/moz.build
+++ b/toolkit/crashreporter/moz.build
@@ -26,9 +26,11 @@ FINAL_LIBRARY = "xul"
 if CONFIG["MOZ_CRASHREPORTER"]:
     if CONFIG["OS_ARCH"] == "WINNT":
         DIRS += [
+            "breakpad-windows-libxul",
             "google-breakpad/src/common",
             "google-breakpad/src/processor",
-            "breakpad-windows-libxul",
+            "mozwer",
+            "mozwer-rust",
         ]
 
         if CONFIG["MOZ_CRASHREPORTER_INJECTOR"]:
diff --git a/toolkit/crashreporter/mozwer-rust/Cargo.toml b/toolkit/crashreporter/mozwer-rust/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..228a4fc98d62f87f0ea32bf34d741693e2058e69
--- /dev/null
+++ b/toolkit/crashreporter/mozwer-rust/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "mozwer_s"
+version = "0.1.0"
+authors = ["Gabriele Svelto <gsvelto@mozilla.com>"]
+edition = "2018"
+
+[dependencies]
+libc = "0.2.0"
+rust-ini = "0.10"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = { version = "1.0" }
+uuid = { version = "0.8.1", features = ["v4"] }
+winapi = { version = "0.3", features = [
+    "combaseapi",
+    "impl-default",
+    "knownfolders",
+    "psapi",
+    "shlobj",
+    "winerror",
+    "winbase",
+    "winnt",
+] }
+
+[lib]
+name = "mozwer_s"
+crate-type = ["staticlib"]
+path = "lib.rs"
diff --git a/toolkit/crashreporter/mozwer-rust/lib.rs b/toolkit/crashreporter/mozwer-rust/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a69bc21c32a0171d08aa897222dbc54fe774561a
--- /dev/null
+++ b/toolkit/crashreporter/mozwer-rust/lib.rs
@@ -0,0 +1,547 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+extern crate ini;
+extern crate winapi;
+
+use ini::Ini;
+use libc::time;
+use serde::Serialize;
+use serde_json::ser::to_writer;
+use std::convert::TryInto;
+use std::ffi::OsString;
+use std::fs::{read_to_string, File};
+use std::io::{BufRead, BufReader, Write};
+use std::mem::size_of;
+use std::os::windows::ffi::OsStringExt;
+use std::os::windows::io::AsRawHandle;
+use std::path::{Path, PathBuf};
+use std::ptr::{null, null_mut};
+use std::slice::from_raw_parts;
+use uuid::Uuid;
+use winapi::shared::minwindef::{BOOL, DWORD, FALSE, MAX_PATH, PBOOL, PDWORD, TRUE};
+use winapi::shared::winerror::{E_UNEXPECTED, S_OK};
+use winapi::um::combaseapi::CoTaskMemFree;
+use winapi::um::knownfolders::FOLDERID_RoamingAppData;
+use winapi::um::processthreadsapi::{GetProcessId, GetThreadId, TerminateProcess};
+use winapi::um::psapi::K32GetModuleFileNameExW;
+use winapi::um::shlobj::SHGetKnownFolderPath;
+use winapi::um::winbase::VerifyVersionInfoW;
+use winapi::um::winnt::{
+    VerSetConditionMask, CONTEXT, DWORDLONG, EXCEPTION_POINTERS, EXCEPTION_RECORD, HANDLE, HRESULT,
+    LPOSVERSIONINFOEXW, OSVERSIONINFOEXW, PCWSTR, PEXCEPTION_POINTERS, PVOID, PWSTR,
+    VER_GREATER_EQUAL, VER_MAJORVERSION, VER_MINORVERSION, VER_SERVICEPACKMAJOR,
+    VER_SERVICEPACKMINOR,
+};
+use winapi::STRUCT;
+
+#[no_mangle]
+pub extern "C" fn OutOfProcessExceptionEventCallback(
+    _context: PVOID,
+    exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
+    b_ownership_claimed: PBOOL,
+    _wsz_event_name: PWSTR,
+    _pch_size: PDWORD,
+    _dw_signature_count: PDWORD,
+) -> HRESULT {
+    match out_of_process_exception_event_callback(exception_information) {
+        Ok(_) => {
+            unsafe {
+                // Inform WER that we claim ownership of this crash
+                *b_ownership_claimed = TRUE;
+                // Make sure that Firefox shuts down
+                TerminateProcess((*exception_information).hProcess, 1);
+            }
+            S_OK
+        }
+        Err(_) => E_UNEXPECTED,
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn OutOfProcessExceptionEventSignatureCallback(
+    _context: PVOID,
+    _exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
+    _w_index: DWORD,
+    _wsz_name: PWSTR,
+    _ch_name: PDWORD,
+    _wsz_value: PWSTR,
+    _ch_value: PDWORD,
+) -> HRESULT {
+    S_OK
+}
+
+#[no_mangle]
+pub extern "C" fn OutOfProcessExceptionEventDebuggerLaunchCallback(
+    _context: PVOID,
+    _exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
+    b_is_custom_debugger: PBOOL,
+    _wsz_debugger_launch: PWSTR,
+    _ch_debugger_launch: PDWORD,
+    _b_is_debugger_autolaunch: PBOOL,
+) -> HRESULT {
+    unsafe {
+        *b_is_custom_debugger = FALSE;
+    }
+
+    S_OK
+}
+
+fn out_of_process_exception_event_callback(
+    exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
+) -> Result<(), ()> {
+    let process = unsafe { (*exception_information).hProcess };
+
+    let application_path = get_application_path(process)?;
+    let mut install_path = application_path.clone();
+    install_path.pop();
+    let application_data = ApplicationData::load_from_disk(install_path.as_ref())?;
+    let release_channel = get_release_channel(install_path.as_ref())?;
+    let crash_reports_dir = get_crash_reports_dir(&application_data)?;
+    let install_time = get_install_time(&crash_reports_dir, &application_data.build_id)?;
+    let crash_report = CrashReport::new(
+        &crash_reports_dir,
+        &release_channel,
+        &application_data,
+        &install_time,
+    );
+    crash_report.write_minidump(exception_information)?;
+    crash_report.write_extra_file()?;
+    crash_report.write_event_file()?;
+
+    Ok(())
+}
+
+fn get_crash_reports_dir(application_data: &ApplicationData) -> Result<PathBuf, ()> {
+    let mut psz_path: PWSTR = null_mut();
+    unsafe {
+        let res = SHGetKnownFolderPath(
+            &FOLDERID_RoamingAppData as *const _,
+            0,
+            null_mut(),
+            &mut psz_path as *mut _,
+        );
+
+        if res == S_OK {
+            let mut len = 0;
+            while psz_path.offset(len).read() != 0 {
+                len += 1;
+            }
+            let str = OsString::from_wide(from_raw_parts(psz_path, len as usize));
+            CoTaskMemFree(psz_path as _);
+            let mut path = PathBuf::from(str);
+            if let Some(vendor) = &application_data.vendor {
+                path.push(vendor);
+            }
+            path.push(&application_data.name);
+            path.push("Crash Reports");
+            Ok(path)
+        } else {
+            Err(())
+        }
+    }
+}
+
+fn get_application_path(process: HANDLE) -> Result<PathBuf, ()> {
+    let mut path: [u16; MAX_PATH + 1] = [0; MAX_PATH + 1];
+    unsafe {
+        let res = K32GetModuleFileNameExW(
+            process,
+            null_mut(),
+            (&mut path).as_mut_ptr(),
+            (MAX_PATH + 1) as u32,
+        );
+
+        if res == 0 {
+            return Err(());
+        }
+
+        let application_path = PathBuf::from(OsString::from_wide(&path[0..res as usize]));
+        Ok(application_path)
+    }
+}
+
+fn get_release_channel(install_path: &Path) -> Result<String, ()> {
+    let channel_prefs =
+        File::open(install_path.join("defaults/pref/channel-prefs.js")).map_err(|_e| ())?;
+    let lines = BufReader::new(channel_prefs).lines();
+    let line = lines
+        .filter_map(Result::ok)
+        .find(|line| line.contains("app.update.channel"))
+        .ok_or(())?;
+    line.split("\"").nth(3).map(|s| s.to_string()).ok_or(())
+}
+
+fn get_install_time(crash_reports_path: &Path, build_id: &str) -> Result<String, ()> {
+    let file_name = "InstallTime".to_owned() + build_id;
+    let file_path = crash_reports_path.join(file_name);
+    read_to_string(file_path).map_err(|_e| ())
+}
+
+#[derive(Debug)]
+struct ApplicationData {
+    vendor: Option<String>,
+    name: String,
+    version: String,
+    build_id: String,
+    product_id: String,
+    server_url: String,
+}
+
+impl ApplicationData {
+    fn load_from_disk(install_path: &Path) -> Result<ApplicationData, ()> {
+        let ini_path = ApplicationData::get_path(install_path);
+        let conf = Ini::load_from_file(ini_path).map_err(|_e| ())?;
+
+        // Parse the "App" section
+        let app_section = conf.section(Some("App")).ok_or(())?;
+        let vendor = app_section.get("Vendor").map(|s| s.to_owned());
+        let name = app_section.get("Name").ok_or(())?.to_owned();
+        let version = app_section.get("Version").ok_or(())?.to_owned();
+        let build_id = app_section.get("BuildID").ok_or(())?.to_owned();
+        let product_id = app_section.get("ID").ok_or(())?.to_owned();
+
+        // Parse the "Crash Reporter" section
+        let crash_reporter_section = conf.section(Some("Crash Reporter")).ok_or(())?;
+        let server_url = crash_reporter_section
+            .get("ServerURL")
+            .ok_or(())?
+            .to_owned();
+
+        // InstallTime<build_id>
+
+        Ok(ApplicationData {
+            vendor,
+            name,
+            version,
+            build_id,
+            product_id,
+            server_url,
+        })
+    }
+
+    fn get_path(install_path: &Path) -> PathBuf {
+        install_path.join("application.ini")
+    }
+}
+
+#[derive(Serialize)]
+#[allow(non_snake_case)]
+struct Annotations<'a> {
+    CrashTime: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    Vendor: Option<&'a str>,
+    ProductName: &'a str,
+    Version: &'a str,
+    BuildID: &'a str,
+    ProductID: &'a str,
+    ServerURL: &'a str,
+    ReleaseChannel: &'a str,
+    InstallTime: &'a str,
+}
+
+impl Annotations<'_> {
+    fn from_application_data<'a>(
+        application_data: &'a ApplicationData,
+        release_channel: &'a str,
+        install_time: &'a str,
+        crash_time: i64,
+    ) -> Annotations<'a> {
+        Annotations {
+            CrashTime: crash_time.to_string(),
+            Vendor: application_data.vendor.as_deref(),
+            ProductName: &application_data.name,
+            Version: &application_data.version,
+            BuildID: &application_data.build_id,
+            ProductID: &application_data.product_id,
+            ServerURL: &application_data.server_url,
+            ReleaseChannel: release_channel,
+            InstallTime: install_time,
+        }
+    }
+}
+
+struct CrashReport<'a> {
+    uuid: String,
+    crash_reports_path: PathBuf,
+    release_channel: &'a str,
+    annotations: Annotations<'a>,
+    crash_time: i64,
+}
+
+impl CrashReport<'_> {
+    fn new<'a>(
+        crash_reports_path: &Path,
+        release_channel: &'a str,
+        application_data: &'a ApplicationData,
+        install_time: &'a str,
+    ) -> CrashReport<'a> {
+        let uuid = Uuid::new_v4()
+            .to_hyphenated()
+            .encode_lower(&mut Uuid::encode_buffer())
+            .to_owned();
+        let crash_reports_path = PathBuf::from(crash_reports_path);
+        let crash_time: i64 = unsafe { time(null_mut()).into() };
+        let annotations = Annotations::from_application_data(
+            application_data,
+            release_channel,
+            install_time,
+            crash_time,
+        );
+        CrashReport {
+            uuid,
+            crash_reports_path,
+            release_channel,
+            annotations,
+            crash_time,
+        }
+    }
+
+    fn is_nightly(&self) -> bool {
+        self.release_channel == "nightly" || self.release_channel == "default"
+    }
+
+    fn get_minidump_type(&self) -> MINIDUMP_TYPE {
+        let mut minidump_type = MiniDumpWithFullMemoryInfo | MiniDumpWithUnloadedModules;
+        if self.is_nightly() {
+            // This is Nightly only because this doubles the size of minidumps based
+            // on the experimental data.
+            minidump_type = minidump_type | MiniDumpWithProcessThreadData;
+
+            // dbghelp.dll on Win7 can't handle overlapping memory regions so we only
+            // enable this feature on Win8 or later.
+            if is_windows8_or_later() {
+                // This allows us to examine heap objects referenced from stack objects
+                // at the cost of further doubling the size of minidumps.
+                minidump_type = minidump_type | MiniDumpWithIndirectlyReferencedMemory
+            }
+        }
+        minidump_type
+    }
+
+    fn get_pending_path(&self) -> PathBuf {
+        self.crash_reports_path.join("pending")
+    }
+
+    fn get_events_path(&self) -> PathBuf {
+        self.crash_reports_path.join("events")
+    }
+
+    fn get_minidump_path(&self) -> PathBuf {
+        self.get_pending_path().join(self.uuid.to_string() + ".dmp")
+    }
+
+    fn get_extra_file_path(&self) -> PathBuf {
+        self.get_pending_path()
+            .join(self.uuid.to_string() + ".extra")
+    }
+
+    fn get_event_file_path(&self) -> PathBuf {
+        self.get_events_path().join(self.uuid.to_string())
+    }
+
+    fn write_minidump(
+        &self,
+        exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
+    ) -> Result<(), ()> {
+        let minidump_path = self.get_minidump_path();
+        let minidump_file = File::create(minidump_path).map_err(|_e| ())?;
+        let minidump_type: MINIDUMP_TYPE = self.get_minidump_type();
+
+        unsafe {
+            let mut exception_pointers = EXCEPTION_POINTERS {
+                ExceptionRecord: &mut ((*exception_information).exceptionRecord),
+                ContextRecord: &mut ((*exception_information).context),
+            };
+
+            let mut exception = MINIDUMP_EXCEPTION_INFORMATION {
+                ThreadId: GetThreadId((*exception_information).hThread),
+                ExceptionPointers: &mut exception_pointers,
+                ClientPointers: FALSE,
+            };
+
+            let res = MiniDumpWriteDump(
+                (*exception_information).hProcess,
+                GetProcessId((*exception_information).hProcess),
+                minidump_file.as_raw_handle() as _,
+                minidump_type,
+                &mut exception,
+                /* userStream */ null(),
+                /* callback */ null(),
+            );
+
+            match res {
+                FALSE => Err(()),
+                _ => Ok(()),
+            }
+        }
+    }
+
+    fn write_extra_file(&self) -> Result<(), ()> {
+        let extra_file = File::create(self.get_extra_file_path()).map_err(|_e| ())?;
+        to_writer(extra_file, &self.annotations).map_err(|_e| ())
+    }
+
+    fn write_event_file(&self) -> Result<(), ()> {
+        let mut event_file = File::create(self.get_event_file_path()).map_err(|_e| ())?;
+        writeln!(event_file, "crash.main.3").map_err(|_e| ())?;
+        writeln!(event_file, "{}", self.crash_time).map_err(|_e| ())?;
+        writeln!(event_file, "{}", self.uuid).map_err(|_e| ())?;
+        to_writer(event_file, &self.annotations).map_err(|_e| ())
+    }
+}
+
+fn is_windows8_or_later() -> bool {
+    let mut info = OSVERSIONINFOEXW {
+        dwOSVersionInfoSize: size_of::<OSVERSIONINFOEXW>().try_into().unwrap(),
+        dwMajorVersion: 6,
+        dwMinorVersion: 2,
+        ..Default::default()
+    };
+
+    unsafe {
+        let mut mask: DWORDLONG = 0;
+        mask = VerSetConditionMask(mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+        mask = VerSetConditionMask(mask, VER_MINORVERSION, VER_GREATER_EQUAL);
+        mask = VerSetConditionMask(mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+        mask = VerSetConditionMask(mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
+
+        let res = VerifyVersionInfoW(
+            &mut info as LPOSVERSIONINFOEXW,
+            VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
+            mask,
+        );
+
+        res != FALSE
+    }
+}
+
+/******************************************************************************
+ * The stuff below should be migrated to the winapi crate, see bug 1696414    *
+ ******************************************************************************/
+
+// we can't use winapi's ENUM macro directly because it doesn't support
+// attributes, so let's define this one here until we migrate this code
+macro_rules! ENUM {
+    {enum $name:ident { $($variant:ident = $value:expr,)+ }} => {
+        #[allow(non_camel_case_types)] pub type $name = u32;
+        $(#[allow(non_upper_case_globals)] pub const $variant: $name = $value;)+
+    };
+}
+
+// winapi doesn't export the FN macro, so we duplicate it here
+macro_rules! FN {
+    (stdcall $func:ident($($t:ty,)*) -> $ret:ty) => (
+        #[allow(non_camel_case_types)] pub type $func = Option<unsafe extern "system" fn($($t,)*) -> $ret>;
+    );
+    (stdcall $func:ident($($p:ident: $t:ty,)*) -> $ret:ty) => (
+        #[allow(non_camel_case_types)] pub type $func = Option<unsafe extern "system" fn($($p: $t,)*) -> $ret>;
+    );
+}
+
+// From um/WerApi.h
+
+STRUCT! {#[allow(non_snake_case)] struct WER_RUNTIME_EXCEPTION_INFORMATION
+{
+    dwSize: DWORD,
+    hProcess: HANDLE,
+    hThread: HANDLE,
+    exceptionRecord: EXCEPTION_RECORD,
+    context: CONTEXT,
+    pwszReportId: PCWSTR,
+    bIsFatal: BOOL,
+    dwReserved: DWORD,
+}}
+
+#[allow(non_camel_case_types)]
+pub type PWER_RUNTIME_EXCEPTION_INFORMATION = *mut WER_RUNTIME_EXCEPTION_INFORMATION;
+
+// From minidumpapiset.hProcess
+
+STRUCT! {#[allow(non_snake_case)] #[repr(packed(4))] struct MINIDUMP_EXCEPTION_INFORMATION {
+    ThreadId: DWORD,
+    ExceptionPointers: PEXCEPTION_POINTERS,
+    ClientPointers: BOOL,
+}}
+
+#[allow(non_camel_case_types)]
+pub type PMINIDUMP_EXCEPTION_INFORMATION = *mut MINIDUMP_EXCEPTION_INFORMATION;
+
+ENUM! { enum MINIDUMP_TYPE {
+    MiniDumpNormal                         = 0x00000000,
+    MiniDumpWithDataSegs                   = 0x00000001,
+    MiniDumpWithFullMemory                 = 0x00000002,
+    MiniDumpWithHandleData                 = 0x00000004,
+    MiniDumpFilterMemory                   = 0x00000008,
+    MiniDumpScanMemory                     = 0x00000010,
+    MiniDumpWithUnloadedModules            = 0x00000020,
+    MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
+    MiniDumpFilterModulePaths              = 0x00000080,
+    MiniDumpWithProcessThreadData          = 0x00000100,
+    MiniDumpWithPrivateReadWriteMemory     = 0x00000200,
+    MiniDumpWithoutOptionalData            = 0x00000400,
+    MiniDumpWithFullMemoryInfo             = 0x00000800,
+    MiniDumpWithThreadInfo                 = 0x00001000,
+    MiniDumpWithCodeSegs                   = 0x00002000,
+    MiniDumpWithoutAuxiliaryState          = 0x00004000,
+    MiniDumpWithFullAuxiliaryState         = 0x00008000,
+    MiniDumpWithPrivateWriteCopyMemory     = 0x00010000,
+    MiniDumpIgnoreInaccessibleMemory       = 0x00020000,
+    MiniDumpWithTokenInformation           = 0x00040000,
+    MiniDumpWithModuleHeaders              = 0x00080000,
+    MiniDumpFilterTriage                   = 0x00100000,
+    MiniDumpWithAvxXStateContext           = 0x00200000,
+    MiniDumpWithIptTrace                   = 0x00400000,
+    MiniDumpScanInaccessiblePartialPages   = 0x00800000,
+    MiniDumpValidTypeFlags                 = 0x00ffffff,
+}}
+
+// We don't actually need the following three structs so we use placeholders
+STRUCT! {#[allow(non_snake_case)] struct MINIDUMP_CALLBACK_INPUT {
+    dummy: u32,
+}}
+
+#[allow(non_camel_case_types)]
+pub type PMINIDUMP_CALLBACK_INPUT = *const MINIDUMP_CALLBACK_INPUT;
+
+STRUCT! {#[allow(non_snake_case)] struct MINIDUMP_USER_STREAM_INFORMATION {
+    dummy: u32,
+}}
+
+#[allow(non_camel_case_types)]
+pub type PMINIDUMP_USER_STREAM_INFORMATION = *const MINIDUMP_USER_STREAM_INFORMATION;
+
+STRUCT! {#[allow(non_snake_case)] struct MINIDUMP_CALLBACK_OUTPUT {
+    dummy: u32,
+}}
+
+#[allow(non_camel_case_types)]
+pub type PMINIDUMP_CALLBACK_OUTPUT = *const MINIDUMP_CALLBACK_OUTPUT;
+
+// MiniDumpWriteDump() function and structs
+FN! {stdcall MINIDUMP_CALLBACK_ROUTINE(
+CallbackParam: PVOID,
+CallbackInput: PMINIDUMP_CALLBACK_INPUT,
+CallbackOutput: PMINIDUMP_CALLBACK_OUTPUT,
+) -> BOOL}
+
+STRUCT! {#[allow(non_snake_case)] #[repr(packed(4))] struct MINIDUMP_CALLBACK_INFORMATION {
+    CallbackRoutine: MINIDUMP_CALLBACK_ROUTINE,
+    CallbackParam: PVOID,
+}}
+
+#[allow(non_camel_case_types)]
+pub type PMINIDUMP_CALLBACK_INFORMATION = *const MINIDUMP_CALLBACK_INFORMATION;
+
+extern "system" {
+    pub fn MiniDumpWriteDump(
+        hProcess: HANDLE,
+        ProcessId: DWORD,
+        hFile: HANDLE,
+        DumpType: MINIDUMP_TYPE,
+        Exceptionparam: PMINIDUMP_EXCEPTION_INFORMATION,
+        UserStreamParam: PMINIDUMP_USER_STREAM_INFORMATION,
+        CallbackParam: PMINIDUMP_CALLBACK_INFORMATION,
+    ) -> BOOL;
+}
diff --git a/toolkit/crashreporter/mozwer-rust/moz.build b/toolkit/crashreporter/mozwer-rust/moz.build
new file mode 100644
index 0000000000000000000000000000000000000000..f921fd8768860662e37cd3efe077bb51f8853b78
--- /dev/null
+++ b/toolkit/crashreporter/mozwer-rust/moz.build
@@ -0,0 +1,10 @@
+RustLibrary("mozwer_s")
+
+OS_LIBS += [
+    "dbghelp",
+    "kernel32",
+    "ole32",
+    "shell32",
+    "userenv",
+    "ws2_32",
+]
diff --git a/toolkit/crashreporter/mozwer/moz.build b/toolkit/crashreporter/mozwer/moz.build
new file mode 100644
index 0000000000000000000000000000000000000000..77f3a6eaf5cedacea75d532dd199441be741f528
--- /dev/null
+++ b/toolkit/crashreporter/mozwer/moz.build
@@ -0,0 +1,12 @@
+UNIFIED_SOURCES = [
+    "mozwer.cpp",
+]
+
+USE_LIBS += [
+    "mozwer_s",
+]
+
+DEFFILE = "mozwer.def"
+USE_STATIC_LIBS = True
+
+SharedLibrary("mozwer")
diff --git a/toolkit/crashreporter/mozwer/mozwer.cpp b/toolkit/crashreporter/mozwer/mozwer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4f4c18494908e089deb6bc280ebd832cc8eb668f
--- /dev/null
+++ b/toolkit/crashreporter/mozwer/mozwer.cpp
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+
+#include "nscore.h"
+
+extern "C" {
+
+NS_EXPORT_(BOOL)
+DllMain(HINSTANCE DllInstance, DWORD Reason, LPVOID Reserved) {
+  UNREFERENCED_PARAMETER(DllInstance);
+  UNREFERENCED_PARAMETER(Reason);
+  UNREFERENCED_PARAMETER(Reserved);
+
+  return TRUE;
+}
+
+} /* extern "C" */
diff --git a/toolkit/crashreporter/mozwer/mozwer.def b/toolkit/crashreporter/mozwer/mozwer.def
new file mode 100644
index 0000000000000000000000000000000000000000..b9ea6ed3ac6f5900e40ac8048d5e2aeb82ab9a1b
--- /dev/null
+++ b/toolkit/crashreporter/mozwer/mozwer.def
@@ -0,0 +1,5 @@
+LIBRARY
+EXPORTS
+       OutOfProcessExceptionEventCallback
+       OutOfProcessExceptionEventSignatureCallback
+       OutOfProcessExceptionEventDebuggerLaunchCallback