[wgpu-headers] Update device lost and uncaptured error callbacks.

- Updates the callbacks to allow for 2 userdatas.
- Updates the C++ DeviceDescriptor to expose setters for the callbacks.
- Updates internal usages to remove to-be-deprecated versions.

Bug: 42241461
Change-Id: I583eb334284ee4ae8d197ed57bd5fee3048be4b8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/192741
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index 322883a..9461100 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -1027,6 +1027,13 @@
     return [(typ, method) for (_, typ, method) in sorted(unsorted)]
 
 
+def find_by_name(members, name):
+    for member in members:
+        if member.name.get() == name:
+            return member
+    assert False
+
+
 def has_callback_arguments(method):
     return any(arg.type.category == 'function pointer' for arg in method.arguments)
 
@@ -1047,6 +1054,7 @@
     # Function pointers, callback functions, and "void *" types (i.e. userdata) cannot
     # be serialized.
     return (type.category != 'function pointer'
+            and type.category != 'callback info'
             and type.category != 'callback function'
             and type.name.get() != 'void *')
 
@@ -1108,6 +1116,7 @@
             'decorate': decorate,
             'as_ktName': as_ktName,
             'has_callbackInfoStruct': has_callbackInfoStruct,
+            'find_by_name': find_by_name,
             'unreachable_code': unreachable_code
         }
 
diff --git a/generator/templates/api.h b/generator/templates/api.h
index cfc89d2..c6c80fb 100644
--- a/generator/templates/api.h
+++ b/generator/templates/api.h
@@ -167,6 +167,26 @@
 
 #define {{API}}_COMMA ,
 
+{% for type in by_category["callback info"] %}
+    typedef struct {{as_cType(type.name)}} {
+        {{API}}ChainedStruct const* nextInChain;
+        {% for member in type.members %}
+            {{as_annotated_cType(member)}};
+        {% endfor %}
+        void* userdata1;
+        void* userdata2;
+    } {{as_cType(type.name)}} {{API}}_STRUCTURE_ATTRIBUTE;
+
+    #define {{API}}_{{type.name.SNAKE_CASE()}}_INIT {{API}}_MAKE_INIT_STRUCT({{as_cType(type.name)}}, { \
+        /*.nextInChain=*/nullptr {{API}}_COMMA \
+        {% for member in type.members %}
+            /*.{{as_varName(member.name)}}=*/{{render_c_default_value(member)}} {{API}}_COMMA \
+        {% endfor %}
+        /*.userdata1=*/nullptr {{API}}_COMMA \
+        /*.userdata2=*/nullptr {{API}}_COMMA \
+    })
+
+{% endfor %}
 {% for type in by_category["structure"] %}
     {% for root in type.chain_roots %}
         // Can be chained in {{as_cType(root.name)}}
@@ -202,28 +222,6 @@
     })
 
 {% endfor %}
-{% for type in by_category["callback info"] %}
-    typedef struct {{as_cType(type.name)}} {
-        {{API}}ChainedStruct const* nextInChain;
-        {{as_cType(types["callback mode"].name)}} mode;
-        {% for member in type.members %}
-            //* Only callback function types are allowed in callback info structs.
-            {{assert(member.type.category == "callback function")}}{{as_annotated_cType(member)}};
-        {% endfor %}
-        void* userdata1;
-        void* userdata2;
-    } {{as_cType(type.name)}} {{API}}_STRUCTURE_ATTRIBUTE;
-
-    #define {{API}}_{{type.name.SNAKE_CASE()}}_INIT {{API}}_MAKE_INIT_STRUCT({{as_cType(type.name)}}, { \
-        /*.mode=*/{{as_cEnum(types["callback mode"].name, Name("undefined"))}} {{API}}_COMMA \
-        {% for member in type.members %}
-            /*.{{as_varName(member.name)}}=*/{{render_c_default_value(member)}} {{API}}_COMMA \
-        {% endfor %}
-        /*.userdata1=*/nullptr {{API}}_COMMA \
-        /*.userdata2=*/nullptr {{API}}_COMMA \
-    })
-
-{% endfor %}
 {% for typeDef in by_category["typedef"] %}
     // {{as_cType(typeDef.name)}} is deprecated.
     // Use {{as_cType(typeDef.type.name)}} instead.
diff --git a/generator/templates/api_cpp.h b/generator/templates/api_cpp.h
index 63e097d..70f88c6 100644
--- a/generator/templates/api_cpp.h
+++ b/generator/templates/api_cpp.h
@@ -26,6 +26,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 {% set API = metadata.api.upper() %}
 {% set api = API.lower() %}
+{% set CAPI = metadata.c_prefix %}
 {% if 'dawn' in enabled_tags %}
     #ifdef __EMSCRIPTEN__
     #error "Do not include this header. Emscripten already provides headers needed for {{metadata.api}}."
@@ -35,6 +36,7 @@
 #ifndef {{PREFIX}}{{API}}_CPP_H_
 #define {{PREFIX}}{{API}}_CPP_H_
 
+#include <cassert>
 #include <cmath>
 #include <cstddef>
 #include <cstdint>
@@ -260,7 +262,7 @@
     //* Stripping the 2 at the end of the callback functions for now until we can deprecate old ones.
     //* TODO: crbug.com/dawn/2509 - Remove name handling once old APIs are deprecated.
     {% set CallbackInfoType = (method.arguments|last).type %}
-    {% set CallbackType = (CallbackInfoType.members|first).type %}
+    {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
     {% set SfinaeArg = " = std::enable_if_t<std::is_convertible_v<F, Cb*>>" if not dfn else "" %}
     template <typename F, typename T,
               typename Cb
@@ -292,7 +294,7 @@
     //* Stripping the 2 at the end of the callback functions for now until we can deprecate old ones.
     //* TODO: crbug.com/dawn/2509 - Remove name handling once old APIs are deprecated.
     {% set CallbackInfoType = (method.arguments|last).type %}
-    {% set CallbackType = (CallbackInfoType.members|first).type %}
+    {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
     {% set SfinaeArg = " = std::enable_if_t<std::is_convertible_v<L, Cb>>" if not dfn else "" %}
     template <typename L,
               typename Cb
@@ -364,7 +366,7 @@
 {% macro render_cpp_callback_info_template_method_impl(type, method) %}
     {{render_cpp_callback_info_template_method_declaration(type, method, dfn=True)}} {
         {% set CallbackInfoType = (method.arguments|last).type %}
-        {% set CallbackType = (CallbackInfoType.members|first).type %}
+        {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
         {{as_cType(CallbackInfoType.name)}} callbackInfo = {};
         callbackInfo.mode = static_cast<{{as_cType(types["callback mode"].name)}}>(callbackMode);
         callbackInfo.callback = [](
@@ -393,7 +395,7 @@
 {% macro render_cpp_callback_info_lambda_method_impl(type, method) %}
     {{render_cpp_callback_info_lambda_method_declaration(type, method, dfn=True)}} {
         {% set CallbackInfoType = (method.arguments|last).type %}
-        {% set CallbackType = (CallbackInfoType.members|first).type %}
+        {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
         using F = void (
             {%- for arg in CallbackType.arguments -%}
                 {%- if not loop.first %}, {% endif -%}
@@ -500,7 +502,10 @@
 static_assert(offsetof(ChainedStruct, sType) == offsetof({{c_prefix}}ChainedStruct, sType),
     "offsetof mismatch for ChainedStruct::sType");
 
-{% for type in by_category["structure"] %}
+//* Special structures that require some custom code generation.
+{% set SpecialStructures = ["device descriptor"] %}
+
+{% for type in by_category["structure"] if type.name.get() not in SpecialStructures %}
     {% set Out = "Out" if type.output else "" %}
     {% set const = "const" if not type.output else "" %}
     {% if type.chained %}
@@ -550,12 +555,64 @@
 
 {% endfor %}
 
+//* Device descriptor is specially implemented in C++ in order to hide callback info. Note that
+//* this is placed at the end of the structs and works for the device descriptor because no other
+//* structs include it as a member. In the future for these special structs, we may need to add
+//* a way to order the definitions w.r.t the topology of the structs.
+{% set type = types["device descriptor"] %}
+{% set CppType = as_cppType(type.name) %}
+namespace detail {
+struct {{CppType}} {
+    ChainedStruct const * nextInChain = nullptr;
+    {% for member in type.members %}
+        {% if member.type.category != "callback info" %}
+            {{as_annotated_cppType(member, type.has_free_members_function) + render_cpp_default_value(member, True, type.has_free_members_function)}};
+        {% else %}
+            {{as_annotated_cType(member)}} = {{CAPI}}_{{member.name.SNAKE_CASE()}}_INIT;
+        {% endif %}
+    {% endfor %}
+};
+}  // namespace detail
+struct {{CppType}} : protected detail::{{CppType}} {
+    inline operator const {{as_cType(type.name)}}&() const noexcept;
+
+    using detail::{{CppType}}::nextInChain;
+    {% for member in type.members %}
+        {% if member.type.category != "callback info" %}
+            using detail::{{CppType}}::{{as_varName(member.name)}};
+        {% endif %}
+    {% endfor %}
+
+    inline {{CppType}}();
+    struct Init;
+    inline {{CppType}}(Init&& init);
+
+    template <typename F, typename T,
+              typename Cb = void (const Device& device, DeviceLostReason reason, const char * message, T userdata),
+              typename = std::enable_if_t<std::is_convertible_v<F, Cb*>>>
+    void SetDeviceLostCallback(CallbackMode callbackMode, F callback, T userdata);
+    template <typename L,
+              typename Cb = std::function<void(const Device& device, DeviceLostReason reason, const char * message)>,
+              typename = std::enable_if_t<std::is_convertible_v<L, Cb>>>
+    void SetDeviceLostCallback(CallbackMode callbackMode, L callback);
+
+    template <typename F, typename T,
+              typename Cb = void (const Device& device, ErrorType type, const char * message, T userdata),
+              typename = std::enable_if_t<std::is_convertible_v<F, Cb*>>>
+    void SetUncapturedErrorCallback(F callback, T userdata);
+    template <typename L,
+              typename Cb = std::function<void(const Device& device, ErrorType type, const char * message)>,
+              typename = std::enable_if_t<std::is_convertible_v<L, Cb>>>
+    void SetUncapturedErrorCallback(L callback);
+};
+
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic push
 // error: 'offsetof' within non-standard-layout type '{{metadata.namespace}}::XXX' is conditionally-supported
 #pragma GCC diagnostic ignored "-Winvalid-offsetof"
 #endif
-{% for type in by_category["structure"] %}
+
+{% for type in by_category["structure"] if type.name.get() not in SpecialStructures %}
     {% set CppType = as_cppType(type.name) %}
     {% set CType = as_cType(type.name) %}
     // {{CppType}} implementation
@@ -589,7 +646,7 @@
                 {%- endfor -%}
             ) {
                 {{as_cMethodNamespaced(type.name, Name("free members"), c_namespace)}}(
-                    *reinterpret_cast<{{as_cType(type.name)}}*>(this));
+                    *reinterpret_cast<{{CType}}*>(this));
             }
         }
 
@@ -608,7 +665,7 @@
             }
             this->~{{CppType}}();
             {% for member in type.members %}
-                detail::AsNonConstReference(this->{{member.name.camelCase()}}) = std::move(rhs.{{member.name.camelCase()}});
+                ::{{metadata.namespace}}::detail::AsNonConstReference(this->{{member.name.camelCase()}}) = std::move(rhs.{{member.name.camelCase()}});
             {% endfor %}
             Reset(rhs);
             return *this;
@@ -618,13 +675,13 @@
         void {{CppType}}::Reset({{CppType}}& value) {
             {{CppType}} defaultValue{};
             {% for member in type.members %}
-                detail::AsNonConstReference(value.{{member.name.camelCase()}}) = defaultValue.{{member.name.camelCase()}};
+                ::{{metadata.namespace}}::detail::AsNonConstReference(value.{{member.name.camelCase()}}) = defaultValue.{{member.name.camelCase()}};
             {% endfor %}
         }
     {% endif %}
 
-    {{CppType}}::operator const {{as_cType(type.name)}}&() const noexcept {
-        return *reinterpret_cast<const {{as_cType(type.name)}}*>(this);
+    {{CppType}}::operator const {{CType}}&() const noexcept {
+        return *reinterpret_cast<const {{CType}}*>(this);
     }
 
     static_assert(sizeof({{CppType}}) == sizeof({{CType}}), "sizeof mismatch for {{CppType}}");
@@ -640,6 +697,119 @@
     {% endfor %}
 
 {% endfor %}
+//* Special implementation for device descriptor.
+{% set type = types["device descriptor"] %}
+{% set CppType = as_cppType(type.name) %}
+{% set CType = as_cType(type.name) %}
+// {{CppType}} implementation
+
+{{CppType}}::operator const {{CType}}&() const noexcept {
+    return *reinterpret_cast<const {{CType}}*>(this);
+}
+
+{{CppType}}::{{CppType}}() : detail::{{CppType}} {} {
+    static_assert(offsetof({{CppType}}, nextInChain) == offsetof({{CType}}, nextInChain),
+                "offsetof mismatch for {{CppType}}::nextInChain");
+    {% for member in type.members %}
+        {% set memberName = member.name.camelCase() %}
+        static_assert(offsetof({{CppType}}, {{memberName}}) == offsetof({{CType}}, {{memberName}}),
+                "offsetof mismatch for {{CppType}}::{{memberName}}");
+    {% endfor %}
+}
+
+struct {{CppType}}::Init {
+    ChainedStruct const * nextInChain;
+    {% for member in type.members if member.type.category != "callback info" %}
+        {% set member_declaration = as_annotated_cppType(member, type.has_free_members_function) + render_cpp_default_value(member, True, type.has_free_members_function) %}
+        {{member_declaration}};
+    {% endfor %}
+};
+
+{{CppType}}::{{CppType}}({{CppType}}::Init&& init) : detail::{{CppType}} {
+    init.nextInChain
+    {%- for member in type.members if member.type.category != "callback info" -%},{{" "}}
+        std::move(init.{{as_varName(member.name)}})
+    {%- endfor -%}
+} {}
+
+static_assert(sizeof({{CppType}}) == sizeof({{CType}}), "sizeof mismatch for {{CppType}}");
+static_assert(alignof({{CppType}}) == alignof({{CType}}), "alignof mismatch for {{CppType}}");
+
+template <typename F, typename T, typename Cb, typename>
+void {{CppType}}::SetDeviceLostCallback(CallbackMode callbackMode, F callback, T userdata) {
+    assert(deviceLostCallbackInfo2.callback == nullptr);
+
+    deviceLostCallbackInfo2.mode = static_cast<WGPUCallbackMode>(callbackMode);
+    deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, char const * message, void* callback, void* userdata) {
+        auto cb = reinterpret_cast<Cb*>(callback);
+        // We manually acquire and release the device to avoid changing any ref counts.
+        auto apiDevice = Device::Acquire(*device);
+        (*cb)(apiDevice, static_cast<DeviceLostReason>(reason), message, static_cast<T>(userdata));
+        apiDevice.MoveToCHandle();
+    };
+    deviceLostCallbackInfo2.userdata1 = reinterpret_cast<void*>(+callback);
+    deviceLostCallbackInfo2.userdata2 = reinterpret_cast<void*>(userdata);
+}
+
+template <typename L, typename Cb, typename>
+void {{CppType}}::SetDeviceLostCallback(CallbackMode callbackMode, L callback) {
+    assert(deviceLostCallbackInfo2.callback == nullptr);
+    using F = void (const Device& device, DeviceLostReason reason, const char * message);
+
+    deviceLostCallbackInfo2.mode = static_cast<WGPUCallbackMode>(callbackMode);
+    if constexpr (std::is_convertible_v<L, F*>) {
+        deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, char const * message, void* callback, void* userdata) {
+            auto cb = reinterpret_cast<F*>(callback);
+            // We manually acquire and release the device to avoid changing any ref counts.
+            auto apiDevice = Device::Acquire(*device);
+            (*cb)(apiDevice, static_cast<DeviceLostReason>(reason), message);
+            apiDevice.MoveToCHandle();
+        };
+        deviceLostCallbackInfo2.userdata1 = reinterpret_cast<void*>(+callback);
+        deviceLostCallbackInfo2.userdata2 = nullptr;
+    } else {
+        auto* lambda = new L(std::move(callback));
+        deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, char const * message, void* callback, void*) {
+            std::unique_ptr<L> lambda(reinterpret_cast<L*>(callback));
+            // We manually acquire and release the device to avoid changing any ref counts.
+            auto apiDevice = Device::Acquire(*device);
+            (*lambda)(apiDevice, static_cast<DeviceLostReason>(reason), message);
+            apiDevice.MoveToCHandle();
+        };
+        deviceLostCallbackInfo2.userdata1 = reinterpret_cast<void*>(lambda);
+        deviceLostCallbackInfo2.userdata2 = nullptr;
+    }
+}
+
+template <typename F, typename T, typename Cb, typename>
+void {{CppType}}::SetUncapturedErrorCallback(F callback, T userdata) {
+    uncapturedErrorCallbackInfo2.callback = [](WGPUDevice const * device, WGPUErrorType type, char const * message, void* callback, void* userdata) {
+        auto cb = reinterpret_cast<Cb*>(callback);
+        // We manually acquire and release the device to avoid changing any ref counts.
+        auto apiDevice = Device::Acquire(*device);
+        (*cb)(apiDevice, static_cast<ErrorType>(type), message, static_cast<T>(userdata));
+        apiDevice.MoveToCHandle();
+    };
+    uncapturedErrorCallbackInfo2.userdata1 = reinterpret_cast<void*>(+callback);
+    uncapturedErrorCallbackInfo2.userdata2 = reinterpret_cast<void*>(userdata);
+}
+
+template <typename L, typename Cb, typename>
+void {{CppType}}::SetUncapturedErrorCallback(L callback) {
+    using F = void (const Device& device, ErrorType type, const char * message);
+    static_assert(std::is_convertible_v<L, F*>, "Uncaptured error callback cannot be a binding lambda");
+
+    uncapturedErrorCallbackInfo2.callback = [](WGPUDevice const * device, WGPUErrorType type, char const * message, void* callback, void* userdata) {
+        auto cb = reinterpret_cast<F*>(callback);
+        // We manually acquire and release the device to avoid changing any ref counts.
+        auto apiDevice = Device::Acquire(*device);
+        (*cb)(apiDevice, static_cast<ErrorType>(type), message);
+        apiDevice.MoveToCHandle();
+    };
+    uncapturedErrorCallbackInfo2.userdata1 = reinterpret_cast<void*>(+callback);
+    uncapturedErrorCallbackInfo2.userdata2 = nullptr;
+}
+
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
diff --git a/generator/templates/art/api_kotlin_types.kt b/generator/templates/art/api_kotlin_types.kt
index d37e0ad..fa8cad2 100644
--- a/generator/templates/art/api_kotlin_types.kt
+++ b/generator/templates/art/api_kotlin_types.kt
@@ -47,7 +47,7 @@
     {%- elif type.category in ['function pointer', 'object'] %}
         {{- type.name.CamelCase() }}
         {%- if optional or default_value %}?{{ ' = null' if emit_defaults }}{% endif %}
-    {%- elif type.category == 'structure' %}
+    {%- elif type.category == 'structure' or type.category == 'callback info' %}
         {{- type.name.CamelCase() }}{{ '?' if optional }}
         {%- if emit_defaults -%}
             {%- if type.has_basic_constructor -%}
diff --git a/generator/templates/art/structures.cpp b/generator/templates/art/structures.cpp
index af901f4..a57fb37 100644
--- a/generator/templates/art/structures.cpp
+++ b/generator/templates/art/structures.cpp
@@ -136,7 +136,7 @@
                                     env->CallLongMethod(mObj, getHandle));
                 }
             }
-            {% elif member.type.category == 'structure' %}
+            {% elif member.type.category == 'structure' or member.type.category == 'callback info' %}
                 //* Mandatory structure.
                 Convert(env, env->CallObjectMethod(obj,
                         env->GetMethodID(clz, "get{{ member.name.CamelCase() }}",
diff --git a/generator/templates/dawn/native/api_structs.cpp b/generator/templates/dawn/native/api_structs.cpp
index 4b4c529..c8e7148 100644
--- a/generator/templates/dawn/native/api_structs.cpp
+++ b/generator/templates/dawn/native/api_structs.cpp
@@ -108,12 +108,12 @@
             return {% if type.extensible or type.chained -%}
                 (nextInChain == rhs.nextInChain) &&
             {%- endif %} std::tie(
-                {% for member in type.members %}
+                {% for member in type.members if member.type.category != 'callback info' %}
                     {{member.name.camelCase()-}}
                     {{ "," if not loop.last else "" }}
                 {% endfor %}
             ) == std::tie(
-                {% for member in type.members %}
+                {% for member in type.members if member.type.category != 'callback info' %}
                     rhs.{{member.name.camelCase()-}}
                     {{ "," if not loop.last else "" }}
                 {% endfor %}
diff --git a/generator/templates/dawn/native/api_structs.h b/generator/templates/dawn/native/api_structs.h
index ae28708..36f0e71 100644
--- a/generator/templates/dawn/native/api_structs.h
+++ b/generator/templates/dawn/native/api_structs.h
@@ -32,6 +32,7 @@
 #define {{DIR}}_{{namespace.upper()}}_STRUCTS_H_
 
 {% set api = metadata.api.lower() %}
+{% set CAPI = metadata.c_prefix %}
 #include "dawn/{{api}}_cpp.h"
 {% set impl_dir = metadata.impl_dir + "/" if metadata.impl_dir else "" %}
 {% set native_namespace = namespace_name.namespace_case() %}
@@ -46,6 +47,8 @@
         {{" "}}= nullptr
     {%- elif member.type.category == "object" and member.optional -%}
         {{" "}}= nullptr
+    {%- elif member.type.category == "callback info" -%}
+        {{" "}}= {{CAPI}}_{{member.name.SNAKE_CASE()}}_INIT
     {%- elif member.type.category in ["enum", "bitmask"] and member.default_value != None -%}
         {{" "}}= {{namespace}}::{{as_cppType(member.type.name)}}::{{as_cppEnum(Name(member.default_value))}}
     {%- elif member.type.category == "native" and member.default_value != None -%}
diff --git a/generator/templates/dawn/wire/WireCmd.cpp b/generator/templates/dawn/wire/WireCmd.cpp
index 0ff5801..efcfe0e 100644
--- a/generator/templates/dawn/wire/WireCmd.cpp
+++ b/generator/templates/dawn/wire/WireCmd.cpp
@@ -107,6 +107,8 @@
                 {%- endif -%}
             ));
         {%- endif -%}
+    {%- elif member.type.category == 'callback info' %}
+        {{out}} = WGPU_{{member.type.name.SNAKE_CASE()}}_INIT;
     {%- elif not is_wire_serializable(member.type) %}
         {{out}} = nullptr;
     {%- elif member.type.name.get() == "size_t" -%}
@@ -267,8 +269,8 @@
         //* "length", but order is not always given.
         {% for member in members | sort(reverse=true, attribute="annotation") %}
             {% set memberName = as_varName(member.name) %}
-            //* Skip serialization for custom serialized members.
-            {% if member.skip_serialize %}
+            //* Skip serialization for custom serialized members and callback infos.
+            {% if member.skip_serialize or member.type.category == 'callback info' %}
                 {% continue %}
             {% endif %}
             //* Value types are directly in the transfer record, objects being replaced with their IDs.
diff --git a/generator/templates/mock_api.cpp b/generator/templates/mock_api.cpp
index 7cd252a..7e28170 100644
--- a/generator/templates/mock_api.cpp
+++ b/generator/templates/mock_api.cpp
@@ -76,12 +76,11 @@
 
 //* Generate the older Call*Callback if there is no Future call equivalent.
 //* Includes:
-//*   - setUncapturedErrorCallback
 //*   - setLoggingCallback
-{% set LegacyCallbackFunctions = ['set uncaptured error callback', 'set logging callback'] %}
+{% set LegacyCallbackFunctions = ['set logging callback'] %}
 
 //* Manually implemented mock functions due to incompatibility.
-{% set ManuallyMockedFunctions = ['set device lost callback'] %}
+{% set ManuallyMockedFunctions = ['set device lost callback', 'set uncaptured error callback'] %}
 
 {% for type in by_category["object"] %}
     {% for method in type.methods if method.name.get() not in ManuallyMockedFunctions %}
@@ -168,7 +167,7 @@
                 return {mNextFutureID++};
             }
             {% set CallbackInfoType = (method.arguments|last).type %}
-            {% set CallbackType = (CallbackInfoType.members|first).type %}
+            {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
             void ProcTableAsClass::Call{{Suffix}}Callback(
                 {{-as_cType(type.name)}} {{as_varName(type.name)}}
                 {%- for arg in CallbackType.arguments -%}
@@ -223,13 +222,21 @@
     {% endfor %}
 {% endfor %}
 
-// Manually implement device lost related callback helpers for testing.
+// Manually implement some callback helpers for testing.
 void ProcTableAsClass::DeviceSetDeviceLostCallback(WGPUDevice device,
                                                    WGPUDeviceLostCallback callback,
                                                    void* userdata) {
     ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device);
-    object->mDeviceLostOldCallback = callback;
-    object->mDeviceLostUserdata = userdata;
+    object->mDeviceLostCallback = [](WGPUDevice const*, WGPUDeviceLostReason reason,
+                                     char const* message, void* callback, void* userdata) {
+        if (callback == nullptr) {
+            return;
+        }
+        auto cb = reinterpret_cast<WGPUDeviceLostCallback>(callback);
+        cb(reason, message, userdata);
+    };
+    object->mDeviceLostUserdata1 = reinterpret_cast<void*>(callback);
+    object->mDeviceLostUserdata2 = userdata;
 
     OnDeviceSetDeviceLostCallback(device, callback, userdata);
 }
@@ -237,12 +244,32 @@
                                                                WGPUDeviceLostReason reason,
                                                                char const* message) {
     ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device);
-    // If we have an old callback set, call that one, otherwise call the new one.
-    if (object->mDeviceLostOldCallback != nullptr) {
-        object->mDeviceLostOldCallback(reason, message, object->mDeviceLostUserdata);
-    } else {
-        object->mDeviceLostCallback(&device, reason, message, object->mDeviceLostUserdata);
-    }
+    object->mDeviceLostCallback(&device, reason, message, object->mDeviceLostUserdata1,
+                                object->mDeviceLostUserdata2);
+}
+void ProcTableAsClass::DeviceSetUncapturedErrorCallback(WGPUDevice device,
+                                                        WGPUErrorCallback callback,
+                                                        void* userdata) {
+    ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device);
+    object->mUncapturedErrorCallback = [](WGPUDevice const*, WGPUErrorType type,
+                                          char const* message, void* callback, void* userdata) {
+        if (callback == nullptr) {
+            return;
+        }
+        auto cb = reinterpret_cast<WGPUErrorCallback>(callback);
+        cb(type, message, userdata);
+    };
+    object->mUncapturedErrorUserdata1 = reinterpret_cast<void*>(callback);
+    object->mUncapturedErrorUserdata2 = userdata;
+
+    OnDeviceSetUncapturedErrorCallback(device, callback, userdata);
+}
+void ProcTableAsClass::CallDeviceSetUncapturedErrorCallbackCallback(WGPUDevice device,
+                                                                    WGPUErrorType type,
+                                                                    char const* message) {
+    ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device);
+    object->mUncapturedErrorCallback(&device, type, message, object->mUncapturedErrorUserdata1,
+                                     object->mUncapturedErrorUserdata2);
 }
 
 {% for type in by_category["object"] %}
diff --git a/generator/templates/mock_api.h b/generator/templates/mock_api.h
index 45b5713..acaf37f 100644
--- a/generator/templates/mock_api.h
+++ b/generator/templates/mock_api.h
@@ -60,13 +60,11 @@
 
         //* Generate the older Call*Callback if there is no Future call equivalent.
         //* Includes:
-        //*   - setDeviceLostCallback
-        //*   - setUncapturedErrorCallback
         //*   - setLoggingCallback
-        {%- set LegacyCallbackFunctions = ['set uncaptured error callback', 'set logging callback'] %}
+        {%- set LegacyCallbackFunctions = ['set logging callback'] %}
 
         //* Manually implemented mock functions due to incompatibility.
-        {% set ManuallyMockedFunctions = ['set device lost callback'] %}
+        {% set ManuallyMockedFunctions = ['set device lost callback', 'set uncaptured error callback'] %}
 
         {%- for type in by_category["object"] %}
 
@@ -103,7 +101,7 @@
                     {%- endfor -%}
                 ) = 0;
                 {% set CallbackInfoType = (method.arguments|last).type %}
-                {% set CallbackType = (CallbackInfoType.members|first).type %}
+                {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
                 void Call{{Suffix}}Callback(
                     {{-as_cType(type.name)}} {{as_varName(type.name)}}
                     {%- for arg in CallbackType.arguments -%}
@@ -145,7 +143,7 @@
             {% endfor %}
         {% endfor %}
 
-        // Manually implement device lost related callback helpers for testing.
+        // Manually implement some callback helpers for testing.
         void DeviceSetDeviceLostCallback(WGPUDevice device,
                                          WGPUDeviceLostCallback callback,
                                          void* userdata);
@@ -155,6 +153,15 @@
         void CallDeviceSetDeviceLostCallbackCallback(WGPUDevice device,
                                                      WGPUDeviceLostReason reason,
                                                      char const* message);
+        void DeviceSetUncapturedErrorCallback(WGPUDevice device,
+                                              WGPUErrorCallback callback,
+                                              void* userdata);
+        virtual void OnDeviceSetUncapturedErrorCallback(WGPUDevice device,
+                                                        WGPUErrorCallback callback,
+                                                        void* userdata) = 0;
+        void CallDeviceSetUncapturedErrorCallbackCallback(WGPUDevice device,
+                                                          WGPUErrorType type,
+                                                          char const* message);
 
         struct Object {
             ProcTableAsClass* procs = nullptr;
@@ -173,16 +180,19 @@
                 {% endfor %}
                 {% for method in type.methods if has_callbackInfoStruct(method) %}
                     {% set CallbackInfoType = (method.arguments|last).type %}
-                    {% set CallbackType = (CallbackInfoType.members|first).type %}
+                    {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
                     void* m{{as_CppMethodSuffix(type.name, method.name)}}Userdata1 = 0;
                     void* m{{as_CppMethodSuffix(type.name, method.name)}}Userdata2 = 0;
                     {{as_cType(CallbackType.name)}} m{{as_CppMethodSuffix(type.name, method.name)}}Callback = nullptr;
                 {% endfor %}
             {% endfor %}
-            // Manually implement device lost related callback helpers for testing.
-            WGPUDeviceLostCallback mDeviceLostOldCallback = nullptr;
-            WGPUDeviceLostCallbackNew mDeviceLostCallback = nullptr;
-            void* mDeviceLostUserdata = 0;
+            // Manually implement some callback helpers for testing.
+            WGPUDeviceLostCallback2 mDeviceLostCallback = nullptr;
+            void* mDeviceLostUserdata1 = 0;
+            void* mDeviceLostUserdata2 = 0;
+            WGPUUncapturedErrorCallback mUncapturedErrorCallback = nullptr;
+            void* mUncapturedErrorUserdata1 = 0;
+            void* mUncapturedErrorUserdata2 = 0;
         };
 
     private:
@@ -233,11 +243,15 @@
             {% endfor %}
         {% endfor %}
 
-        // Manually implement device lost related callback helpers for testing.
+        // Manually implement some callback helpers for testing.
         MOCK_METHOD(void,
                     OnDeviceSetDeviceLostCallback,
                     (WGPUDevice device, WGPUDeviceLostCallback callback, void* userdata),
                     (override));
+        MOCK_METHOD(void,
+                    OnDeviceSetUncapturedErrorCallback,
+                    (WGPUDevice device, WGPUErrorCallback callback, void* userdata),
+                    (override));
 };
 
 #endif  // MOCK_{{API}}_H
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index 0141ecd..93c5501 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -101,7 +101,8 @@
     "request adapter callback info 2": {
         "category": "callback info",
         "members": [
-           {"name": "callback", "type": "request adapter callback 2"}
+            {"name": "mode", "type": "callback mode"},
+            {"name": "callback", "type": "request adapter callback 2"}
         ]
    },
     "request adapter status": {
@@ -258,7 +259,9 @@
             {"name": "device lost callback", "type": "device lost callback", "default": "nullptr", "tags": ["deprecated"]},
             {"name": "device lost userdata", "type": "void *", "default": "nullptr", "tags": ["deprecated"]},
             {"name": "device lost callback info", "type": "device lost callback info"},
-            {"name": "uncaptured error callback info", "type": "uncaptured error callback info"}
+            {"name": "uncaptured error callback info", "type": "uncaptured error callback info"},
+            {"name": "device lost callback info 2", "type": "device lost callback info 2"},
+            {"name": "uncaptured error callback info 2", "type": "uncaptured error callback info 2"}
         ]
     },
     "dawn toggles descriptor": {
@@ -728,6 +731,7 @@
     "buffer map callback info 2": {
         "category": "callback info",
         "members": [
+            {"name": "mode", "type": "callback mode"},
             {"name": "callback", "type": "buffer map callback 2"}
         ]
     },
@@ -1014,6 +1018,7 @@
     "compilation info callback info 2": {
         "category": "callback info",
         "members": [
+            {"name": "mode", "type": "callback mode"},
             {"name": "callback", "type": "compilation info callback 2"}
         ]
     },
@@ -1240,6 +1245,7 @@
     "create compute pipeline async callback info 2": {
         "category": "callback info",
         "members": [
+            {"name": "mode", "type": "callback mode"},
             {"name": "callback", "type": "create compute pipeline async callback 2"}
         ]
     },
@@ -1285,6 +1291,7 @@
     "create render pipeline async callback info 2": {
         "category": "callback info",
         "members": [
+            {"name": "mode", "type": "callback mode"},
             {"name": "callback", "type": "create render pipeline async callback 2"}
         ]
     },
@@ -1686,6 +1693,14 @@
             {"name": "userdata", "type": "void *"}
         ]
     },
+    "device lost callback 2": {
+        "category": "callback function",
+        "args": [
+            {"name": "device", "type": "device", "annotation": "const*", "length": 1},
+            {"name": "reason", "type": "device lost reason"},
+            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"}
+        ]
+    },
     "device lost callback info": {
         "category": "structure",
         "extensible": "in",
@@ -1695,6 +1710,13 @@
             {"name": "userdata", "type": "void *", "default": "nullptr"}
         ]
     },
+    "device lost callback info 2": {
+        "category": "callback info",
+        "members": [
+            {"name": "mode", "type": "callback mode"},
+            {"name": "callback", "type": "device lost callback 2", "default": "nullptr"}
+        ]
+    },
     "device lost reason": {
         "category": "enum",
         "emscripten_no_enum_table": true,
@@ -1716,6 +1738,14 @@
             {"name": "userdata", "type": "void *"}
         ]
     },
+    "uncaptured error callback": {
+        "category": "callback function",
+        "args": [
+            {"name": "device", "type": "device", "annotation": "const*", "length": 1},
+            {"name": "type", "type": "error type"},
+            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"}
+        ]
+    },
     "uncaptured error callback info": {
         "category": "structure",
         "extensible": "in",
@@ -1724,6 +1754,12 @@
             {"name": "userdata", "type": "void *", "default": "nullptr"}
         ]
     },
+    "uncaptured error callback info 2": {
+        "category": "callback info",
+        "members": [
+            {"name": "callback", "type": "uncaptured error callback", "default": "nullptr"}
+        ]
+    },
     "pop error scope status": {
         "category": "enum",
         "emscripten_no_enum_table": true,
@@ -1762,6 +1798,7 @@
     "pop error scope callback info 2": {
         "category": "callback info",
         "members": [
+            {"name": "mode", "type": "callback mode"},
             {"name": "callback", "type": "pop error scope callback 2"}
         ]
     },
@@ -2999,6 +3036,7 @@
     "queue work done callback info 2": {
         "category": "callback info",
         "members": [
+            {"name": "mode", "type": "callback mode"},
             {"name": "callback", "type": "queue work done callback 2"}
         ]
     },
@@ -3449,7 +3487,8 @@
     "request device callback info 2": {
         "category": "callback info",
         "members": [
-           {"name": "callback", "type": "request device callback 2"}
+            {"name": "mode", "type": "callback mode"},
+            {"name": "callback", "type": "request device callback 2"}
         ]
    },
 
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index e0ac3fd..3504aab 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -165,21 +165,53 @@
     raw_ptr<void> mUserdata;
 };
 
-static constexpr UncapturedErrorCallbackInfo kEmptyUncapturedErrorCallbackInfo = {nullptr, nullptr,
-                                                                                  nullptr};
+void LegacyDeviceLostCallback(WGPUDevice const* device,
+                              WGPUDeviceLostReason reason,
+                              char const* message,
+                              void* callback,
+                              void* userdata) {
+    if (callback == nullptr) {
+        return;
+    }
+    auto cb = reinterpret_cast<WGPUDeviceLostCallback>(callback);
+    cb(reason, message, userdata);
+}
+
+void LegacyDeviceLostCallback2(WGPUDevice const* device,
+                               WGPUDeviceLostReason reason,
+                               char const* message,
+                               void* callback,
+                               void* userdata) {
+    if (callback == nullptr) {
+        return;
+    }
+    auto cb = reinterpret_cast<WGPUDeviceLostCallbackNew>(callback);
+    cb(device, reason, message, userdata);
+}
+
+void LegacyUncapturedErrorCallback(WGPUDevice const* device,
+                                   WGPUErrorType type,
+                                   const char* message,
+                                   void* callback,
+                                   void* userdata) {
+    if (callback == nullptr) {
+        return;
+    }
+    auto cb = reinterpret_cast<WGPUErrorCallback>(callback);
+    cb(type, message, userdata);
+}
+
+static constexpr WGPUUncapturedErrorCallbackInfo2 kEmptyUncapturedErrorCallbackInfo = {
+    nullptr, nullptr, nullptr, nullptr};
 
 }  // anonymous namespace
 
-DeviceBase::DeviceLostEvent::DeviceLostEvent(const DeviceLostCallbackInfo& callbackInfo)
-    : TrackedEvent(callbackInfo.mode, SystemEvent::CreateNonProgressingEvent()),
-      mCallback(callbackInfo.callback),
-      mUserdata(callbackInfo.userdata) {}
-
-DeviceBase::DeviceLostEvent::DeviceLostEvent(wgpu::DeviceLostCallback oldCallback, void* userdata)
-    : TrackedEvent(wgpu::CallbackMode::AllowProcessEvents,
+DeviceBase::DeviceLostEvent::DeviceLostEvent(const WGPUDeviceLostCallbackInfo2& callbackInfo)
+    : TrackedEvent(static_cast<wgpu::CallbackMode>(callbackInfo.mode),
                    SystemEvent::CreateNonProgressingEvent()),
-      mOldCallback(oldCallback),
-      mUserdata(userdata) {}
+      mCallback(callbackInfo.callback),
+      mUserdata1(callbackInfo.userdata1),
+      mUserdata2(callbackInfo.userdata2) {}
 
 DeviceBase::DeviceLostEvent::~DeviceLostEvent() {
     EnsureComplete(EventCompletionType::Shutdown);
@@ -192,9 +224,9 @@
 
 #if defined(DAWN_ENABLE_ASSERTS)
     // TODO(crbug.com/dawn/2465) Make default AllowSpontaneous once SetDeviceLostCallback is gone.
-    static constexpr DeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = {
-        nullptr, wgpu::CallbackMode::AllowProcessEvents,
-        [](WGPUDevice const*, WGPUDeviceLostReason, char const*, void*) {
+    static constexpr WGPUDeviceLostCallbackInfo2 kDefaultDeviceLostCallbackInfo = {
+        nullptr, WGPUCallbackMode_AllowProcessEvents,
+        [](WGPUDevice const*, WGPUDeviceLostReason, char const*, void*, void*) {
             static bool calledOnce = false;
             if (!calledOnce) {
                 calledOnce = true;
@@ -203,35 +235,31 @@
                                       "suppress this message, set the callback explicitly.";
             }
         },
-        nullptr};
+        nullptr, nullptr};
 #else
-    static constexpr DeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = {
-        nullptr, wgpu::CallbackMode::AllowProcessEvents, nullptr, nullptr};
+    static constexpr WGPUDeviceLostCallbackInfo2 kDefaultDeviceLostCallbackInfo = {
+        nullptr, WGPUCallbackMode_AllowProcessEvents, nullptr, nullptr, nullptr};
 #endif  // DAWN_ENABLE_ASSERTS
 
-    Ref<DeviceBase::DeviceLostEvent> lostEvent;
-    if (descriptor->deviceLostCallback != nullptr) {
+    WGPUDeviceLostCallbackInfo2 deviceLostCallbackInfo = kDefaultDeviceLostCallbackInfo;
+    if (descriptor->deviceLostCallbackInfo2.callback != nullptr) {
+        deviceLostCallbackInfo = descriptor->deviceLostCallbackInfo2;
+    } else if (descriptor->deviceLostCallbackInfo.callback != nullptr) {
+        auto& callbackInfo = descriptor->deviceLostCallbackInfo;
+        deviceLostCallbackInfo = {
+            ToAPI(callbackInfo.nextInChain), ToAPI(callbackInfo.mode), &LegacyDeviceLostCallback2,
+            reinterpret_cast<void*>(callbackInfo.callback), callbackInfo.userdata};
+    } else if (descriptor->deviceLostCallback != nullptr) {
         dawn::WarningLog()
             << "DeviceDescriptor.deviceLostCallback and DeviceDescriptor.deviceLostUserdata are "
                "deprecated. Use DeviceDescriptor.deviceLostCallbackInfo instead.";
-        lostEvent = AcquireRef(new DeviceBase::DeviceLostEvent(descriptor->deviceLostCallback,
-                                                               descriptor->deviceLostUserdata));
-    } else {
-        DeviceLostCallbackInfo deviceLostCallbackInfo = kDefaultDeviceLostCallbackInfo;
-        if (descriptor->deviceLostCallbackInfo.callback != nullptr ||
-            descriptor->deviceLostCallbackInfo.mode != wgpu::CallbackMode::WaitAnyOnly) {
-            deviceLostCallbackInfo = descriptor->deviceLostCallbackInfo;
-            if (deviceLostCallbackInfo.mode != wgpu::CallbackMode::AllowSpontaneous) {
-                // TODO(dawn:2458) Currently we default the callback mode to ProcessEvents if not
-                // passed for backwards compatibility. We should add warning logging for it though
-                // when available.
-                deviceLostCallbackInfo.mode = wgpu::CallbackMode::AllowProcessEvents;
-            }
-        }
-        lostEvent = AcquireRef(new DeviceBase::DeviceLostEvent(deviceLostCallbackInfo));
+        deviceLostCallbackInfo = {nullptr, WGPUCallbackMode_AllowProcessEvents,
+                                  &LegacyDeviceLostCallback,
+                                  reinterpret_cast<void*>(descriptor->deviceLostCallback),
+                                  descriptor->deviceLostUserdata};
     }
 
-    return lostEvent;
+    return AcquireRef(new DeviceBase::DeviceLostEvent(deviceLostCallbackInfo));
 }
 
 void DeviceBase::DeviceLostEvent::Complete(EventCompletionType completionType) {
@@ -239,17 +267,25 @@
         mReason = wgpu::DeviceLostReason::InstanceDropped;
         mMessage = "A valid external Instance reference no longer exists.";
     }
+
+    auto device = ToAPI(mDevice.Get());
+    void* userdata1 = mUserdata1.ExtractAsDangling();
+    void* userdata2 = mUserdata2.ExtractAsDangling();
+
     if (mReason == wgpu::DeviceLostReason::InstanceDropped ||
         mReason == wgpu::DeviceLostReason::FailedCreation) {
-        mDevice = nullptr;
+        device = nullptr;
+    }
+    if (mCallback) {
+        mCallback(&device, ToAPI(mReason), mMessage.c_str(), userdata1, userdata2);
     }
 
-    if (mOldCallback) {
-        mOldCallback(ToAPI(mReason), mMessage.c_str(), mUserdata.ExtractAsDangling());
-    } else if (mCallback) {
-        auto device = ToAPI(mDevice.Get());
-        mCallback(&device, ToAPI(mReason), mMessage.c_str(), mUserdata.ExtractAsDangling());
+    // After the device lost callback fires, the uncaptured error callback is no longer valid so we
+    // unset it here.
+    if (mDevice != nullptr) {
+        mDevice->mUncapturedErrorCallbackInfo = kEmptyUncapturedErrorCallbackInfo;
     }
+
     mDevice = nullptr;
 }
 
@@ -313,9 +349,9 @@
     mLostEvent->mDevice = this;
 
 #if defined(DAWN_ENABLE_ASSERTS)
-    static constexpr UncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo = {
+    static constexpr WGPUUncapturedErrorCallbackInfo2 kDefaultUncapturedErrorCallbackInfo = {
         nullptr,
-        [](WGPUErrorType, char const*, void*) {
+        [](WGPUDevice const*, WGPUErrorType, char const*, void*, void*) {
             static bool calledOnce = false;
             if (!calledOnce) {
                 calledOnce = true;
@@ -324,14 +360,19 @@
                                       "and suppress this message, set the callback explicitly.";
             }
         },
-        nullptr};
+        nullptr, nullptr};
 #else
-    static constexpr UncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo =
+    static constexpr WGPUUncapturedErrorCallbackInfo2 kDefaultUncapturedErrorCallbackInfo =
         kEmptyUncapturedErrorCallbackInfo;
 #endif  // DAWN_ENABLE_ASSERTS
     mUncapturedErrorCallbackInfo = kDefaultUncapturedErrorCallbackInfo;
-    if (descriptor->uncapturedErrorCallbackInfo.callback != nullptr) {
-        mUncapturedErrorCallbackInfo = descriptor->uncapturedErrorCallbackInfo;
+    if (descriptor->uncapturedErrorCallbackInfo2.callback != nullptr) {
+        mUncapturedErrorCallbackInfo = descriptor->uncapturedErrorCallbackInfo2;
+    } else if (descriptor->uncapturedErrorCallbackInfo.callback != nullptr) {
+        auto& callbackInfo = descriptor->uncapturedErrorCallbackInfo;
+        mUncapturedErrorCallbackInfo = {
+            ToAPI(callbackInfo.nextInChain), &LegacyUncapturedErrorCallback,
+            reinterpret_cast<void*>(callbackInfo.callback), callbackInfo.userdata};
     }
 
     AdapterProperties adapterProperties;
@@ -406,7 +447,8 @@
     mFormatTable = BuildFormatTable(this);
 
     DeviceDescriptor desc = {};
-    desc.deviceLostCallbackInfo = {nullptr, wgpu::CallbackMode::AllowSpontaneous};
+    desc.deviceLostCallbackInfo2 = {nullptr, WGPUCallbackMode_AllowSpontaneous, nullptr, nullptr,
+                                    nullptr};
     mLostEvent = DeviceLostEvent::Create(&desc);
     mLostEvent->mDevice = this;
 }
@@ -751,9 +793,10 @@
             // Only call the uncaptured error callback if the device is alive. After the
             // device is lost, the uncaptured error callback should cease firing.
             if (mUncapturedErrorCallbackInfo.callback != nullptr && mState == State::Alive) {
+                auto device = ToAPI(this);
                 mUncapturedErrorCallbackInfo.callback(
-                    static_cast<WGPUErrorType>(ToWGPUErrorType(type)), messageStr.c_str(),
-                    mUncapturedErrorCallbackInfo.userdata);
+                    &device, ToAPI(ToWGPUErrorType(type)), messageStr.c_str(),
+                    mUncapturedErrorCallbackInfo.userdata1, mUncapturedErrorCallbackInfo.userdata2);
             }
         }
     }
@@ -772,6 +815,10 @@
 }
 
 void DeviceBase::APISetUncapturedErrorCallback(wgpu::ErrorCallback callback, void* userdata) {
+    GetInstance()->EmitDeprecationWarning(
+        "SetUncapturedErrorCallback is deprecated. Pass the callback in the device descriptor "
+        "instead.");
+
     // The registered callback function and userdata pointer are stored and used by deferred
     // callback tasks, and after setting a different callback (especially in the case of
     // resetting) the resources pointed by such pointer may be freed. Flush all deferred
@@ -788,7 +835,8 @@
     if (IsLost()) {
         return;
     }
-    mUncapturedErrorCallbackInfo = {nullptr, callback, userdata};
+    mUncapturedErrorCallbackInfo = {nullptr, &LegacyUncapturedErrorCallback,
+                                    reinterpret_cast<void*>(callback), userdata};
 }
 
 void DeviceBase::APISetDeviceLostCallback(wgpu::DeviceLostCallback callback, void* userdata) {
@@ -806,15 +854,16 @@
     // after Dawn device is destroyed and before Dawn wire server is destroyed.
     if (callback == nullptr) {
         mLostEvent->mCallback = nullptr;
-        mLostEvent->mOldCallback = nullptr;
-        mLostEvent->mUserdata = nullptr;
+        mLostEvent->mUserdata1 = nullptr;
+        mLostEvent->mUserdata2 = nullptr;
         return;
     }
     if (IsLost()) {
         return;
     }
-    mLostEvent->mOldCallback = callback;
-    mLostEvent->mUserdata = userdata;
+    mLostEvent->mCallback = &LegacyDeviceLostCallback;
+    mLostEvent->mUserdata1 = reinterpret_cast<void*>(callback);
+    mLostEvent->mUserdata2 = userdata;
 }
 
 void DeviceBase::APIPushErrorScope(wgpu::ErrorFilter filter) {
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index f8288c5..e7d3e9c 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -96,17 +96,14 @@
         wgpu::DeviceLostReason mReason;
         std::string mMessage;
 
-        wgpu::DeviceLostCallbackNew mCallback = nullptr;
-        // TODO(https://crbug.com/dawn/2465): Remove old callback when setters are deprecated, and
-        // move userdata into private.
-        wgpu::DeviceLostCallback mOldCallback = nullptr;
-        raw_ptr<void> mUserdata;
+        WGPUDeviceLostCallback2 mCallback = nullptr;
+        raw_ptr<void> mUserdata1;
+        raw_ptr<void> mUserdata2;
         // Note that the device is set when the event is passed to construct a device.
         Ref<DeviceBase> mDevice = nullptr;
 
       private:
-        explicit DeviceLostEvent(const DeviceLostCallbackInfo& callbackInfo);
-        DeviceLostEvent(wgpu::DeviceLostCallback oldCallback, void* userdata);
+        explicit DeviceLostEvent(const WGPUDeviceLostCallbackInfo2& callbackInfo);
         ~DeviceLostEvent() override;
 
         void Complete(EventCompletionType completionType) override;
@@ -550,7 +547,7 @@
                                                     const TextureCopy& dst,
                                                     const Extent3D& copySizePixels) = 0;
 
-    UncapturedErrorCallbackInfo mUncapturedErrorCallbackInfo;
+    WGPUUncapturedErrorCallbackInfo2 mUncapturedErrorCallbackInfo;
 
     std::shared_mutex mLoggingMutex;
     wgpu::LoggingCallback mLoggingCallback = nullptr;
diff --git a/src/dawn/tests/DawnNativeTest.cpp b/src/dawn/tests/DawnNativeTest.cpp
index 3009641..89b69cf 100644
--- a/src/dawn/tests/DawnNativeTest.cpp
+++ b/src/dawn/tests/DawnNativeTest.cpp
@@ -81,7 +81,6 @@
 
     adapter = instance->EnumerateAdapters(&options)[0];
     device = wgpu::Device::Acquire(CreateTestDevice());
-    device.SetUncapturedErrorCallback(DawnNativeTest::OnDeviceError, nullptr);
 }
 
 std::unique_ptr<dawn::platform::Platform> DawnNativeTest::CreateTestPlatform() {
@@ -89,11 +88,12 @@
 }
 
 WGPUDevice DawnNativeTest::CreateTestDevice() {
-    return adapter.CreateDevice();
-}
+    wgpu::DeviceDescriptor desc = {};
+    desc.SetUncapturedErrorCallback(
+        [](const wgpu::Device&, wgpu::ErrorType type, const char* message) {
+            DAWN_ASSERT(type != wgpu::ErrorType::NoError);
+            FAIL() << "Unexpected error: " << message;
+        });
 
-// static
-void DawnNativeTest::OnDeviceError(WGPUErrorType type, const char* message, void* userdata) {
-    DAWN_ASSERT(type != WGPUErrorType_NoError);
-    FAIL() << "Unexpected error: " << message;
+    return adapter.CreateDevice(&desc);
 }
diff --git a/src/dawn/tests/DawnNativeTest.h b/src/dawn/tests/DawnNativeTest.h
index 0dca092..831315a 100644
--- a/src/dawn/tests/DawnNativeTest.h
+++ b/src/dawn/tests/DawnNativeTest.h
@@ -53,7 +53,7 @@
     void SetUp() override;
 
     virtual std::unique_ptr<dawn::platform::Platform> CreateTestPlatform();
-    virtual WGPUDevice CreateTestDevice();
+    WGPUDevice CreateTestDevice();
 
   protected:
     std::unique_ptr<dawn::platform::Platform> platform;
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index fdfe569..cf6fe8e 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -1123,16 +1123,6 @@
     deviceDescriptor.requiredFeatures = requiredFeatures.data();
     deviceDescriptor.requiredFeatureCount = requiredFeatures.size();
 
-    // Set up the mocks for device loss.
-    void* deviceUserdata = GetUniqueUserdata();
-    deviceDescriptor.deviceLostCallbackInfo.mode = wgpu::CallbackMode::AllowSpontaneous;
-    deviceDescriptor.deviceLostCallbackInfo.callback = mDeviceLostCallback.Callback();
-    deviceDescriptor.deviceLostCallbackInfo.userdata =
-        mDeviceLostCallback.MakeUserdata(deviceUserdata);
-    // The loss of the device is expected to happen at the end of the test so at it directly.
-    EXPECT_CALL(mDeviceLostCallback, Call(_, WGPUDeviceLostReason_Destroyed, _, deviceUserdata))
-        .Times(AtMost(1));
-
     wgpu::DawnCacheDeviceDescriptor cacheDesc = {};
     deviceDescriptor.nextInChain = &cacheDesc;
     cacheDesc.isolationKey = isolationKey.c_str();
@@ -1155,15 +1145,22 @@
     // RequestDevice is overriden by CreateDeviceImpl and device descriptor is ignored by it.
     wgpu::DeviceDescriptor deviceDesc = {};
 
+    // Set up the mocks for device loss.
+    deviceDesc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
+                                     mDeviceLostCallback.Callback());
+    deviceDesc.SetUncapturedErrorCallback(mDeviceErrorCallback.TemplatedCallback(),
+                                          mDeviceErrorCallback.TemplatedCallbackUserdata());
+
     adapter.RequestDevice(&deviceDesc, wgpu::CallbackMode::AllowSpontaneous,
                           [&apiDevice](wgpu::RequestDeviceStatus, wgpu::Device result,
                                        const char*) { apiDevice = std::move(result); });
     FlushWire();
     DAWN_ASSERT(apiDevice);
 
-    // Set up the mocks for uncaptured errors.
-    apiDevice.SetUncapturedErrorCallback(mDeviceErrorCallback.Callback(),
-                                         mDeviceErrorCallback.MakeUserdata(apiDevice.Get()));
+    // The loss of the device is expected to happen at the end of the test so add it directly.
+    EXPECT_CALL(mDeviceLostCallback,
+                Call(CHandleIs(apiDevice.Get()), wgpu::DeviceLostReason::Destroyed, _))
+        .Times(AtMost(1));
 
     apiDevice.SetLoggingCallback(
         [](WGPULoggingType type, char const* message, void*) {
@@ -1252,7 +1249,9 @@
         resolvedDevice = device;
     }
 
-    EXPECT_CALL(mDeviceLostCallback, Call(_, WGPUDeviceLostReason_Unknown, _, _)).Times(1);
+    EXPECT_CALL(mDeviceLostCallback,
+                Call(CHandleIs(resolvedDevice.Get()), wgpu::DeviceLostReason::Unknown, _))
+        .Times(1);
     resolvedDevice.ForceLoss(wgpu::DeviceLostReason::Unknown, "Device lost for testing");
     resolvedDevice.Tick();
 }
diff --git a/src/dawn/tests/DawnTest.h b/src/dawn/tests/DawnTest.h
index bbc9366..7ec0386 100644
--- a/src/dawn/tests/DawnTest.h
+++ b/src/dawn/tests/DawnTest.h
@@ -122,15 +122,20 @@
 #define EXPECT_TEXTURE_FLOAT16_EQ(...) \
     AddTextureExpectation<float, uint16_t>(__FILE__, __LINE__, __VA_ARGS__)
 
-#define ASSERT_DEVICE_ERROR_MSG_ON(device, statement, matcher)                    \
-    FlushWire();                                                                  \
-    EXPECT_CALL(mDeviceErrorCallback,                                             \
-                Call(testing::Ne(WGPUErrorType_NoError), matcher, device.Get())); \
-    statement;                                                                    \
-    instance.ProcessEvents();                                                     \
-    FlushWire();                                                                  \
-    testing::Mock::VerifyAndClearExpectations(&mDeviceErrorCallback);             \
-    do {                                                                          \
+// Matcher for C++ types to verify that their internal C-handles are identical.
+MATCHER_P(CHandleIs, cType, "") {
+    return arg.Get() == cType;
+}
+
+#define ASSERT_DEVICE_ERROR_MSG_ON(device, statement, matcher)                                  \
+    FlushWire();                                                                                \
+    EXPECT_CALL(mDeviceErrorCallback,                                                           \
+                Call(CHandleIs(device.Get()), testing::Ne(wgpu::ErrorType::NoError), matcher)); \
+    statement;                                                                                  \
+    instance.ProcessEvents();                                                                   \
+    FlushWire();                                                                                \
+    testing::Mock::VerifyAndClearExpectations(&mDeviceErrorCallback);                           \
+    do {                                                                                        \
     } while (0)
 
 #define ASSERT_DEVICE_ERROR_MSG(statement, matcher) \
@@ -337,8 +342,12 @@
     // Mock callbacks tracking errors and destruction. These are strict mocks because any errors or
     // device loss that aren't expected should result in test failures and not just some warnings
     // printed to stdout.
-    testing::StrictMock<testing::MockCallback<WGPUErrorCallback>> mDeviceErrorCallback;
-    testing::StrictMock<testing::MockCallback<WGPUDeviceLostCallbackNew>> mDeviceLostCallback;
+    testing::StrictMock<
+        testing::MockCppCallback<void (*)(const wgpu::Device&, wgpu::ErrorType, const char*)>>
+        mDeviceErrorCallback;
+    testing::StrictMock<testing::MockCppCallback<
+        void (*)(const wgpu::Device&, wgpu::DeviceLostReason, const char*)>>
+        mDeviceLostCallback;
 
     // Helper methods to implement the EXPECT_ macros
     std::ostringstream& AddBufferExpectation(const char* file,
diff --git a/src/dawn/tests/MockCallback.h b/src/dawn/tests/MockCallback.h
index 1638eb3..78936c0 100644
--- a/src/dawn/tests/MockCallback.h
+++ b/src/dawn/tests/MockCallback.h
@@ -124,10 +124,20 @@
 //   EXPECT_CALL(mock, Call(wgpu::PopErrorScopeStatus::Success, _, _));
 template <typename R, typename... Args>
 class MockCppCallback<R (*)(Args...)> : public ::testing::MockFunction<R(Args...)> {
+  private:
+    using TemplatedCallbackT = MockCppCallback<R (*)(Args...)>;
+
   public:
     auto Callback() {
         return [this](Args... args) -> R { return this->Call(args...); };
     }
+
+    // Returns the templated version of the callback with a static function. Useful when we cannot
+    // use a binding lambda. This must be used with TemplatedCallbackUserdata.
+    auto TemplatedCallback() {
+        return [](Args... args, TemplatedCallbackT* self) -> R { return self->Call(args...); };
+    }
+    TemplatedCallbackT* TemplatedCallbackUserdata() { return this; }
 };
 
 }  // namespace testing
diff --git a/src/dawn/tests/benchmarks/NullDeviceSetup.cpp b/src/dawn/tests/benchmarks/NullDeviceSetup.cpp
index e029b7a..72630fe 100644
--- a/src/dawn/tests/benchmarks/NullDeviceSetup.cpp
+++ b/src/dawn/tests/benchmarks/NullDeviceSetup.cpp
@@ -60,21 +60,19 @@
 
             // Create the device.
             wgpu::DeviceDescriptor desc = GetDeviceDescriptor();
-            desc.deviceLostCallbackInfo = {
-                nullptr, wgpu::CallbackMode::AllowSpontaneous,
-                [](WGPUDevice const*, WGPUDeviceLostReason reason, char const* message, void*) {
-                    if (reason == WGPUDeviceLostReason_Unknown) {
+            desc.SetDeviceLostCallback(
+                wgpu::CallbackMode::AllowSpontaneous,
+                [](const wgpu::Device&, wgpu::DeviceLostReason reason, char const* message) {
+                    if (reason == wgpu::DeviceLostReason::Unknown) {
                         dawn::ErrorLog() << message;
                         DAWN_UNREACHABLE();
                     }
-                },
-                this};
-            desc.uncapturedErrorCallbackInfo = {nullptr,
-                                                [](WGPUErrorType, char const* message, void*) {
-                                                    dawn::ErrorLog() << message;
-                                                    DAWN_UNREACHABLE();
-                                                },
-                                                this};
+                });
+            desc.SetUncapturedErrorCallback(
+                [](const wgpu::Device&, wgpu::ErrorType, const char* message) {
+                    dawn::ErrorLog() << message;
+                    DAWN_UNREACHABLE();
+                });
 
             adapter.RequestDevice(
                 &desc, wgpu::CallbackMode::AllowSpontaneous,
diff --git a/src/dawn/tests/end2end/DeviceLostTests.cpp b/src/dawn/tests/end2end/DeviceLostTests.cpp
index d6cfd24..8ff9d0f 100644
--- a/src/dawn/tests/end2end/DeviceLostTests.cpp
+++ b/src/dawn/tests/end2end/DeviceLostTests.cpp
@@ -52,13 +52,15 @@
 class DeviceLostTest : public DawnTest {
   protected:
     void SetUp() override {
-        DawnTest::SetUp();
         DAWN_TEST_UNSUPPORTED_IF(UsesWire());
+        DawnTest::SetUp();
     }
 
     void TearDown() override {
-        instance.ProcessEvents();  // Flush all callbacks.
-        DawnTest::TearDown();
+        if (!UsesWire()) {
+            instance.ProcessEvents();  // Flush all callbacks.
+            DawnTest::TearDown();
+        }
     }
 
     static void MapFailCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
@@ -75,7 +77,7 @@
     MockMapAsyncCallback mMapAsyncCb;
 };
 
-// Test that DeviceLostCallback is invoked when LostForTestimg is called
+// Test that DeviceLostCallback is invoked when LostForTesting is called
 TEST_P(DeviceLostTest, DeviceLostCallbackIsCalled) {
     LoseDeviceForTesting();
 }
diff --git a/src/dawn/tests/end2end/EventTests.cpp b/src/dawn/tests/end2end/EventTests.cpp
index 28e215b..fd11f86 100644
--- a/src/dawn/tests/end2end/EventTests.cpp
+++ b/src/dawn/tests/end2end/EventTests.cpp
@@ -152,7 +152,7 @@
 
     void LoseTestDevice() {
         EXPECT_CALL(mDeviceLostCallback,
-                    Call(testing::_, WGPUDeviceLostReason_Unknown, testing::_, testing::_))
+                    Call(CHandleIs(testDevice.Get()), wgpu::DeviceLostReason::Unknown, testing::_))
             .Times(1);
         testDevice.ForceLoss(wgpu::DeviceLostReason::Unknown, "Device lost for testing");
         testInstance.ProcessEvents();
diff --git a/src/dawn/tests/end2end/VideoViewsTests.cpp b/src/dawn/tests/end2end/VideoViewsTests.cpp
index 00a0c41..5011f05 100644
--- a/src/dawn/tests/end2end/VideoViewsTests.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests.cpp
@@ -1479,8 +1479,9 @@
 // Tests creating a texture with a multi-plane format.
 TEST_P(VideoViewsValidationTests, RenderAttachmentInvalid) {
     // "Invalid Texture" error is expected if failed to create the video texture.
-    EXPECT_CALL(mDeviceErrorCallback, Call(testing::Ne(WGPUErrorType_NoError),
-                                           testing::HasSubstr("Invalid Texture"), device.Get()))
+    EXPECT_CALL(mDeviceErrorCallback,
+                Call(CHandleIs(device.Get()), testing::Ne(wgpu::ErrorType::NoError),
+                     testing::HasSubstr("Invalid Texture")))
         .Times(testing::AnyNumber());
 
     // multi-planar formats are NOT allowed to be renderable by default and require
diff --git a/src/dawn/tests/unittests/native/mocks/DawnMockTest.cpp b/src/dawn/tests/unittests/native/mocks/DawnMockTest.cpp
index d4b6e14..fd50314 100644
--- a/src/dawn/tests/unittests/native/mocks/DawnMockTest.cpp
+++ b/src/dawn/tests/unittests/native/mocks/DawnMockTest.cpp
@@ -42,8 +42,21 @@
 }
 
 void DawnMockTest::DropDevice() {
+    if (device == nullptr) {
+        return;
+    }
+
+    // Since the device owns the instance in these tests, we need to explicitly verify that the
+    // instance has completed all work. To do this, we take an additional ref to the instance here
+    // and use it to process events until completion after dropping the device.
+    Ref<InstanceBase> instance = mDeviceMock->GetInstance();
+
     mDeviceMock = nullptr;
     device = nullptr;
+
+    do {
+    } while (instance->ProcessEvents());
+    instance = nullptr;
 }
 
 DawnMockTest::~DawnMockTest() {
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.cpp b/src/dawn/tests/unittests/validation/ValidationTest.cpp
index 7786b01..1258f4e 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.cpp
+++ b/src/dawn/tests/unittests/validation/ValidationTest.cpp
@@ -348,9 +348,33 @@
 
     // Initialize the device.
     wgpu::DeviceDescriptor deviceDescriptor = {};
-    deviceDescriptor.deviceLostCallbackInfo = {nullptr, wgpu::CallbackMode::AllowSpontaneous,
-                                               ValidationTest::OnDeviceLost, this};
-    deviceDescriptor.uncapturedErrorCallbackInfo = {nullptr, ValidationTest::OnDeviceError, this};
+    deviceDescriptor.SetDeviceLostCallback(
+        wgpu::CallbackMode::AllowSpontaneous,
+        [this](const wgpu::Device&, wgpu::DeviceLostReason reason, const char* message) {
+            if (mExpectDestruction) {
+                EXPECT_EQ(reason, wgpu::DeviceLostReason::Destroyed);
+                return;
+            }
+            ADD_FAILURE() << "Device lost during test: " << message;
+            DAWN_ASSERT(false);
+        });
+    deviceDescriptor.SetUncapturedErrorCallback(
+        [](const wgpu::Device&, wgpu::ErrorType type, const char* message, ValidationTest* self) {
+            DAWN_ASSERT(type != wgpu::ErrorType::NoError);
+
+            ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message;
+            ASSERT_FALSE(self->mError) << "Got two errors in expect block, first one is:\n"  //
+                                       << self->mDeviceErrorMessage                          //
+                                       << "\nsecond one is:\n"                               //
+                                       << message;
+
+            self->mDeviceErrorMessage = message;
+            if (self->mExpectError) {
+                ASSERT_THAT(message, self->mErrorMatcher);
+            }
+            self->mError = true;
+        },
+        this);
 
     // Set the required features for the device.
     auto requiredFeatures = GetRequiredFeatures();
@@ -368,37 +392,6 @@
     return false;
 }
 
-// static
-void ValidationTest::OnDeviceError(WGPUErrorType type, const char* message, void* userdata) {
-    DAWN_ASSERT(type != WGPUErrorType_NoError);
-    auto* self = static_cast<ValidationTest*>(userdata);
-
-    ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message;
-    ASSERT_FALSE(self->mError) << "Got two errors in expect block, first one is:\n"  //
-                               << self->mDeviceErrorMessage                          //
-                               << "\nsecond one is:\n"                               //
-                               << message;
-
-    self->mDeviceErrorMessage = message;
-    if (self->mExpectError) {
-        ASSERT_THAT(message, self->mErrorMatcher);
-    }
-    self->mError = true;
-}
-
-void ValidationTest::OnDeviceLost(WGPUDevice const* device,
-                                  WGPUDeviceLostReason reason,
-                                  const char* message,
-                                  void* userdata) {
-    auto* self = static_cast<ValidationTest*>(userdata);
-    if (self->mExpectDestruction) {
-        EXPECT_EQ(reason, WGPUDeviceLostReason_Destroyed);
-        return;
-    }
-    ADD_FAILURE() << "Device lost during test: " << message;
-    DAWN_ASSERT(false);
-}
-
 ValidationTest::PlaceholderRenderPass::PlaceholderRenderPass(const wgpu::Device& device)
     : attachmentFormat(wgpu::TextureFormat::RGBA8Unorm), width(400), height(400) {
     wgpu::TextureDescriptor descriptor;
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.h b/src/dawn/tests/unittests/validation/ValidationTest.h
index a5e5e20..08abf58 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.h
+++ b/src/dawn/tests/unittests/validation/ValidationTest.h
@@ -180,11 +180,6 @@
                const wgpu::InstanceDescriptor* wireDesc = nullptr);
 
     wgpu::Device RequestDeviceSync(const wgpu::DeviceDescriptor& deviceDesc);
-    static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata);
-    static void OnDeviceLost(WGPUDevice const* device,
-                             WGPUDeviceLostReason reason,
-                             const char* message,
-                             void* userdata);
 
     virtual bool UseCompatibilityMode() const;
 
diff --git a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
index afcfb03..7b51fbb 100644
--- a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
@@ -114,18 +114,15 @@
     });
 }
 
-static void DeviceLostCallback(WGPUDevice const* device,
-                               WGPUDeviceLostReason reason,
-                               const char* message,
-                               void* userdata) {}
+static void DeviceLostCallback(const wgpu::Device&,
+                               wgpu::DeviceLostReason reason,
+                               const char* message) {}
 
 // Test that the DeviceDescriptor is not allowed to pass a device lost callback from the client to
 // the server.
 TEST_P(WireAdapterTests, RequestDeviceAssertsOnLostCallbackPointer) {
-    int userdata = 1337;
     wgpu::DeviceDescriptor desc = {};
-    desc.deviceLostCallbackInfo.callback = DeviceLostCallback;
-    desc.deviceLostCallbackInfo.userdata = &userdata;
+    desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, DeviceLostCallback);
 
     AdapterRequestDevice(adapter, &desc);
 
@@ -134,10 +131,12 @@
             EXPECT_STREQ(apiDesc->label, desc.label);
 
             // The callback should not be passed through to the server, and it should be overridden.
-            ASSERT_NE(apiDesc->deviceLostCallbackInfo.callback, nullptr);
-            ASSERT_NE(apiDesc->deviceLostCallbackInfo.callback, &DeviceLostCallback);
-            ASSERT_NE(apiDesc->deviceLostCallbackInfo.userdata, nullptr);
-            ASSERT_NE(apiDesc->deviceLostCallbackInfo.userdata, &userdata);
+            WGPUDeviceDescriptor& inputDesc = *reinterpret_cast<WGPUDeviceDescriptor*>(&desc);
+            ASSERT_NE(apiDesc->deviceLostCallbackInfo2.callback,
+                      inputDesc.deviceLostCallbackInfo2.callback);
+            ASSERT_NE(apiDesc->deviceLostCallbackInfo2.callback, nullptr);
+            ASSERT_NE(apiDesc->deviceLostCallbackInfo2.userdata1, nullptr);
+            ASSERT_EQ(apiDesc->deviceLostCallbackInfo2.userdata2, nullptr);
 
             // Call the callback so the test doesn't wait indefinitely.
             api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
@@ -174,8 +173,6 @@
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _))
         .WillOnce(InvokeWithoutArgs([&] {
             // Set on device creation to forward callbacks to the client.
-            EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull()))
-                .Times(1);
             EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1);
 
             EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull()))
@@ -233,19 +230,8 @@
         FlushCallbacks();
     });
 
-    // Test that callbacks can propagate from server to client.
-    MockCallback<WGPUErrorCallback> errorCb;
-    device.SetUncapturedErrorCallback(errorCb.Callback(), errorCb.MakeUserdata(this));
-    api.CallDeviceSetUncapturedErrorCallbackCallback(apiDevice, WGPUErrorType_Validation,
-                                                     "Some error message");
-
-    EXPECT_CALL(errorCb, Call(WGPUErrorType_Validation, StrEq("Some error message"), this))
-        .Times(1);
-    FlushCallbacks();
-
     device = nullptr;
     // Cleared when the device is destroyed.
-    EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1);
     EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1);
     EXPECT_CALL(api, DeviceRelease(apiDevice));
 
@@ -348,8 +334,6 @@
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _))
         .WillOnce(InvokeWithoutArgs([&] {
             // Set on device creation to forward callbacks to the client.
-            EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull()))
-                .Times(1);
             EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1);
 
             EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull()))
@@ -387,7 +371,6 @@
 
     device = nullptr;
     // Cleared when the device is destroyed.
-    EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1);
     EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1);
     EXPECT_CALL(api, DeviceRelease(apiDevice));
     FlushClient();
diff --git a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
index 7b6ebe0..be8c670 100644
--- a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
@@ -82,7 +82,7 @@
 // Check that disconnecting the wire client calls the device lost callback exacty once.
 TEST_F(WireDisconnectTests, CallsDeviceLostCallback) {
     // Disconnect the wire client. We should receive device lost only once.
-    EXPECT_CALL(deviceLostCallback, Call(_, WGPUDeviceLostReason_InstanceDropped, _, this))
+    EXPECT_CALL(deviceLostCallback, Call(_, WGPUDeviceLostReason_InstanceDropped, _, _, this))
         .Times(Exactly(1));
     GetWireClient()->Disconnect();
     GetWireClient()->Disconnect();
@@ -96,7 +96,7 @@
 
     // Flush the device lost return command.
     EXPECT_CALL(deviceLostCallback,
-                Call(_, WGPUDeviceLostReason_Unknown, StrEq("some reason"), this))
+                Call(_, WGPUDeviceLostReason_Unknown, StrEq("some reason"), _, this))
         .Times(Exactly(1));
     FlushServer();
 
@@ -166,9 +166,6 @@
     // should be deleted first because it may free its reference to the default queue
     // on deletion.
     Sequence s1, s2, s3, s4, s5;
-    EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr))
-        .Times(1)
-        .InSequence(s1, s2);
     EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr))
         .Times(1)
         .InSequence(s1, s2);
diff --git a/src/dawn/tests/unittests/wire/WireQueueTests.cpp b/src/dawn/tests/unittests/wire/WireQueueTests.cpp
index 69142e8..ce56fc8 100644
--- a/src/dawn/tests/unittests/wire/WireQueueTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireQueueTests.cpp
@@ -174,7 +174,6 @@
     EXPECT_CALL(api, QueueRelease(apiQueue));
     EXPECT_CALL(api, DeviceRelease(apiDevice));
     // These set X callback methods are called before the device is released.
-    EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1);
     EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1);
     FlushClient();
 
@@ -195,7 +194,6 @@
 
     EXPECT_CALL(api, DeviceRelease(apiDevice));
     // These set X callback methods are called before the device is released.
-    EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1);
     EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1);
     FlushClient();
 
diff --git a/src/dawn/tests/unittests/wire/WireTest.cpp b/src/dawn/tests/unittests/wire/WireTest.cpp
index 8fe6855..c339d65 100644
--- a/src/dawn/tests/unittests/wire/WireTest.cpp
+++ b/src/dawn/tests/unittests/wire/WireTest.cpp
@@ -143,12 +143,13 @@
     // Create the device for testing.
     apiDevice = api.GetNewDevice();
     WGPUDeviceDescriptor deviceDesc = {};
-    deviceDesc.deviceLostCallbackInfo.mode = WGPUCallbackMode_WaitAnyOnly;
-    deviceDesc.deviceLostCallbackInfo.callback = deviceLostCallback.Callback();
-    deviceDesc.deviceLostCallbackInfo.userdata = deviceLostCallback.MakeUserdata(this);
+    deviceDesc.deviceLostCallbackInfo2 = {nullptr, WGPUCallbackMode_AllowSpontaneous,
+                                          deviceLostCallback.Callback(), nullptr,
+                                          deviceLostCallback.MakeUserdata(this)};
+    deviceDesc.uncapturedErrorCallbackInfo2 = {nullptr, uncapturedErrorCallback.Callback(), nullptr,
+                                               uncapturedErrorCallback.MakeUserdata(this)};
     EXPECT_CALL(deviceLostCallback, Call).Times(AtMost(1));
-    deviceDesc.uncapturedErrorCallbackInfo.callback = uncapturedErrorCallback.Callback();
-    deviceDesc.uncapturedErrorCallbackInfo.userdata = uncapturedErrorCallback.MakeUserdata(this);
+
     MockCallback<WGPURequestDeviceCallback2> deviceCb;
     wgpuAdapterRequestDevice2(adapter.Get(), &deviceDesc,
                               {nullptr, WGPUCallbackMode_AllowSpontaneous, deviceCb.Callback(),
@@ -156,16 +157,18 @@
     EXPECT_CALL(api, OnAdapterRequestDevice2(apiAdapter, NotNull(), _))
         .WillOnce(WithArg<1>([&](const WGPUDeviceDescriptor* desc) {
             // Set on device creation to forward callbacks to the client.
-            EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull()))
-                .Times(1);
             EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1);
 
             // The mock objects currently require us to manually set the callbacks because we
-            // are no longer explicitly calling SetDeviceLostCallback anymore.
+            // are no longer explicitly calling the setters anymore.
             ProcTableAsClass::Object* object =
                 reinterpret_cast<ProcTableAsClass::Object*>(apiDevice);
-            object->mDeviceLostCallback = desc->deviceLostCallbackInfo.callback;
-            object->mDeviceLostUserdata = desc->deviceLostCallbackInfo.userdata;
+            object->mDeviceLostCallback = desc->deviceLostCallbackInfo2.callback;
+            object->mDeviceLostUserdata1 = desc->deviceLostCallbackInfo2.userdata1;
+            object->mDeviceLostUserdata2 = desc->deviceLostCallbackInfo2.userdata2;
+            object->mUncapturedErrorCallback = desc->uncapturedErrorCallbackInfo2.callback;
+            object->mUncapturedErrorUserdata1 = desc->uncapturedErrorCallbackInfo2.userdata1;
+            object->mUncapturedErrorUserdata2 = desc->uncapturedErrorCallbackInfo2.userdata2;
 
             EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull()))
                 .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) {
@@ -215,8 +218,6 @@
     if (mWireServer && apiDevice) {
         // These are called on server destruction to clear the callbacks. They must not be
         // called after the server is destroyed.
-        EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr))
-            .Times(Exactly(1));
         EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(Exactly(1));
     }
     mC2sBuf->SetHandler(nullptr);
@@ -263,8 +264,6 @@
     if (mWireServer) {
         // These are called on server destruction to clear the callbacks. They must not be
         // called after the server is destroyed.
-        EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr))
-            .Times(Exactly(1));
         EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(Exactly(1));
     }
     mC2sBuf->SetHandler(nullptr);
diff --git a/src/dawn/tests/unittests/wire/WireTest.h b/src/dawn/tests/unittests/wire/WireTest.h
index c9c86a7..1680a64 100644
--- a/src/dawn/tests/unittests/wire/WireTest.h
+++ b/src/dawn/tests/unittests/wire/WireTest.h
@@ -150,8 +150,8 @@
 
     testing::StrictMock<MockProcTable> api;
 
-    testing::MockCallback<WGPUDeviceLostCallbackNew> deviceLostCallback;
-    testing::MockCallback<WGPUErrorCallback> uncapturedErrorCallback;
+    testing::MockCallback<WGPUDeviceLostCallback2> deviceLostCallback;
+    testing::MockCallback<WGPUUncapturedErrorCallback> uncapturedErrorCallback;
 
     WGPUInstance instance;
     WGPUInstance apiInstance;
diff --git a/src/dawn/wire/client/Device.cpp b/src/dawn/wire/client/Device.cpp
index 6d18a37..c7337f6 100644
--- a/src/dawn/wire/client/Device.cpp
+++ b/src/dawn/wire/client/Device.cpp
@@ -156,8 +156,44 @@
                             EventType::CreateRenderPipeline,
                             WGPUCreateRenderPipelineAsyncCallbackInfo2>;
 
-static constexpr WGPUUncapturedErrorCallbackInfo kEmptyUncapturedErrorCallbackInfo = {
-    nullptr, nullptr, nullptr};
+void LegacyDeviceLostCallback(WGPUDevice const*,
+                              WGPUDeviceLostReason reason,
+                              char const* message,
+                              void* callback,
+                              void* userdata) {
+    if (callback == nullptr) {
+        return;
+    }
+    auto cb = reinterpret_cast<WGPUDeviceLostCallback>(callback);
+    cb(reason, message, userdata);
+}
+
+void LegacyDeviceLostCallback2(WGPUDevice const* device,
+                               WGPUDeviceLostReason reason,
+                               char const* message,
+                               void* callback,
+                               void* userdata) {
+    if (callback == nullptr) {
+        return;
+    }
+    auto cb = reinterpret_cast<WGPUDeviceLostCallbackNew>(callback);
+    cb(device, reason, message, userdata);
+}
+
+void LegacyUncapturedErrorCallback(WGPUDevice const*,
+                                   WGPUErrorType type,
+                                   const char* message,
+                                   void* callback,
+                                   void* userdata) {
+    if (callback == nullptr) {
+        return;
+    }
+    auto cb = reinterpret_cast<WGPUErrorCallback>(callback);
+    cb(type, message, userdata);
+}
+
+static constexpr WGPUUncapturedErrorCallbackInfo2 kEmptyUncapturedErrorCallbackInfo = {
+    nullptr, nullptr, nullptr, nullptr};
 
 }  // namespace
 
@@ -165,10 +201,14 @@
   public:
     static constexpr EventType kType = EventType::DeviceLost;
 
-    DeviceLostEvent(const WGPUDeviceLostCallbackInfo& callbackInfo, Device* device)
+    DeviceLostEvent(const WGPUDeviceLostCallbackInfo2& callbackInfo, Device* device)
         : TrackedEvent(callbackInfo.mode), mDevice(device) {
         DAWN_ASSERT(device != nullptr);
         mDevice->AddRef();
+
+        mDevice->mDeviceLostInfo.callback = callbackInfo.callback;
+        mDevice->mDeviceLostInfo.userdata1 = callbackInfo.userdata1;
+        mDevice->mDeviceLostInfo.userdata2 = callbackInfo.userdata2;
     }
 
     ~DeviceLostEvent() override { mDevice.ExtractAsDangling()->Release(); }
@@ -191,14 +231,13 @@
             mMessage = "A valid external Instance reference no longer exists.";
         }
 
-        void* userdata = mDevice->mDeviceLostInfo.userdata.ExtractAsDangling();
-        if (mDevice->mDeviceLostInfo.oldCallback != nullptr) {
-            mDevice->mDeviceLostInfo.oldCallback(mReason, mMessage ? mMessage->c_str() : nullptr,
-                                                 userdata);
-        } else if (mDevice->mDeviceLostInfo.callback != nullptr) {
+        void* userdata1 = mDevice->mDeviceLostInfo.userdata1.ExtractAsDangling();
+        void* userdata2 = mDevice->mDeviceLostInfo.userdata2.ExtractAsDangling();
+
+        if (mDevice->mDeviceLostInfo.callback != nullptr) {
             auto device = mReason != WGPUDeviceLostReason_FailedCreation ? ToAPI(mDevice) : nullptr;
-            mDevice->mDeviceLostInfo.callback(&device, mReason,
-                                              mMessage ? mMessage->c_str() : nullptr, userdata);
+            mDevice->mDeviceLostInfo.callback(
+                &device, mReason, mMessage ? mMessage->c_str() : nullptr, userdata1, userdata2);
         }
         mDevice->mUncapturedErrorCallbackInfo = kEmptyUncapturedErrorCallbackInfo;
     }
@@ -217,9 +256,9 @@
                const WGPUDeviceDescriptor* descriptor)
     : ObjectWithEventsBase(params, eventManagerHandle), mIsAlive(std::make_shared<bool>(true)) {
 #if defined(DAWN_ENABLE_ASSERTS)
-    static constexpr WGPUDeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = {
+    static constexpr WGPUDeviceLostCallbackInfo2 kDefaultDeviceLostCallbackInfo = {
         nullptr, WGPUCallbackMode_AllowSpontaneous,
-        [](WGPUDevice const*, WGPUDeviceLostReason, char const*, void*) {
+        [](WGPUDevice const*, WGPUDeviceLostReason, char const*, void*, void*) {
             static bool calledOnce = false;
             if (!calledOnce) {
                 calledOnce = true;
@@ -228,10 +267,10 @@
                                       "and suppress this message, set the callback explicitly.";
             }
         },
-        nullptr};
-    static constexpr WGPUUncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo = {
+        nullptr, nullptr};
+    static constexpr WGPUUncapturedErrorCallbackInfo2 kDefaultUncapturedErrorCallbackInfo = {
         nullptr,
-        [](WGPUErrorType, char const*, void*) {
+        [](WGPUDevice const*, WGPUErrorType, char const*, void*, void*) {
             static bool calledOnce = false;
             if (!calledOnce) {
                 calledOnce = true;
@@ -240,37 +279,42 @@
                                       "and suppress this message, set the callback explicitly.";
             }
         },
-        nullptr};
+        nullptr, nullptr};
 #else
-    static constexpr WGPUDeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = {
-        nullptr, WGPUCallbackMode_AllowSpontaneous, nullptr, nullptr};
-    static constexpr WGPUUncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo =
+    static constexpr WGPUDeviceLostCallbackInfo2 kDefaultDeviceLostCallbackInfo = {
+        nullptr, WGPUCallbackMode_AllowSpontaneous, nullptr, nullptr, nullptr};
+    static constexpr WGPUUncapturedErrorCallbackInfo2 kDefaultUncapturedErrorCallbackInfo =
         kEmptyUncapturedErrorCallbackInfo;
 #endif  // DAWN_ENABLE_ASSERTS
 
-    WGPUDeviceLostCallbackInfo deviceLostCallbackInfo = kDefaultDeviceLostCallbackInfo;
+    WGPUDeviceLostCallbackInfo2 deviceLostCallbackInfo = kDefaultDeviceLostCallbackInfo;
     if (descriptor != nullptr) {
-        if (descriptor->deviceLostCallbackInfo.callback != nullptr) {
-            deviceLostCallbackInfo = descriptor->deviceLostCallbackInfo;
-            if (deviceLostCallbackInfo.mode == WGPUCallbackMode_WaitAnyOnly) {
-                // TODO(dawn:2458) Currently we default the callback mode to Spontaneous if not
-                // passed for backwards compatibility. We should add warning logging for it though
-                // when available. Update this when we have WGPUCallbackMode_Undefined.
-                deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowSpontaneous;
-            }
-            mDeviceLostInfo.callback = deviceLostCallbackInfo.callback;
-            mDeviceLostInfo.userdata = deviceLostCallbackInfo.userdata;
+        if (descriptor->deviceLostCallbackInfo2.callback != nullptr) {
+            deviceLostCallbackInfo = descriptor->deviceLostCallbackInfo2;
+        } else if (descriptor->deviceLostCallbackInfo.callback != nullptr) {
+            auto& callbackInfo = descriptor->deviceLostCallbackInfo;
+            deviceLostCallbackInfo = {
+                callbackInfo.nextInChain, callbackInfo.mode, &LegacyDeviceLostCallback2,
+                reinterpret_cast<void*>(callbackInfo.callback), callbackInfo.userdata};
         } else if (descriptor->deviceLostCallback != nullptr) {
-            deviceLostCallbackInfo = {nullptr, WGPUCallbackMode_AllowSpontaneous, nullptr, nullptr};
-            mDeviceLostInfo.oldCallback = descriptor->deviceLostCallback;
-            mDeviceLostInfo.userdata = descriptor->deviceLostUserdata;
+            deviceLostCallbackInfo = {nullptr, WGPUCallbackMode_AllowSpontaneous,
+                                      &LegacyDeviceLostCallback,
+                                      reinterpret_cast<void*>(descriptor->deviceLostCallback),
+                                      descriptor->deviceLostUserdata};
         }
     }
     mDeviceLostInfo.event = std::make_unique<DeviceLostEvent>(deviceLostCallbackInfo, this);
 
     mUncapturedErrorCallbackInfo = kDefaultUncapturedErrorCallbackInfo;
-    if (descriptor && descriptor->uncapturedErrorCallbackInfo.callback != nullptr) {
-        mUncapturedErrorCallbackInfo = descriptor->uncapturedErrorCallbackInfo;
+    if (descriptor != nullptr) {
+        if (descriptor->uncapturedErrorCallbackInfo2.callback != nullptr) {
+            mUncapturedErrorCallbackInfo = descriptor->uncapturedErrorCallbackInfo2;
+        } else if (descriptor->uncapturedErrorCallbackInfo.callback != nullptr) {
+            auto& callbackInfo = descriptor->uncapturedErrorCallbackInfo;
+            mUncapturedErrorCallbackInfo = {
+                callbackInfo.nextInChain, &LegacyUncapturedErrorCallback,
+                reinterpret_cast<void*>(callbackInfo.callback), callbackInfo.userdata};
+        }
     }
 }
 
@@ -317,8 +361,10 @@
 
 void Device::HandleError(WGPUErrorType errorType, const char* message) {
     if (mUncapturedErrorCallbackInfo.callback) {
-        mUncapturedErrorCallbackInfo.callback(errorType, message,
-                                              mUncapturedErrorCallbackInfo.userdata);
+        auto device = ToAPI(this);
+        mUncapturedErrorCallbackInfo.callback(&device, errorType, message,
+                                              mUncapturedErrorCallbackInfo.userdata1,
+                                              mUncapturedErrorCallbackInfo.userdata2);
     }
 }
 
@@ -355,7 +401,8 @@
 
 void Device::SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata) {
     if (mDeviceLostInfo.futureID != kNullFutureID) {
-        mUncapturedErrorCallbackInfo = {nullptr, errorCallback, errorUserdata};
+        mUncapturedErrorCallbackInfo = {nullptr, &LegacyUncapturedErrorCallback,
+                                        reinterpret_cast<void*>(errorCallback), errorUserdata};
     }
 }
 
@@ -366,8 +413,9 @@
 
 void Device::SetDeviceLostCallback(WGPUDeviceLostCallback callback, void* userdata) {
     if (mDeviceLostInfo.futureID != kNullFutureID) {
-        mDeviceLostInfo.oldCallback = callback;
-        mDeviceLostInfo.userdata = userdata;
+        mDeviceLostInfo.callback = &LegacyDeviceLostCallback;
+        mDeviceLostInfo.userdata1 = reinterpret_cast<void*>(callback);
+        mDeviceLostInfo.userdata2 = userdata;
     }
 }
 
diff --git a/src/dawn/wire/client/Device.h b/src/dawn/wire/client/Device.h
index 244d4f8..2a2d88e 100644
--- a/src/dawn/wire/client/Device.h
+++ b/src/dawn/wire/client/Device.h
@@ -113,13 +113,13 @@
     struct DeviceLostInfo {
         FutureID futureID = kNullFutureID;
         std::unique_ptr<TrackedEvent> event = nullptr;
-        WGPUDeviceLostCallbackNew callback = nullptr;
-        WGPUDeviceLostCallback oldCallback = nullptr;
-        raw_ptr<void> userdata = nullptr;
+        WGPUDeviceLostCallback2 callback = nullptr;
+        raw_ptr<void> userdata1 = nullptr;
+        raw_ptr<void> userdata2 = nullptr;
     };
     DeviceLostInfo mDeviceLostInfo;
 
-    WGPUUncapturedErrorCallbackInfo mUncapturedErrorCallbackInfo;
+    WGPUUncapturedErrorCallbackInfo2 mUncapturedErrorCallbackInfo;
     WGPULoggingCallback mLoggingCallback = nullptr;
     raw_ptr<void> mLoggingUserdata = nullptr;
 
diff --git a/src/dawn/wire/server/Server.cpp b/src/dawn/wire/server/Server.cpp
index b363c19..bc6b6c2 100644
--- a/src/dawn/wire/server/Server.cpp
+++ b/src/dawn/wire/server/Server.cpp
@@ -170,15 +170,8 @@
     // Also, the device is special-cased in Server::DoDestroyObject to call
     // ClearDeviceCallbacks. This ensures that callbacks will not fire after |deviceObject|
     // is freed.
-    mProcs.deviceSetUncapturedErrorCallback(
-        device->handle,
-        [](WGPUErrorType type, const char* message, void* userdata) {
-            DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
-            info->server->OnUncapturedError(info->self, type, message);
-        },
-        device->info.get());
-    // Set callback to post warning and other infomation to client.
-    // Almost the same with UncapturedError.
+
+    // Set callback to post warning and other information to client.
     mProcs.deviceSetLoggingCallback(
         device->handle,
         [](WGPULoggingType type, const char* message, void* userdata) {
@@ -189,9 +182,7 @@
 }
 
 void Server::ClearDeviceCallbacks(WGPUDevice device) {
-    // Un-set the error and logging callbacks since we cannot forward them
-    // after the server has been destroyed.
-    mProcs.deviceSetUncapturedErrorCallback(device, nullptr, nullptr);
+    // Un-set the logging callback since we cannot forward them after the server has been destroyed.
     mProcs.deviceSetLoggingCallback(device, nullptr, nullptr);
 }
 
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp
index b608c9d..6f9cab2 100644
--- a/src/dawn/wire/server/ServerAdapter.cpp
+++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -56,9 +56,16 @@
     deviceLostUserdata->future = deviceLostFuture;
 
     WGPUDeviceDescriptor desc = *descriptor;
-    desc.deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowProcessEvents;
-    desc.deviceLostCallbackInfo.callback = ForwardToServer<&Server::OnDeviceLost>;
-    desc.deviceLostCallbackInfo.userdata = deviceLostUserdata.release();
+    desc.deviceLostCallbackInfo2 = {nullptr, WGPUCallbackMode_AllowProcessEvents,
+                                    ForwardToServer2<&Server::OnDeviceLost>,
+                                    deviceLostUserdata.release(), nullptr};
+    desc.uncapturedErrorCallbackInfo2 = {
+        nullptr,
+        [](WGPUDevice const*, WGPUErrorType type, const char* message, void*, void* userdata) {
+            DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
+            info->server->OnUncapturedError(info->self, type, message);
+        },
+        nullptr, device->info.get()};
 
     if (userdataCount == 1) {
         mProcs.adapterRequestDevice(adapter->handle, &desc,