Reland "[dawn][emscripten] Add message argument to QueueWorkDoneCallback"

This reverts commit 3df36f75b735ee1286ddc7192bb780a31fe23622.

Reason for revert: Breaking change handled in ml_drift

Bug: 414868699
Original change's description:
> Revert "[dawn][emscripten] Add message argument to QueueWorkDoneCallback"
>
> This reverts commit f6d857eadd069adbc782bda87d8fa0e34de2c687.
>
> Reason for revert: blocking Dawn -> chromium roll
>
> Bug: 414868699
> Original change's description:
> > [dawn][emscripten] Add message argument to QueueWorkDoneCallback
> >
> > Following upstream webgpu.h change:
> > https://github.com/webgpu-native/webgpu-headers/pull/528
> >
> > Bug: 414868699
> > Change-Id: Ieaf72cd4a295d60659d0cdc20f1e0f95ff054684
> > Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/243697
> > Reviewed-by: Loko Kung <lokokung@google.com>
> > Commit-Queue: Kai Ninomiya <kainino@chromium.org>
>
> TBR=kainino@chromium.org,dawn-scoped@luci-project-accounts.iam.gserviceaccount.com,lokokung@google.com
>
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: 414868699
> Change-Id: I88c130ea80884d9e2eada2ce274b276d13f52553
> Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/244594
> Reviewed-by: Shrek Shao <shrekshao@google.com>
> Reviewed-by: James Price <jrprice@google.com>
> Reviewed-by: Loko Kung <lokokung@google.com>
> Commit-Queue: James Price <jrprice@google.com>
> Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>

# Not skipping CQ checks because this is a reland.

Bug: 414868699
Change-Id: I4ebfe09041dbad9f0af8585dbdcc1612e4951212
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/244654
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
diff --git a/generator/templates/api.h b/generator/templates/api.h
index e84183c..ae4b153 100644
--- a/generator/templates/api.h
+++ b/generator/templates/api.h
@@ -43,6 +43,7 @@
 #define WGPU_BREAKING_CHANGE_STRING_VIEW_LABELS
 #define WGPU_BREAKING_CHANGE_STRING_VIEW_OUTPUT_STRUCTS
 #define WGPU_BREAKING_CHANGE_STRING_VIEW_CALLBACKS
+#define WGPU_BREAKING_CHANGE_QUEUE_WORK_DONE_CALLBACK_MESSAGE
 {% macro render_c_default_value(member) -%}
     {%- if member.annotation in ["*", "const*", "const*const*"] -%}
         //* Pointer types should always default to NULL.
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index 6959658..e67b519 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -2815,7 +2815,8 @@
     "queue work done callback": {
         "category": "callback function",
         "args": [
-            {"name": "status", "type": "queue work done status"}
+            {"name": "status", "type": "queue work done status"},
+            {"name": "message", "type": "string view"}
         ]
     },
     "queue work done callback info": {
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 1706cb2..2fb9007 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -189,7 +189,8 @@
         "queue work done callback": [
             { "name": "event manager", "type": "ObjectHandle" },
             { "name": "future", "type": "future" },
-            { "name": "status", "type": "queue work done status" }
+            { "name": "status", "type": "queue work done status" },
+            { "name": "message", "type": "string view" }
         ],
         "shader module get compilation info callback": [
             { "name": "event manager", "type": "ObjectHandle" },
diff --git a/src/dawn/native/Queue.cpp b/src/dawn/native/Queue.cpp
index 393154a..acd0f4d 100644
--- a/src/dawn/native/Queue.cpp
+++ b/src/dawn/native/Queue.cpp
@@ -38,6 +38,7 @@
 
 #include "dawn/common/Constants.h"
 #include "dawn/common/FutureUtils.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/common/ityp_span.h"
 #include "dawn/native/BlitBufferToDepthStencil.h"
 #include "dawn/native/Buffer.h"
@@ -178,6 +179,7 @@
     struct WorkDoneEvent final : public EventManager::TrackedEvent {
         std::optional<WGPUQueueWorkDoneStatus> mEarlyStatus;
         WGPUQueueWorkDoneCallback mCallback;
+        std::string mMessage;
         raw_ptr<void> mUserdata1;
         raw_ptr<void> mUserdata2;
 
@@ -213,7 +215,8 @@
                 status = mEarlyStatus.value();
             }
 
-            mCallback(status, mUserdata1.ExtractAsDangling(), mUserdata2.ExtractAsDangling());
+            mCallback(status, ToOutputStringView(mMessage), mUserdata1.ExtractAsDangling(),
+                      mUserdata2.ExtractAsDangling());
         }
     };
 
diff --git a/src/dawn/node/binding/GPUQueue.cpp b/src/dawn/node/binding/GPUQueue.cpp
index 4cd3e92..f7da83d 100644
--- a/src/dawn/node/binding/GPUQueue.cpp
+++ b/src/dawn/node/binding/GPUQueue.cpp
@@ -63,14 +63,14 @@
     auto ctx = std::make_unique<AsyncContext<void>>(env, PROMISE_INFO, async_);
     auto promise = ctx->promise;
 
-    queue_.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
-                               [ctx = std::move(ctx)](wgpu::QueueWorkDoneStatus status) {
-                                   if (status != wgpu::QueueWorkDoneStatus::Success) {
-                                       Napi::Error::New(ctx->env, "onSubmittedWorkDone() failed")
-                                           .ThrowAsJavaScriptException();
-                                   }
-                                   ctx->promise.Resolve();
-                               });
+    queue_.OnSubmittedWorkDone(
+        wgpu::CallbackMode::AllowProcessEvents,
+        [ctx = std::move(ctx)](wgpu::QueueWorkDoneStatus status, wgpu::StringView message) {
+            if (status != wgpu::QueueWorkDoneStatus::Success) {
+                Napi::Error::New(ctx->env, std::string(message)).ThrowAsJavaScriptException();
+            }
+            ctx->promise.Resolve();
+        });
 
     return promise;
 }
diff --git a/src/dawn/tests/end2end/AllocatorMemoryInstrumentationTests.cpp b/src/dawn/tests/end2end/AllocatorMemoryInstrumentationTests.cpp
index d7700e6..792caac 100644
--- a/src/dawn/tests/end2end/AllocatorMemoryInstrumentationTests.cpp
+++ b/src/dawn/tests/end2end/AllocatorMemoryInstrumentationTests.cpp
@@ -67,8 +67,8 @@
     device.GetQueue().Submit(0, nullptr);
     // Use Futures WaitAny to wait for the queue to update serial.
     wgpu::FutureWaitInfo waitInfo{};
-    waitInfo.future = device.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
-                                                            [](wgpu::QueueWorkDoneStatus) {});
+    waitInfo.future = device.GetQueue().OnSubmittedWorkDone(
+        wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus, wgpu::StringView) {});
     const auto& instance = device.GetAdapter().GetInstance();
     auto status =
         instance.WaitAny(1, &waitInfo, /*timeoutNS=*/std::numeric_limits<uint64_t>::max());
diff --git a/src/dawn/tests/end2end/BufferTests.cpp b/src/dawn/tests/end2end/BufferTests.cpp
index 34342f0..862e228 100644
--- a/src/dawn/tests/end2end/BufferTests.cpp
+++ b/src/dawn/tests/end2end/BufferTests.cpp
@@ -710,7 +710,7 @@
     // 1. submission without using buffer.
     SubmitCommandBuffer({});
     wgpu::Future f1 = queue.OnSubmittedWorkDone(
-        GetParam().mFutureCallbackMode, [&](wgpu::QueueWorkDoneStatus status) {
+        GetParam().mFutureCallbackMode, [&](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
             ASSERT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
             done[0] = true;
 
@@ -761,7 +761,7 @@
 
     // 2. Wait for command completion.
     wgpu::Future f2 = queue.OnSubmittedWorkDone(
-        GetParam().mFutureCallbackMode, [&](wgpu::QueueWorkDoneStatus status) {
+        GetParam().mFutureCallbackMode, [&](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
             ASSERT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
             done[1] = true;
 
@@ -785,7 +785,7 @@
     // 1. submission without using buffer.
     SubmitCommandBuffer({});
     wgpu::Future f1 = queue.OnSubmittedWorkDone(
-        GetParam().mFutureCallbackMode, [&](wgpu::QueueWorkDoneStatus status) {
+        GetParam().mFutureCallbackMode, [&](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
             ASSERT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
             done[0] = true;
 
diff --git a/src/dawn/tests/end2end/CopyTests.cpp b/src/dawn/tests/end2end/CopyTests.cpp
index 5f78341..6aa55b5 100644
--- a/src/dawn/tests/end2end/CopyTests.cpp
+++ b/src/dawn/tests/end2end/CopyTests.cpp
@@ -3515,11 +3515,12 @@
 
     // Ensure the underlying ID3D12Resource of bigBuffer is deleted.
     bool submittedWorkDone = false;
-    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
-                              [&submittedWorkDone](wgpu::QueueWorkDoneStatus status) {
-                                  EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
-                                  submittedWorkDone = true;
-                              });
+    queue.OnSubmittedWorkDone(
+        wgpu::CallbackMode::AllowProcessEvents,
+        [&submittedWorkDone](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
+            EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
+            submittedWorkDone = true;
+        });
     while (!submittedWorkDone) {
         WaitABit();
     }
@@ -3789,11 +3790,12 @@
 
     void EnsureSubmittedWorkDone() {
         bool submittedWorkDone = false;
-        queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
-                                  [&submittedWorkDone](wgpu::QueueWorkDoneStatus status) {
-                                      EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
-                                      submittedWorkDone = true;
-                                  });
+        queue.OnSubmittedWorkDone(
+            wgpu::CallbackMode::AllowProcessEvents,
+            [&submittedWorkDone](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
+                EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
+                submittedWorkDone = true;
+            });
         while (!submittedWorkDone) {
             WaitABit();
         }
diff --git a/src/dawn/tests/end2end/DestroyTests.cpp b/src/dawn/tests/end2end/DestroyTests.cpp
index 9b48b85c..d1911ba 100644
--- a/src/dawn/tests/end2end/DestroyTests.cpp
+++ b/src/dawn/tests/end2end/DestroyTests.cpp
@@ -258,7 +258,7 @@
 
     wgpu::Queue queue = device.GetQueue();
     queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
-                              [](wgpu::QueueWorkDoneStatus status) {
+                              [](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
                                   EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
                               });
 }
diff --git a/src/dawn/tests/end2end/DeviceLifetimeTests.cpp b/src/dawn/tests/end2end/DeviceLifetimeTests.cpp
index 7b32557..2d1f3dd 100644
--- a/src/dawn/tests/end2end/DeviceLifetimeTests.cpp
+++ b/src/dawn/tests/end2end/DeviceLifetimeTests.cpp
@@ -60,7 +60,7 @@
 
     // Ask for an onSubmittedWorkDone callback and drop the device.
     queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
-                              [](wgpu::QueueWorkDoneStatus status) {
+                              [](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
                                   EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
                               });
 
@@ -76,7 +76,7 @@
 
     // Ask for an onSubmittedWorkDone callback and drop the device inside the callback.
     queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
-                              [this](wgpu::QueueWorkDoneStatus status) {
+                              [this](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
                                   EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
                                   this->device = nullptr;
                               });
diff --git a/src/dawn/tests/end2end/DeviceLostTests.cpp b/src/dawn/tests/end2end/DeviceLostTests.cpp
index 301d73e..068b80c 100644
--- a/src/dawn/tests/end2end/DeviceLostTests.cpp
+++ b/src/dawn/tests/end2end/DeviceLostTests.cpp
@@ -32,6 +32,7 @@
 #include "dawn/native/DawnNative.h"
 #include "dawn/tests/DawnTest.h"
 #include "dawn/tests/MockCallback.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/utils/ComboRenderPipelineDescriptor.h"
 #include "dawn/utils/WGPUHelpers.h"
 #include "gmock/gmock.h"
@@ -40,6 +41,7 @@
 namespace {
 
 using testing::_;
+using testing::EmptySizedString;
 using testing::Exactly;
 using testing::HasSubstr;
 using testing::MockCppCallback;
@@ -492,7 +494,7 @@
     LoseDeviceForTesting();
 
     // Callback should have success status
-    EXPECT_CALL(mWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success));
+    EXPECT_CALL(mWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success, EmptySizedString()));
     queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents, mWorkDoneCb.Callback());
     WaitABit();
 }
@@ -500,7 +502,7 @@
 // Test QueueOnSubmittedWorkDone when the device is lost after calling OnSubmittedWorkDone
 TEST_P(DeviceLostTest, QueueOnSubmittedWorkDoneBeforeLossFails) {
     // Callback should have success status
-    EXPECT_CALL(mWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success));
+    EXPECT_CALL(mWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success, EmptySizedString()));
     queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents, mWorkDoneCb.Callback());
 
     LoseDeviceForTesting();
diff --git a/src/dawn/tests/end2end/EventTests.cpp b/src/dawn/tests/end2end/EventTests.cpp
index b9aa6e7..53831fc 100644
--- a/src/dawn/tests/end2end/EventTests.cpp
+++ b/src/dawn/tests/end2end/EventTests.cpp
@@ -202,7 +202,8 @@
 
     wgpu::Future OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus expectedStatus) {
         return testQueue.OnSubmittedWorkDone(
-            GetCallbackMode(), [this, expectedStatus](wgpu::QueueWorkDoneStatus status) {
+            GetCallbackMode(),
+            [this, expectedStatus](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
                 mCallbacksCompletedCount++;
                 ASSERT_EQ(status, expectedStatus);
             });
@@ -413,8 +414,9 @@
     testInstance = nullptr;  // Drop the last external ref to the instance.
 
     wgpu::QueueWorkDoneStatus status = kStatusUninitialized;
-    testQueue.OnSubmittedWorkDone(GetCallbackMode(),
-                                  [&status](wgpu::QueueWorkDoneStatus result) { status = result; });
+    testQueue.OnSubmittedWorkDone(
+        GetCallbackMode(),
+        [&status](wgpu::QueueWorkDoneStatus result, wgpu::StringView) { status = result; });
 
     ASSERT_EQ(status, wgpu::QueueWorkDoneStatus::CallbackCancelled);
 }
@@ -428,8 +430,9 @@
     UseSecondInstance();
 
     wgpu::QueueWorkDoneStatus status = kStatusUninitialized;
-    testQueue.OnSubmittedWorkDone(GetCallbackMode(),
-                                  [&status](wgpu::QueueWorkDoneStatus result) { status = result; });
+    testQueue.OnSubmittedWorkDone(
+        GetCallbackMode(),
+        [&status](wgpu::QueueWorkDoneStatus result, wgpu::StringView) { status = result; });
 
     ASSERT_EQ(status, kStatusUninitialized);
     testInstance = nullptr;  // Drop the last external ref to the instance.
@@ -496,10 +499,11 @@
     }
 
     for (uint64_t timeout : {uint64_t(1), uint64_t(0), UINT64_MAX}) {
-        wgpu::WaitStatus status = instance2.WaitAny(
-            device2.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
-                                                   [](wgpu::QueueWorkDoneStatus) {}),
-            timeout);
+        wgpu::WaitStatus status =
+            instance2.WaitAny(device2.GetQueue().OnSubmittedWorkDone(
+                                  wgpu::CallbackMode::WaitAnyOnly,
+                                  [](wgpu::QueueWorkDoneStatus, wgpu::StringView) {}),
+                              timeout);
         if (timeout == 0) {
             ASSERT_TRUE(status == wgpu::WaitStatus::Success ||
                         status == wgpu::WaitStatus::TimedOut);
@@ -533,8 +537,9 @@
         for (size_t count : {kTimedWaitAnyMaxCountDefault, kTimedWaitAnyMaxCountDefault + 1}) {
             std::vector<wgpu::FutureWaitInfo> infos;
             for (size_t i = 0; i < count; ++i) {
-                infos.push_back({queue2.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
-                                                            [](wgpu::QueueWorkDoneStatus) {})});
+                infos.push_back({queue2.OnSubmittedWorkDone(
+                    wgpu::CallbackMode::WaitAnyOnly,
+                    [](wgpu::QueueWorkDoneStatus, wgpu::StringView) {})});
             }
             wgpu::WaitStatus status = instance2.WaitAny(infos.size(), infos.data(), timeout);
             if (timeout == 0) {
@@ -579,9 +584,9 @@
     for (uint64_t timeout : {uint64_t(0), uint64_t(1)}) {
         std::vector<wgpu::FutureWaitInfo> infos{{
             {queue2.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
-                                        [](wgpu::QueueWorkDoneStatus) {})},
+                                        [](wgpu::QueueWorkDoneStatus, wgpu::StringView) {})},
             {queue3.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
-                                        [](wgpu::QueueWorkDoneStatus) {})},
+                                        [](wgpu::QueueWorkDoneStatus, wgpu::StringView) {})},
         }};
         wgpu::WaitStatus status = instance2.WaitAny(infos.size(), infos.data(), timeout);
         if (timeout == 0) {
@@ -653,7 +658,7 @@
     for (auto& future : futures) {
         HeavySubmit();
         future = queue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
-                                           [](wgpu::QueueWorkDoneStatus) {});
+                                           [](wgpu::QueueWorkDoneStatus, wgpu::StringView) {});
     }
 
     for (const auto& future : futures) {
@@ -680,7 +685,7 @@
 TEST_P(FutureTests, MixedSourcePolling) {
     // OnSubmittedWorkDone is implemented via a queue serial.
     device.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
-                                          [](wgpu::QueueWorkDoneStatus) {});
+                                          [](wgpu::QueueWorkDoneStatus, wgpu::StringView) {});
 
     // PopErrorScope is implemented via a signal.
     device.PushErrorScope(wgpu::ErrorFilter::Validation);
diff --git a/src/dawn/tests/end2end/QueueTimelineTests.cpp b/src/dawn/tests/end2end/QueueTimelineTests.cpp
index d7204e4..13acb9f 100644
--- a/src/dawn/tests/end2end/QueueTimelineTests.cpp
+++ b/src/dawn/tests/end2end/QueueTimelineTests.cpp
@@ -29,17 +29,19 @@
 
 #include "dawn/tests/DawnTest.h"
 #include "dawn/tests/MockCallback.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "gmock/gmock.h"
 
 namespace dawn {
 namespace {
 
 using testing::_;
+using testing::EmptySizedString;
 using testing::InSequence;
 using testing::MockCppCallback;
 
-using MockMapAsyncCallback = MockCppCallback<void (*)(wgpu::MapAsyncStatus, wgpu::StringView)>;
-using MockQueueWorkDoneCallback = MockCppCallback<void (*)(wgpu::QueueWorkDoneStatus)>;
+using MockMapAsyncCallback = MockCppCallback<wgpu::BufferMapCallback<void>*>;
+using MockQueueWorkDoneCallback = MockCppCallback<wgpu::QueueWorkDoneCallback<void>*>;
 
 class QueueTimelineTests : public DawnTest {
   protected:
@@ -63,8 +65,9 @@
     MockQueueWorkDoneCallback mockQueueWorkDoneCb;
 
     InSequence sequence;
-    EXPECT_CALL(mockMapAsyncCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
-    EXPECT_CALL(mockQueueWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success)).Times(1);
+    EXPECT_CALL(mockMapAsyncCb, Call(wgpu::MapAsyncStatus::Success, EmptySizedString())).Times(1);
+    EXPECT_CALL(mockQueueWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success, EmptySizedString()))
+        .Times(1);
 
     mMapReadBuffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
                             wgpu::CallbackMode::AllowProcessEvents, mockMapAsyncCb.Callback());
@@ -81,8 +84,10 @@
     MockQueueWorkDoneCallback mockQueueWorkDoneCb2;
 
     InSequence sequence;
-    EXPECT_CALL(mockQueueWorkDoneCb1, Call(wgpu::QueueWorkDoneStatus::Success)).Times(1);
-    EXPECT_CALL(mockQueueWorkDoneCb2, Call(wgpu::QueueWorkDoneStatus::Success)).Times(1);
+    EXPECT_CALL(mockQueueWorkDoneCb1, Call(wgpu::QueueWorkDoneStatus::Success, EmptySizedString()))
+        .Times(1);
+    EXPECT_CALL(mockQueueWorkDoneCb2, Call(wgpu::QueueWorkDoneStatus::Success, EmptySizedString()))
+        .Times(1);
 
     queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
                               mockQueueWorkDoneCb1.Callback());
diff --git a/src/dawn/tests/perf_tests/DawnPerfTest.cpp b/src/dawn/tests/perf_tests/DawnPerfTest.cpp
index 5e93bfe..5444ae6 100644
--- a/src/dawn/tests/perf_tests/DawnPerfTest.cpp
+++ b/src/dawn/tests/perf_tests/DawnPerfTest.cpp
@@ -266,7 +266,9 @@
         submittedIterations++;
         mTest->queue.OnSubmittedWorkDone(
             wgpu::CallbackMode::AllowProcessEvents,
-            [&finishedIterations](wgpu::QueueWorkDoneStatus) { finishedIterations++; });
+            [&finishedIterations](wgpu::QueueWorkDoneStatus, wgpu::StringView) {
+                finishedIterations++;
+            });
 
         if (mRunning) {
             ++mNumStepsPerformed;
diff --git a/src/dawn/tests/perf_tests/UniformBufferUpdatePerf.cpp b/src/dawn/tests/perf_tests/UniformBufferUpdatePerf.cpp
index 408880e..2789497 100644
--- a/src/dawn/tests/perf_tests/UniformBufferUpdatePerf.cpp
+++ b/src/dawn/tests/perf_tests/UniformBufferUpdatePerf.cpp
@@ -379,7 +379,7 @@
                 } else {
                     queue.OnSubmittedWorkDone(
                         wgpu::CallbackMode::AllowProcessEvents,
-                        [this, uniformBuffer](wgpu::QueueWorkDoneStatus status) {
+                        [this, uniformBuffer](wgpu::QueueWorkDoneStatus status, wgpu::StringView) {
                             if (status == wgpu::QueueWorkDoneStatus::Success) {
                                 this->ReturnUniformBuffer(uniformBuffer);
                             }
diff --git a/src/dawn/tests/unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp b/src/dawn/tests/unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp
index c68d4da..a7c6168 100644
--- a/src/dawn/tests/unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp
@@ -34,16 +34,17 @@
 namespace dawn {
 namespace {
 
+using testing::_;
 using testing::MockCppCallback;
 
 class QueueOnSubmittedWorkDoneValidationTests : public ValidationTest {
   protected:
-    MockCppCallback<void (*)(wgpu::QueueWorkDoneStatus)> mWorkDoneCb;
+    MockCppCallback<wgpu::QueueWorkDoneCallback<void>*> mWorkDoneCb;
 };
 
 // Test that OnSubmittedWorkDone can be called as soon as the queue is created.
 TEST_F(QueueOnSubmittedWorkDoneValidationTests, CallBeforeSubmits) {
-    EXPECT_CALL(mWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success)).Times(1);
+    EXPECT_CALL(mWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success, _)).Times(1);
     device.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
                                           mWorkDoneCb.Callback());
 
diff --git a/src/dawn/tests/unittests/wire/WireQueueTests.cpp b/src/dawn/tests/unittests/wire/WireQueueTests.cpp
index 8781132..1751551 100644
--- a/src/dawn/tests/unittests/wire/WireQueueTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireQueueTests.cpp
@@ -27,6 +27,8 @@
 
 #include <memory>
 
+#include "dawn/common/StringViewUtils.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/tests/unittests/wire/WireFutureTest.h"
 #include "dawn/tests/unittests/wire/WireTest.h"
 #include "dawn/wire/WireClient.h"
@@ -36,11 +38,12 @@
 namespace {
 
 using testing::_;
+using testing::EmptySizedString;
 using testing::InvokeWithoutArgs;
 using testing::Ne;
+using testing::NonEmptySizedString;
 using testing::Return;
-
-static constexpr WGPUQueueWorkDoneStatus kError = static_cast<WGPUQueueWorkDoneStatus>(0);
+using testing::SizedString;
 
 using WireQueueTestBase = WireFutureTest<wgpu::QueueWorkDoneCallback<void>*>;
 class WireQueueTests : public WireQueueTestBase {
@@ -58,13 +61,14 @@
     OnSubmittedWorkDone();
 
     EXPECT_CALL(api, OnQueueOnSubmittedWorkDone(apiQueue, _)).WillOnce(InvokeWithoutArgs([&] {
-        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Success);
+        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Success,
+                                                 kEmptyOutputStringView);
     }));
     FlushClient();
     FlushFutures();
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(wgpu::QueueWorkDoneStatus::Success)).Times(1);
+        EXPECT_CALL(mockCb, Call(wgpu::QueueWorkDoneStatus::Success, EmptySizedString())).Times(1);
 
         FlushCallbacks();
     });
@@ -75,13 +79,15 @@
     OnSubmittedWorkDone();
 
     EXPECT_CALL(api, OnQueueOnSubmittedWorkDone(apiQueue, _)).WillOnce(InvokeWithoutArgs([&] {
-        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, kError);
+        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Error,
+                                                 ToOutputStringView("Some message"));
     }));
     FlushClient();
     FlushFutures();
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(Ne(wgpu::QueueWorkDoneStatus::Success))).Times(1);
+        EXPECT_CALL(mockCb, Call(wgpu::QueueWorkDoneStatus::Error, SizedString("Some message")))
+            .Times(1);
 
         FlushCallbacks();
     });
@@ -97,13 +103,16 @@
     OnSubmittedWorkDone();
 
     EXPECT_CALL(api, OnQueueOnSubmittedWorkDone(apiQueue, _)).WillOnce(InvokeWithoutArgs([&] {
-        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, kError);
+        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Error,
+                                                 ToOutputStringView("Some message"));
     }));
     FlushClient();
     FlushFutures();
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(wgpu::QueueWorkDoneStatus::CallbackCancelled)).Times(1);
+        EXPECT_CALL(mockCb,
+                    Call(wgpu::QueueWorkDoneStatus::CallbackCancelled, NonEmptySizedString()))
+            .Times(1);
 
         GetWireClient()->Disconnect();
     });
@@ -116,12 +125,15 @@
     OnSubmittedWorkDone();
 
     EXPECT_CALL(api, OnQueueOnSubmittedWorkDone(apiQueue, _)).WillOnce(InvokeWithoutArgs([&] {
-        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, kError);
+        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Error,
+                                                 ToOutputStringView("Some message"));
     }));
     FlushClient();
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(wgpu::QueueWorkDoneStatus::CallbackCancelled)).Times(1);
+        EXPECT_CALL(mockCb,
+                    Call(wgpu::QueueWorkDoneStatus::CallbackCancelled, NonEmptySizedString()))
+            .Times(1);
 
         GetWireClient()->Disconnect();
     });
@@ -133,7 +145,9 @@
     GetWireClient()->Disconnect();
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(wgpu::QueueWorkDoneStatus::CallbackCancelled)).Times(1);
+        EXPECT_CALL(mockCb,
+                    Call(wgpu::QueueWorkDoneStatus::CallbackCancelled, NonEmptySizedString()))
+            .Times(1);
 
         OnSubmittedWorkDone();
     });
@@ -145,12 +159,14 @@
     OnSubmittedWorkDone();
 
     EXPECT_CALL(api, OnQueueOnSubmittedWorkDone(apiQueue, _)).WillOnce(InvokeWithoutArgs([&] {
-        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, kError);
+        api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Error,
+                                                 ToOutputStringView("Some message"));
     }));
     FlushClient();
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(wgpu::QueueWorkDoneStatus::CallbackCancelled))
+        EXPECT_CALL(mockCb,
+                    Call(wgpu::QueueWorkDoneStatus::CallbackCancelled, NonEmptySizedString()))
             .Times(kNumRequests + 1)
             .WillOnce([&]() {
                 for (size_t i = 0; i < kNumRequests; i++) {
diff --git a/src/dawn/wire/client/Queue.cpp b/src/dawn/wire/client/Queue.cpp
index 89bd325..23cbeca 100644
--- a/src/dawn/wire/client/Queue.cpp
+++ b/src/dawn/wire/client/Queue.cpp
@@ -28,8 +28,10 @@
 #include "dawn/wire/client/Queue.h"
 
 #include <memory>
+#include <string>
 #include <utility>
 
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/wire/client/Client.h"
 #include "dawn/wire/client/EventManager.h"
 #include "partition_alloc/pointers/raw_ptr.h"
@@ -49,8 +51,11 @@
 
     EventType GetType() override { return kType; }
 
-    WireResult ReadyHook(FutureID futureID, WGPUQueueWorkDoneStatus status) {
+    WireResult ReadyHook(FutureID futureID,
+                         WGPUQueueWorkDoneStatus status,
+                         WGPUStringView message) {
         mStatus = status;
+        mMessage = ToString(message);
         return WireResult::Success;
     }
 
@@ -58,11 +63,12 @@
     void CompleteImpl(FutureID futureID, EventCompletionType completionType) override {
         if (completionType == EventCompletionType::Shutdown) {
             mStatus = WGPUQueueWorkDoneStatus_CallbackCancelled;
+            mMessage = "A valid external Instance reference no longer exists.";
         }
         void* userdata1 = mUserdata1.ExtractAsDangling();
         void* userdata2 = mUserdata2.ExtractAsDangling();
         if (mCallback) {
-            mCallback(mStatus, userdata1, userdata2);
+            mCallback(mStatus, ToOutputStringView(mMessage), userdata1, userdata2);
         }
     }
 
@@ -71,6 +77,7 @@
     raw_ptr<void> mUserdata2;
 
     WGPUQueueWorkDoneStatus mStatus = WGPUQueueWorkDoneStatus_Success;
+    std::string mMessage;
 };
 
 }  // anonymous namespace
@@ -83,8 +90,9 @@
 
 WireResult Client::DoQueueWorkDoneCallback(ObjectHandle eventManager,
                                            WGPUFuture future,
-                                           WGPUQueueWorkDoneStatus status) {
-    return GetEventManager(eventManager).SetFutureReady<WorkDoneEvent>(future.id, status);
+                                           WGPUQueueWorkDoneStatus status,
+                                           WGPUStringView message) {
+    return GetEventManager(eventManager).SetFutureReady<WorkDoneEvent>(future.id, status, message);
 }
 
 WGPUFuture Queue::OnSubmittedWorkDone(const WGPUQueueWorkDoneCallbackInfo& callbackInfo) {
diff --git a/src/dawn/wire/server/Server.h b/src/dawn/wire/server/Server.h
index 2c87127..7dd5f64 100644
--- a/src/dawn/wire/server/Server.h
+++ b/src/dawn/wire/server/Server.h
@@ -250,7 +250,9 @@
     void OnBufferMapAsyncCallback(MapUserdata* userdata,
                                   WGPUMapAsyncStatus status,
                                   WGPUStringView message);
-    void OnQueueWorkDone(QueueWorkDoneUserdata* userdata, WGPUQueueWorkDoneStatus status);
+    void OnQueueWorkDone(QueueWorkDoneUserdata* userdata,
+                         WGPUQueueWorkDoneStatus status,
+                         WGPUStringView message);
     void OnCreateComputePipelineAsyncCallback(CreatePipelineAsyncUserData* userdata,
                                               WGPUCreatePipelineAsyncStatus status,
                                               WGPUComputePipeline pipeline,
diff --git a/src/dawn/wire/server/ServerQueue.cpp b/src/dawn/wire/server/ServerQueue.cpp
index b95c57d..dc7b51e 100644
--- a/src/dawn/wire/server/ServerQueue.cpp
+++ b/src/dawn/wire/server/ServerQueue.cpp
@@ -32,11 +32,14 @@
 
 namespace dawn::wire::server {
 
-void Server::OnQueueWorkDone(QueueWorkDoneUserdata* data, WGPUQueueWorkDoneStatus status) {
+void Server::OnQueueWorkDone(QueueWorkDoneUserdata* data,
+                             WGPUQueueWorkDoneStatus status,
+                             WGPUStringView message) {
     ReturnQueueWorkDoneCallbackCmd cmd;
     cmd.eventManager = data->eventManager;
     cmd.future = data->future;
     cmd.status = status;
+    cmd.message = message;
 
     SerializeCommand(cmd);
 }
diff --git a/src/emdawnwebgpu/tests/FuturesTests.cpp b/src/emdawnwebgpu/tests/FuturesTests.cpp
index b36d1a2..a97afb2 100644
--- a/src/emdawnwebgpu/tests/FuturesTests.cpp
+++ b/src/emdawnwebgpu/tests/FuturesTests.cpp
@@ -275,13 +275,12 @@
     queue.Submit(1, &commands);
 
     wgpu::QueueWorkDoneStatus copyStatus;
-    EXPECT_EQ(
-        instance.WaitAny(queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous,
-                                                   [&copyStatus](wgpu::QueueWorkDoneStatus status) {
-                                                       copyStatus = status;
-                                                   }),
-                         UINT64_MAX),
-        wgpu::WaitStatus::Success);
+    EXPECT_EQ(instance.WaitAny(queue.OnSubmittedWorkDone(
+                                   wgpu::CallbackMode::AllowSpontaneous,
+                                   [&copyStatus](wgpu::QueueWorkDoneStatus status,
+                                                 wgpu::StringView) { copyStatus = status; }),
+                               UINT64_MAX),
+              wgpu::WaitStatus::Success);
     ASSERT_EQ(copyStatus, wgpu::QueueWorkDoneStatus::Success);
 
     // Map the readable buffer and verify the contents.
diff --git a/third_party/emdawnwebgpu/pkg/webgpu/src/library_webgpu.js b/third_party/emdawnwebgpu/pkg/webgpu/src/library_webgpu.js
index e51c549..7198a61 100644
--- a/third_party/emdawnwebgpu/pkg/webgpu/src/library_webgpu.js
+++ b/third_party/emdawnwebgpu/pkg/webgpu/src/library_webgpu.js
@@ -2078,6 +2078,7 @@
       _emwgpuOnWorkDoneCompleted(futureId, {{{ gpu.QueueWorkDoneStatus.Success }}});
     }, () => {
       {{{ runtimeKeepalivePop() }}}
+      // We could translate this into a status+message, but it's not supposed to ever happen.
       abort('Unexpected failure in GPUQueue.onSubmittedWorkDone().')
     }));
   },
diff --git a/third_party/emdawnwebgpu/pkg/webgpu/src/webgpu.cpp b/third_party/emdawnwebgpu/pkg/webgpu/src/webgpu.cpp
index be0bb93..730cfdb 100644
--- a/third_party/emdawnwebgpu/pkg/webgpu/src/webgpu.cpp
+++ b/third_party/emdawnwebgpu/pkg/webgpu/src/webgpu.cpp
@@ -1235,11 +1235,13 @@
   void ReadyHook(WGPUQueueWorkDoneStatus status) { mStatus = status; }
 
   void Complete(FutureID, EventCompletionType type) override {
+    std::string message;
     if (type == EventCompletionType::Shutdown) {
       mStatus = WGPUQueueWorkDoneStatus_CallbackCancelled;
+      message = "A valid external Instance reference no longer exists.";
     }
     if (mCallback) {
-      mCallback(mStatus, mUserdata1, mUserdata2);
+      mCallback(mStatus, ToOutputStringView(message), mUserdata1, mUserdata2);
     }
   }
 
diff --git a/third_party/webgpu-headers/webgpu.h.diff b/third_party/webgpu-headers/webgpu.h.diff
index 4cbae88..822bf96 100644
--- a/third_party/webgpu-headers/webgpu.h.diff
+++ b/third_party/webgpu-headers/webgpu.h.diff
@@ -7,6 +7,7 @@
 +#define WGPU_BREAKING_CHANGE_STRING_VIEW_LABELS
 +#define WGPU_BREAKING_CHANGE_STRING_VIEW_OUTPUT_STRUCTS
 +#define WGPU_BREAKING_CHANGE_STRING_VIEW_CALLBACKS
++#define WGPU_BREAKING_CHANGE_QUEUE_WORK_DONE_CALLBACK_MESSAGE
  
  #if defined(WGPU_SHARED_LIBRARY)
  #    if defined(_WIN32)
@@ -108,15 +109,6 @@
  } WGPUWGSLLanguageFeatureName WGPU_ENUM_ATTRIBUTE;
  
 @@
- 
- typedef void (*WGPUPopErrorScopeCallback)(WGPUPopErrorScopeStatus status, WGPUErrorType type, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE;
- 
--typedef void (*WGPUQueueWorkDoneCallback)(WGPUQueueWorkDoneStatus status, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE;
-+typedef void (*WGPUQueueWorkDoneCallback)(WGPUQueueWorkDoneStatus status, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE;
- 
- typedef void (*WGPURequestAdapterCallback)(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE;
- 
-@@
  #define WGPU_BUFFER_BINDING_LAYOUT_INIT _wgpu_MAKE_INIT_STRUCT(WGPUBufferBindingLayout, { \
      /*.nextInChain=*/NULL _wgpu_COMMA \
      /*.type=*/WGPUBufferBindingType_Undefined _wgpu_COMMA \