[dawn][headers] Match upstream webgpu.h for WGSLLanguageFeatures

- Adds WGSLLanguageFeatureName enum. Older WGSLFeatureName will
  be removed once downstream dependencies have migrated to new
  enums and APIs.
- Adds SupportedWGSLLanguageFeatures struct.
- Implements Instance::GetWGSLLanguageFeatures.

Bug: 368672124
Change-Id: Ic4966c49049c63894495e0939aa65a1077fd4e8b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/223219
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index 58a8ebc..c5f817d 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -1601,6 +1601,14 @@
             {"name": "features", "type": "feature name", "annotation": "const*", "length": "feature count"}
         ]
     },
+    "supported WGSL language features": {
+        "category": "structure",
+        "out": true,
+        "members": [
+            {"name": "feature count", "type": "size_t"},
+            {"name": "features", "type": "WGSL language feature name", "annotation": "const*", "length": "feature count"}
+        ]
+    },
     "logging callback": {
         "category": "callback function",
         "tags": ["dawn"],
@@ -2372,7 +2380,7 @@
                 "name": "has WGSL language feature",
                 "returns": "bool",
                 "args": [
-                    {"name": "feature", "type": "WGSL feature name"}
+                    {"name": "feature", "type": "WGSL language feature name"}
                 ]
             },
             {
@@ -2381,6 +2389,13 @@
                 "args": [
                     {"name": "features", "type": "WGSL feature name", "annotation": "*"}
                 ]
+            },
+            {
+                "name": "get WGSL language features",
+                "returns": "status",
+                "args": [
+                    {"name": "features", "type": "supported WGSL language features", "annotation": "*"}
+                ]
             }
         ]
     },
@@ -4087,6 +4102,21 @@
             {"value": 4, "name": "chromium testing shipped", "jsrepr": "'chromium_testing_shipped'", "tags": ["dawn"]}
         ]
     },
+    "WGSL language feature name": {
+        "category": "enum",
+        "values": [
+            {"value": 1, "name": "readonly and readwrite storage textures",  "jsrepr": "'readonly_and_readwrite_storage_textures'"},
+            {"value": 2, "name": "packed 4x8 integer dot product", "jsrepr": "'packed_4x8_integer_dot_product'"},
+            {"value": 3, "name": "unrestricted pointer parameters", "jsrepr": "'unrestricted_pointer_parameters'"},
+            {"value": 4, "name": "pointer composite access", "jsrepr": "'pointer_composite_access'"},
+
+            {"value": 0, "name": "chromium testing unimplemented", "jsrepr": "'chromium_testing_unimplemented'", "tags": ["dawn"]},
+            {"value": 1, "name": "chromium testing unsafe experimental", "jsrepr": "'chromium_testing_unsafe_experimental'", "tags": ["dawn"]},
+            {"value": 2, "name": "chromium testing experimental", "jsrepr": "'chromium_testing_experimental'", "tags": ["dawn"]},
+            {"value": 3, "name": "chromium testing shipped with killswitch", "jsrepr": "'chromium_testing_shipped_with_killswitch'", "tags": ["dawn"]},
+            {"value": 4, "name": "chromium testing shipped", "jsrepr": "'chromium_testing_shipped'", "tags": ["dawn"]}
+        ]
+    },
     "whole size" : {
         "category": "constant",
         "type": "uint64_t",
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 51216f3..076fd40 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -240,6 +240,7 @@
             "DevicePopErrorScope2",
             "DeviceSetLoggingCallback",
             "InstanceEnumerateWGSLLanguageFeatures",
+            "InstanceGetWGSLLanguageFeatures",
             "InstanceHasWGSLLanguageFeature",
             "InstanceCreateSurface",
             "InstanceRequestAdapter",
diff --git a/src/dawn/native/Instance.cpp b/src/dawn/native/Instance.cpp
index 0edb138..89bdf46 100644
--- a/src/dawn/native/Instance.cpp
+++ b/src/dawn/native/Instance.cpp
@@ -103,11 +103,11 @@
 
 namespace {
 
-wgpu::WGSLFeatureName ToWGPUFeature(tint::wgsl::LanguageFeature f) {
+wgpu::WGSLLanguageFeatureName ToWGPUWGSLLanguageFeature(tint::wgsl::LanguageFeature f) {
     switch (f) {
 #define CASE(WgslName, WgpuName)                \
     case tint::wgsl::LanguageFeature::WgslName: \
-        return wgpu::WGSLFeatureName::WgpuName;
+        return wgpu::WGSLLanguageFeatureName::WgpuName;
         DAWN_FOREACH_WGSL_FEATURE(CASE)
 #undef CASE
         case tint::wgsl::LanguageFeature::kUndefined:
@@ -656,7 +656,7 @@
         }
 
         if (enable && wgslFeature != tint::wgsl::LanguageFeature::kUndefined) {
-            mWGSLFeatures.emplace(ToWGPUFeature(wgslFeature));
+            mWGSLFeatures.emplace(ToWGPUWGSLLanguageFeature(wgslFeature));
             mTintLanguageFeatures.emplace(wgslFeature);
         }
     }
@@ -671,23 +671,45 @@
                 continue;
             }
             mTintLanguageFeatures.erase(tintFeature);
-            mWGSLFeatures.erase(ToWGPUFeature(tintFeature));
+            mWGSLFeatures.erase(ToWGPUWGSLLanguageFeature(tintFeature));
         }
     }
 }
 
-bool InstanceBase::APIHasWGSLLanguageFeature(wgpu::WGSLFeatureName feature) const {
+bool InstanceBase::APIHasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName feature) const {
     return mWGSLFeatures.contains(feature);
 }
 
+wgpu::Status InstanceBase::APIGetWGSLLanguageFeatures(
+    SupportedWGSLLanguageFeatures* features) const {
+    DAWN_ASSERT(features != nullptr);
+
+    size_t featureCount = mWGSLFeatures.size();
+    wgpu::WGSLLanguageFeatureName* wgslFeatures = new wgpu::WGSLLanguageFeatureName[featureCount];
+    uint32_t index = 0;
+    for (wgpu::WGSLLanguageFeatureName feature : mWGSLFeatures) {
+        wgslFeatures[index++] = feature;
+    }
+    DAWN_ASSERT(index == featureCount);
+
+    features->featureCount = featureCount;
+    features->features = wgslFeatures;
+    return wgpu::Status::Success;
+}
+
 size_t InstanceBase::APIEnumerateWGSLLanguageFeatures(wgpu::WGSLFeatureName* features) const {
     if (features != nullptr) {
-        for (wgpu::WGSLFeatureName f : mWGSLFeatures) {
-            *features = f;
+        for (wgpu::WGSLLanguageFeatureName f : mWGSLFeatures) {
+            *features = static_cast<wgpu::WGSLFeatureName>(f);
             ++features;
         }
     }
     return mWGSLFeatures.size();
 }
 
+void APISupportedWGSLLanguageFeaturesFreeMembers(
+    WGPUSupportedWGSLLanguageFeatures supportedFeatures) {
+    delete[] supportedFeatures.features;
+}
+
 }  // namespace dawn::native
diff --git a/src/dawn/native/Instance.h b/src/dawn/native/Instance.h
index abe8f5e..6545bfa 100644
--- a/src/dawn/native/Instance.h
+++ b/src/dawn/native/Instance.h
@@ -149,7 +149,8 @@
     [[nodiscard]] wgpu::WaitStatus APIWaitAny(size_t count,
                                               FutureWaitInfo* futures,
                                               uint64_t timeoutNS);
-    bool APIHasWGSLLanguageFeature(wgpu::WGSLFeatureName feature) const;
+    bool APIHasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName feature) const;
+    wgpu::Status APIGetWGSLLanguageFeatures(SupportedWGSLLanguageFeatures* features) const;
     // Always writes the full list when features is not nullptr.
     // TODO(https://github.com/webgpu-native/webgpu-headers/issues/252): Add a count argument.
     size_t APIEnumerateWGSLLanguageFeatures(wgpu::WGSLFeatureName* features) const;
@@ -208,7 +209,7 @@
     TogglesState mToggles;
     TogglesInfo mTogglesInfo;
 
-    absl::flat_hash_set<wgpu::WGSLFeatureName> mWGSLFeatures;
+    absl::flat_hash_set<wgpu::WGSLLanguageFeatureName> mWGSLFeatures;
     absl::flat_hash_set<tint::wgsl::LanguageFeature> mTintLanguageFeatures;
 
 #if defined(DAWN_USE_X11)
diff --git a/src/dawn/tests/end2end/Packed4x8IntegerDotProductTests.cpp b/src/dawn/tests/end2end/Packed4x8IntegerDotProductTests.cpp
index d6433b1..021f633 100644
--- a/src/dawn/tests/end2end/Packed4x8IntegerDotProductTests.cpp
+++ b/src/dawn/tests/end2end/Packed4x8IntegerDotProductTests.cpp
@@ -63,7 +63,8 @@
         }
 )";
 
-    ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::Packed4x8IntegerDotProduct));
+    ASSERT_TRUE(
+        instance.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::Packed4x8IntegerDotProduct));
 
     wgpu::BufferDescriptor bufferDesc;
     bufferDesc.size = 4 * sizeof(uint32_t);
@@ -134,7 +135,8 @@
         }
 )";
 
-    ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::Packed4x8IntegerDotProduct));
+    ASSERT_TRUE(
+        instance.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::Packed4x8IntegerDotProduct));
 
     wgpu::BufferDescriptor bufferDesc;
     bufferDesc.size = 8 * sizeof(uint32_t);
@@ -205,7 +207,8 @@
         }
 )";
 
-    ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::Packed4x8IntegerDotProduct));
+    ASSERT_TRUE(
+        instance.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::Packed4x8IntegerDotProduct));
 
     wgpu::BufferDescriptor bufferDesc;
     bufferDesc.size = sizeof(uint32_t) * 4 * 8;
diff --git a/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp b/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp
index 8bc7b18..5eff844 100644
--- a/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp
@@ -87,24 +87,26 @@
     }
 };
 
-wgpu::WGSLFeatureName kNonExistentFeature = static_cast<wgpu::WGSLFeatureName>(0xFFFF'FFFF);
+wgpu::WGSLLanguageFeatureName kNonExistentFeature =
+    static_cast<wgpu::WGSLLanguageFeatureName>(0xFFFF'FFFF);
 
 // Check HasFeature for an Instance that doesn't have unsafe APIs.
 TEST_F(WGSLFeatureValidationTest, HasFeatureDefaultInstance) {
     SetUp({});
 
     // Shipped features are present.
-    ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
+    ASSERT_TRUE(
+        instance.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::ChromiumTestingShipped));
     ASSERT_TRUE(instance.HasWGSLLanguageFeature(
-        wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch));
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingShippedWithKillswitch));
 
     // Experimental and unimplemented features are not present.
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnimplemented));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingExperimental));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnsafeExperimental));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnimplemented));
 
     // Non-existent features are not present.
     ASSERT_FALSE(instance.HasWGSLLanguageFeature(kNonExistentFeature));
@@ -115,17 +117,18 @@
     SetUp({.exposeExperimental = true});
 
     // Shipped and experimental features are present.
-    ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
-    ASSERT_TRUE(instance.HasWGSLLanguageFeature(
-        wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch));
     ASSERT_TRUE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
+        instance.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::ChromiumTestingShipped));
+    ASSERT_TRUE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingShippedWithKillswitch));
+    ASSERT_TRUE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingExperimental));
 
     // Unsafe and unimplemented features are not present.
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnimplemented));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnsafeExperimental));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnimplemented));
 
     // Non-existent features are not present.
     ASSERT_FALSE(instance.HasWGSLLanguageFeature(kNonExistentFeature));
@@ -136,17 +139,18 @@
     SetUp({.allowUnsafeAPIs = true});
 
     // Shipped and experimental features are present.
-    ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
+    ASSERT_TRUE(
+        instance.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::ChromiumTestingShipped));
     ASSERT_TRUE(instance.HasWGSLLanguageFeature(
-        wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch));
-    ASSERT_TRUE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
-    ASSERT_TRUE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingShippedWithKillswitch));
+    ASSERT_TRUE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingExperimental));
+    ASSERT_TRUE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnsafeExperimental));
 
     // Unimplemented features are not present.
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnimplemented));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnimplemented));
 
     // Non-existent features are not present.
     ASSERT_FALSE(instance.HasWGSLLanguageFeature(kNonExistentFeature));
@@ -157,51 +161,47 @@
     SetUp({.useTestingFeatures = false});
 
     // None of the testing features are present.
-    ASSERT_FALSE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
+    ASSERT_FALSE(
+        instance.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::ChromiumTestingShipped));
     ASSERT_FALSE(instance.HasWGSLLanguageFeature(
-        wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch));
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnimplemented));
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingShippedWithKillswitch));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingExperimental));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnsafeExperimental));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnimplemented));
 }
 
-// Tests for the behavior of WGSL feature enumeration.
-TEST_F(WGSLFeatureValidationTest, EnumerateFeatures) {
+// Tests for the behavior of getting WGSL language features.
+TEST_F(WGSLFeatureValidationTest, GetFeatures) {
     SetUp({});
 
-    size_t featureCount = instance.EnumerateWGSLLanguageFeatures(nullptr);
-
-    std::vector<wgpu::WGSLFeatureName> features(featureCount + 1, kNonExistentFeature);
-    size_t secondFeatureCount = instance.EnumerateWGSLLanguageFeatures(features.data());
+    wgpu::SupportedWGSLLanguageFeatures supportedFeatures = {};
+    ASSERT_EQ(wgpu::Status::Success, instance.GetWGSLLanguageFeatures(&supportedFeatures));
+    ASSERT_NE(0u, supportedFeatures.featureCount);
+    const wgpu::WGSLLanguageFeatureName* features = supportedFeatures.features;
 
     // Exactly featureCount features should be written, and all return true in HasWGSLFeature.
-    ASSERT_EQ(secondFeatureCount, featureCount);
-    for (size_t i = 0; i < featureCount; i++) {
+    for (size_t i = 0; i < supportedFeatures.featureCount; i++) {
         ASSERT_TRUE(instance.HasWGSLLanguageFeature(features[i]));
-        ASSERT_NE(kNonExistentFeature, features[i]);
     }
-    ASSERT_EQ(kNonExistentFeature, features[featureCount]);
 
     // Test the presence / absence of some known testing features.
+    const wgpu::WGSLLanguageFeatureName* begin = features;
+    const wgpu::WGSLLanguageFeatureName* end = features + supportedFeatures.featureCount;
+    ASSERT_NE(std::find(begin, end, wgpu::WGSLLanguageFeatureName::ChromiumTestingShipped), end);
     ASSERT_NE(
-        std::find(features.begin(), features.end(), wgpu::WGSLFeatureName::ChromiumTestingShipped),
-        features.end());
-    ASSERT_NE(std::find(features.begin(), features.end(),
-                        wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch),
-              features.end());
+        std::find(begin, end, wgpu::WGSLLanguageFeatureName::ChromiumTestingShippedWithKillswitch),
+        end);
 
-    ASSERT_EQ(std::find(features.begin(), features.end(),
-                        wgpu::WGSLFeatureName::ChromiumTestingUnimplemented),
-              features.end());
-    ASSERT_EQ(std::find(features.begin(), features.end(),
-                        wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental),
-              features.end());
-    ASSERT_EQ(std::find(features.begin(), features.end(),
-                        wgpu::WGSLFeatureName::ChromiumTestingExperimental),
-              features.end());
+    ASSERT_EQ(std::find(begin, end, wgpu::WGSLLanguageFeatureName::ChromiumTestingUnimplemented),
+              end);
+    ASSERT_EQ(
+        std::find(begin, end, wgpu::WGSLLanguageFeatureName::ChromiumTestingUnsafeExperimental),
+        end);
+    ASSERT_EQ(std::find(begin, end, wgpu::WGSLLanguageFeatureName::ChromiumTestingExperimental),
+              end);
 }
 
 // Check that the enabled / disabled features are used to validate the WGSL shaders.
@@ -232,14 +232,15 @@
 
     // The blocklisted feature is not present.
     ASSERT_FALSE(instance.HasWGSLLanguageFeature(
-        wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch));
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingShippedWithKillswitch));
 
     // The others are.
-    ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
     ASSERT_TRUE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
-    ASSERT_TRUE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
+        instance.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::ChromiumTestingShipped));
+    ASSERT_TRUE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingExperimental));
+    ASSERT_TRUE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnsafeExperimental));
 
     // Using the blocklisted extension fails.
     ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, R"(
@@ -254,11 +255,12 @@
                          "chromium_testing_unsafe_experimental"}});
 
     // All blocklisted features aren't present.
-    ASSERT_FALSE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
     ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
-    ASSERT_FALSE(
-        instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
+        instance.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::ChromiumTestingShipped));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingExperimental));
+    ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+        wgpu::WGSLLanguageFeatureName::ChromiumTestingUnsafeExperimental));
 }
 
 // Test that DawnWGSLBlocklist can contain garbage names without causing problems.
diff --git a/src/dawn/wire/client/Instance.cpp b/src/dawn/wire/client/Instance.cpp
index 7555bd4..d2d48ba 100644
--- a/src/dawn/wire/client/Instance.cpp
+++ b/src/dawn/wire/client/Instance.cpp
@@ -109,11 +109,11 @@
     Ref<Adapter> mAdapter;
 };
 
-WGPUWGSLFeatureName ToWGPUFeature(tint::wgsl::LanguageFeature f) {
+WGPUWGSLLanguageFeatureName ToWGPUWGSLLanguageFeature(tint::wgsl::LanguageFeature f) {
     switch (f) {
 #define CASE(WgslName, WgpuName)                \
     case tint::wgsl::LanguageFeature::WgslName: \
-        return WGPUWGSLFeatureName_##WgpuName;
+        return WGPUWGSLLanguageFeatureName_##WgpuName;
         DAWN_FOREACH_WGSL_FEATURE(CASE)
 #undef CASE
         case tint::wgsl::LanguageFeature::kUndefined:
@@ -263,7 +263,7 @@
         }
 
         if (enable && wgslFeature != tint::wgsl::LanguageFeature::kUndefined) {
-            mWGSLFeatures.emplace(ToWGPUFeature(wgslFeature));
+            mWGSLFeatures.emplace(ToWGPUWGSLLanguageFeature(wgslFeature));
         }
     }
 
@@ -276,19 +276,37 @@
                 // Ignore unknown features in the blocklist.
                 continue;
             }
-            mWGSLFeatures.erase(ToWGPUFeature(tintFeature));
+            mWGSLFeatures.erase(ToWGPUWGSLLanguageFeature(tintFeature));
         }
     }
 }
 
-bool Instance::HasWGSLLanguageFeature(WGPUWGSLFeatureName feature) const {
+bool Instance::HasWGSLLanguageFeature(WGPUWGSLLanguageFeatureName feature) const {
     return mWGSLFeatures.contains(feature);
 }
 
+WGPUStatus Instance::GetWGSLLanguageFeatures(WGPUSupportedWGSLLanguageFeatures* features) const {
+    if (features == nullptr) {
+        return WGPUStatus_Error;
+    }
+
+    size_t featureCount = mWGSLFeatures.size();
+    WGPUWGSLLanguageFeatureName* wgslFeatures = new WGPUWGSLLanguageFeatureName[featureCount];
+    uint32_t index = 0;
+    for (WGPUWGSLLanguageFeatureName feature : mWGSLFeatures) {
+        wgslFeatures[index++] = feature;
+    }
+    DAWN_ASSERT(index == featureCount);
+
+    features->featureCount = featureCount;
+    features->features = wgslFeatures;
+    return WGPUStatus_Success;
+}
+
 size_t Instance::EnumerateWGSLLanguageFeatures(WGPUWGSLFeatureName* features) const {
     if (features != nullptr) {
-        for (WGPUWGSLFeatureName f : mWGSLFeatures) {
-            *features = f;
+        for (WGPUWGSLLanguageFeatureName f : mWGSLFeatures) {
+            *features = static_cast<WGPUWGSLFeatureName>(f);
             ++features;
         }
     }
@@ -320,3 +338,8 @@
     DAWN_UNREACHABLE();
     return nullptr;
 }
+
+DAWN_WIRE_EXPORT void wgpuDawnWireClientSupportedWGSLLanguageFeaturesFreeMembers(
+    WGPUSupportedWGSLLanguageFeatures supportedFeatures) {
+    delete[] supportedFeatures.features;
+}
diff --git a/src/dawn/wire/client/Instance.h b/src/dawn/wire/client/Instance.h
index c88f520..72d425c 100644
--- a/src/dawn/wire/client/Instance.h
+++ b/src/dawn/wire/client/Instance.h
@@ -50,7 +50,8 @@
     void ProcessEvents();
     WGPUWaitStatus WaitAny(size_t count, WGPUFutureWaitInfo* infos, uint64_t timeoutNS);
 
-    bool HasWGSLLanguageFeature(WGPUWGSLFeatureName feature) const;
+    bool HasWGSLLanguageFeature(WGPUWGSLLanguageFeatureName feature) const;
+    WGPUStatus GetWGSLLanguageFeatures(WGPUSupportedWGSLLanguageFeatures* features) const;
     // Always writes the full list when features is not nullptr.
     // TODO(https://github.com/webgpu-native/webgpu-headers/issues/252): Add a count argument.
     size_t EnumerateWGSLLanguageFeatures(WGPUWGSLFeatureName* features) const;
@@ -62,7 +63,7 @@
     void GatherWGSLFeatures(const WGPUDawnWireWGSLControl* wgslControl,
                             const WGPUDawnWGSLBlocklist* wgslBlocklist);
 
-    absl::flat_hash_set<WGPUWGSLFeatureName> mWGSLFeatures;
+    absl::flat_hash_set<WGPUWGSLLanguageFeatureName> mWGSLFeatures;
 };
 
 }  // namespace dawn::wire::client
diff --git a/third_party/emdawnwebgpu/library_webgpu.js b/third_party/emdawnwebgpu/library_webgpu.js
index e0f9949..dd6c6c4 100644
--- a/third_party/emdawnwebgpu/library_webgpu.js
+++ b/third_party/emdawnwebgpu/library_webgpu.js
@@ -1935,7 +1935,24 @@
     if (!('wgslLanguageFeatures' in navigator["gpu"])) {
       return false;
     }
-    return navigator["gpu"]["wgslLanguageFeatures"].has(WebGPU.WGSLFeatureName[featureEnumValue]);
+    return navigator["gpu"]["wgslLanguageFeatures"].has(WebGPU.WGSLLanguageFeatureName[featureEnumValue]);
+  },
+
+  wgpuInstanceGetWGSLLanguageFeatures: (instance, supportedFeatures) => {
+    // Always allocate enough space for all the features, though some may be unused.
+    var featuresPtr = _malloc(navigator["gpu"]["wgslLanguageFeatures"].size * 4);
+    var offset = 0;
+    var numFeatures = 0;
+    navigator["gpu"]["wgslLanguageFeatures"].forEach(feature => {
+      var featureEnumValue = WebGPU.WGSLLanguageFeatureNameString2Enum[feature];
+      if (featureEnumValue !== undefined) {
+        {{{ makeSetValue('featuresPtr', 'offset', 'featureEnumValue', 'i32') }}};
+        offset += 4;
+        numFeatures++;
+      }
+    });
+    {{{ makeSetValue('supportedFeatures', C_STRUCTS.WGPUSupportedWGSLLanguageFeatures.features, 'featuresPtr', '*') }}};
+    {{{ makeSetValue('supportedFeatures', C_STRUCTS.WGPUSupportedWGSLLanguageFeatures.featureCount, 'numFeatures', '*') }}};
   },
 
   emwgpuInstanceRequestAdapter__deps: ['emwgpuCreateAdapter', 'emwgpuOnRequestAdapterCompleted'],
@@ -2530,6 +2547,10 @@
 for (var value in LibraryWebGPU.$WebGPU.FeatureName) {
   LibraryWebGPU.$WebGPU.FeatureNameString2Enum[LibraryWebGPU.$WebGPU.FeatureName[value]] = value;
 }
+LibraryWebGPU.$WebGPU.WGSLLanguageFeatureNameString2Enum = {};
+for (var value in LibraryWebGPU.$WebGPU.WGSLLanguageFeatureName) {
+  LibraryWebGPU.$WebGPU.WGSLLanguageFeatureNameString2Enum[LibraryWebGPU.$WebGPU.WGSLLanguageFeatureName[value]] = value;
+}
 
 // Add and set __i53abi to true for functions with 64-bit value in their
 // signatures, if not explicitly set otherwise.
diff --git a/tools/android/BUILD.gn b/tools/android/BUILD.gn
index 94a7077..acf6b16 100644
--- a/tools/android/BUILD.gn
+++ b/tools/android/BUILD.gn
@@ -183,6 +183,7 @@
     "java/android/dawn/StoreOp.kt",
     "java/android/dawn/SupportedFeatures.kt",
     "java/android/dawn/SupportedLimits.kt",
+    "java/android/dawn/SupportedWGSLLanguageFeatures.kt",
     "java/android/dawn/Surface.kt",
     "java/android/dawn/SurfaceCapabilities.kt",
     "java/android/dawn/SurfaceConfiguration.kt",
@@ -211,6 +212,7 @@
     "java/android/dawn/VertexState.kt",
     "java/android/dawn/VertexStepMode.kt",
     "java/android/dawn/WGSLFeatureName.kt",
+    "java/android/dawn/WGSLLanguageFeatureName.kt",
     "java/android/dawn/WaitStatus.kt",
   ]
 }
diff --git a/tools/android/webgpu/src/test/java/android/dawn/MappedNamedConstantsTest.kt b/tools/android/webgpu/src/test/java/android/dawn/MappedNamedConstantsTest.kt
index 886ca44..8f47d0f 100644
--- a/tools/android/webgpu/src/test/java/android/dawn/MappedNamedConstantsTest.kt
+++ b/tools/android/webgpu/src/test/java/android/dawn/MappedNamedConstantsTest.kt
@@ -69,6 +69,7 @@
         VertexFormat::class,
         VertexStepMode::class,
         WaitStatus::class,
+        WGSLLanguageFeatureName::class,
         WGSLFeatureName::class
     )