Medium CVSS 6.5 webkit Cross Origin 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may disclose sensitive user information
ComponentWebCore
Sub-componentWebCore WebGPU
Bug ClassCross Origin
Tracker315368
Fix commit67b563b85f48 (WebKit/WebKit) +80/-0
CWECWE-346 (Origin validation error)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedVitaly Simonovich, Muhamad Syaiful, Christian Meurer Xavier
Disclosed2026-06-29

Background

importExternalTexture
A WebGPU API ingesting HTMLVideoElement frames as a texture that shaders can sample and read back.
Origin taint / taintsOrigin
Cross-origin non-CORS media is tainted; its pixels must not be readable, as canvas enforces.
External-texture reuse path
A Cocoa fast path that undestroys/reuses a previously-created external texture; it also needs the taint check.

Root Cause Analysis

This closes remaining cross-origin video paths in WebGPU’s importExternalTexture that were not covered by the origin-taint check, defeating the Same-Origin Policy for video pixels. GPUDevice::importExternalTexture ingests an HTMLVideoElement’s frames as a WebGPU external texture that a shader can sample and read back. To respect the SOP (as canvas does by tainting), an origin-taint check must reject a cross-origin, non-CORS video before it enters the pipeline.

Pre-patch, importExternalTexture had paths that imported the video WITHOUT that check — notably the Cocoa fast path that reuses/undestroys a previously-created external texture (externalTextureForDescriptor) and the main import path.

The fix adds a checkVideoElementOriginTaint lambda that fetches the caller’s security origin from scriptExecutionContext()->securityOrigin() and, if videoElement.taintsOrigin(securityOrigin), returns a SecurityError (‘Cross origin external videos are not allowed in WebGPU’); it invokes this check on BOTH the reused-external-texture path and the main path before importing.

The restored invariant is that a cross-origin tainted video cannot enter (or be reused in) a WebGPU external texture through any importExternalTexture path, so its pixels cannot be sampled and exfiltrated. This is an information-disclosure fix, not memory corruption.

Key insight
importExternalTexture enforced the cross-origin video taint check on only some paths; adding it to the reused-external-texture fast path and the main path closes the remaining SOP holes.

Attack Path

  1. Load a cross-origin video Load a cross-origin video without CORS so it is origin-tainted.
  2. Import it as an external texture Call GPUDevice.importExternalTexture on the tainted video, hitting a path that lacked the taint check (e.g. the reused-texture fast path).
  3. Sample and read back Sample the external texture in a shader and read the result into a GPU buffer readable by script.
  4. Exfiltrate cross-origin pixels Recover the cross-origin video’s pixels, defeating the Same-Origin Policy.

Impact Assessment

A cross-origin information disclosure in the WebContent process defeating the Same-Origin Policy for video: uncovered importExternalTexture paths let a page read pixels of a cross-origin video. No memory corruption; the risk is exfiltration of cross-origin visual content (e.g. authenticated video), reliable and stealthy.

Files Changed

  • LayoutTests/fast/webgpu/regression/repro_315368-expected.txt
  • LayoutTests/fast/webgpu/regression/repro_315368.html
  • LayoutTests/fast/webgpu/regression/repro_315368b-expected.txt
  • LayoutTests/fast/webgpu/regression/repro_315368b.html
  • Source/WebCore/Modules/WebGPU/GPUDevice.cpp

Audit Directions

  • All media-ingestion paths
    Audit every importExternalTexture / external-texture reuse and update path (and WebGL video uploads) for a consistent taintsOrigin check.
  • Origin retrieval
    Confirm each path obtains the caller’s security origin (scriptExecutionContext()->securityOrigin()) before importing media.
diff --git a/Source/WebCore/Modules/WebGPU/GPUDevice.cpp b/Source/WebCore/Modules/WebGPU/GPUDevice.cpp
index add1e02b8303..3d03ba423a61 100644
--- a/Source/WebCore/Modules/WebGPU/GPUDevice.cpp
+++ b/Source/WebCore/Modules/WebGPU/GPUDevice.cpp
@@ -74,6 +74,7 @@
 #include "JSGPUUncapturedErrorEvent.h"
 #include "JSGPUValidationError.h"
 #include "RequestAnimationFrameCallback.h"
+#include "SecurityOrigin.h"
 #include "WebGPUXRBinding.h"
 #include "XRGPUBinding.h"
 #include <wtf/TZoneMallocInlines.h>
@@ -394,6 +395,15 @@ private:
 
 ExceptionOr<Ref<GPUExternalTexture>> GPUDevice::importExternalTexture(GPUExternalTextureDescriptor&& externalTextureDescriptor)
 {
+#if ENABLE(VIDEO)
+    auto checkVideoElementOriginTaint = [this](const HTMLVideoElement& videoElement) -> std::optional<Exception> {
+        RefPtr context = scriptExecutionContext();
+        if (RefPtr securityOrigin = context ? context->securityOrigin() : nullptr; securityOrigin && videoElement.taintsOrigin(*securityOrigin))
+            return Exception { ExceptionCode::SecurityError, "GPUDevice.importExternalTexture: Cross origin external videos are not allowed in WebGPU"_s };
+        return std::nullopt;
+    };
+#endif
+
 #if ENABLE(VIDEO) && PLATFORM(COCOA)
     if (RefPtr externalTexture = externalTextureForDescriptor(externalTextureDescriptor)) {
         externalTexture->undestroy();
@@ -402,6 +412,9 @@ ExceptionOr<Ref<GPUExternalTexture>> GPUDevice::importExternalTexture(GPUExterna
 #else
         Ref videoElementRef = externalTextureDescriptor.source;
 #endif
+        if (auto exception = checkVideoElementOriginTaint(videoElementRef))
+            return WTF::move(*exception);
+
         m_videoElementToExternalTextureMap.remove(videoElementRef);
         if (auto optionalMediaIdentifier = externalTextureDescriptor.mediaIdentifier()) {
             m_backing->updateExternalTexture(externalTexture->backing(), *optionalMediaIdentifier);
@@ -425,6 +438,9 @@ ExceptionOr<Ref<GPUExternalTexture>> GPUDevice::importExternalTexture(GPUExterna
 #else
     Ref videoElementRef = externalTextureDescriptor.source;
 #endif
+    if (auto exception = checkVideoElementOriginTaint(videoElementRef))
+        return WTF::move(*exception);
+
     WeakPtr videoElementPtr = videoElementRef.ptr();
     m_videoElementToExternalTextureMap.set(videoElementRef, externalTexture.get());
     m_previouslyImportedExternalTexture.first = videoElementRef.ptr();
diff --git a/LayoutTests/fast/webgpu/regression/repro_315368-expected.txt b/LayoutTests/fast/webgpu/regression/repro_315368-expected.txt
new file mode 100644
index 000000000000..f3f8a6a97a34
--- /dev/null
+++ b/LayoutTests/fast/webgpu/regression/repro_315368-expected.txt
@@ -0,0 +1,2 @@
+CONSOLE MESSAGE: Pass
+
diff --git a/LayoutTests/fast/webgpu/regression/repro_315368.html b/LayoutTests/fast/webgpu/regression/repro_315368.html
new file mode 100644
index 000000000000..cecbedb144ce
--- /dev/null
+++ b/LayoutTests/fast/webgpu/regression/repro_315368.html
@@ -0,0 +1,29 @@
+<script>
+  globalThis.testRunner?.waitUntilDone();
+  const log = console.debug;
+
+  const veryBrightVideo = `data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAvG1kYXQAAAAfTgEFGkdWStxcTEM/lO/FETzRQ6gD7gAA7gIAA3EYgAAAAEgoAa8iNjAkszOL+e58c//cEe//0TT//scp1n/381P/RWP/zOW4QtxorfVogeh8nQDbQAAAAwAQMCcWUTAAAAMAAAMAAAMA84AAAAAVAgHQAyu+KT35E7gAADFgAAADABLQAAAAEgIB4AiS76MTkNbgAAF3AAAPSAAAABICAeAEn8+hBOTXYAADUgAAHRAAAAPibW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAAKcAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAw10cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAAKcAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAABAAAAAQAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAACnAAAAAAABAAAAAAKFbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAABdwAAAD6BVxAAAAAAAMWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABDb3JlIE1lZGlhIFZpZGVvAAAAAixtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAHsc3RibAAAARxzdHNkAAAAAAAAAAEAAAEMaHZjMQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAQABAASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAAHVodmNDAQIgAAAAsAAAAAAAPPAA/P36+gAACwOgAAEAGEABDAH//wIgAAADALAAAAMAAAMAPBXAkKEAAQAmQgEBAiAAAAMAsAAAAwAAAwA8oBQgQcCTDLYgV7kWVYC1CRAJAICiAAEACUQBwChkuNBTJAAAAApmaWVsAQAAAAATY29scm5jbHgACQAQAAkAAAAAEHBhc3AAAAABAAAAAQAAABRidHJ0AAAAAAAALPwAACz8AAAAKHN0dHMAAAAAAAAAAwAAAAIAAAPoAAAAAQAAAAEAAAABAAAD6AAAABRzdHNzAAAAAAAAAAEAAAABAAAAEHNkdHAAAAAAIBAQGAAAAChjdHRzAAAAAAAAAAMAAAABAAAAAAAAAAEAAAfQAAAAAgAAAAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAQAAAABAAAAJHN0c3oAAAAAAAAAAAAAAAQAAABvAAAAGQAAABYAAAAWAAAAFHN0Y28AAAAAAAAAAQAAACwAAABhdWR0YQAAAFltZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAAJKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYwLjMuMTAw`;
+
+  // Tests the new texture path in importExternalTexture: first import of a same-origin video
+  // element exercises the code path that creates a new GPUExternalTexture (bug 315368).
+  onload = async () => {
+    let adapter = await navigator.gpu.requestAdapter({});
+    let device = await adapter.requestDevice({});
+
+    let video = document.createElement('video');
+    video.src = veryBrightVideo;
+    video.muted = true;
+    await new Promise(resolve => { video.onloadeddata = () => { video.play(); resolve(); }; });
+
+    video.requestVideoFrameCallback(() => {
+      try {
+        device.importExternalTexture({source: video});
+        log('Pass');
+      } catch (e) {
+        log('FAIL: ' + e.message);
+      }
+      globalThis.testRunner?.dumpAsText();
+      globalThis.testRunner?.notifyDone();
+    });
+  };
+</script>
diff --git a/LayoutTests/fast/webgpu/regression/repro_315368b-expected.txt b/LayoutTests/fast/webgpu/regression/repro_315368b-expected.txt
new file mode 100644
index 000000000000..f3f8a6a97a34
--- /dev/null
+++ b/LayoutTests/fast/webgpu/regression/repro_315368b-expected.txt
@@ -0,0 +1,2 @@
+CONSOLE MESSAGE: Pass
+
diff --git a/LayoutTests/fast/webgpu/regression/repro_315368b.html b/LayoutTests/fast/webgpu/regression/repro_315368b.html
new file mode 100644
index 000000000000..b5efaa958908
--- /dev/null
+++ b/LayoutTests/fast/webgpu/regression/repro_315368b.html
@@ -0,0 +1,31 @@
+<script>
+  globalThis.testRunner?.waitUntilDone();
+  const log = console.debug;
+
+  const veryBrightVideo = `data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAvG1kYXQAAAAfTgEFGkdWStxcTEM/lO/FETzRQ6gD7gAA7gIAA3EYgAAAAEgoAa8iNjAkszOL+e58c//cEe//0TT//scp1n/381P/RWP/zOW4QtxorfVogeh8nQDbQAAAAwAQMCcWUTAAAAMAAAMAAAMA84AAAAAVAgHQAyu+KT35E7gAADFgAAADABLQAAAAEgIB4AiS76MTkNbgAAF3AAAPSAAAABICAeAEn8+hBOTXYAADUgAAHRAAAAPibW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAAKcAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAw10cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAAKcAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAABAAAAAQAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAACnAAAAAAABAAAAAAKFbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAABdwAAAD6BVxAAAAAAAMWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABDb3JlIE1lZGlhIFZpZGVvAAAAAixtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAHsc3RibAAAARxzdHNkAAAAAAAAAAEAAAEMaHZjMQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAQABAASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAAHVodmNDAQIgAAAAsAAAAAAAPPAA/P36+gAACwOgAAEAGEABDAH//wIgAAADALAAAAMAAAMAPBXAkKEAAQAmQgEBAiAAAAMAsAAAAwAAAwA8oBQgQcCTDLYgV7kWVYC1CRAJAICiAAEACUQBwChkuNBTJAAAAApmaWVsAQAAAAATY29scm5jbHgACQAQAAkAAAAAEHBhc3AAAAABAAAAAQAAABRidHJ0AAAAAAAALPwAACz8AAAAKHN0dHMAAAAAAAAAAwAAAAIAAAPoAAAAAQAAAAEAAAABAAAD6AAAABRzdHNzAAAAAAAAAAEAAAABAAAAEHNkdHAAAAAAIBAQGAAAAChjdHRzAAAAAAAAAAMAAAABAAAAAAAAAAEAAAfQAAAAAgAAAAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAQAAAABAAAAJHN0c3oAAAAAAAAAAAAAAAQAAABvAAAAGQAAABYAAAAWAAAAFHN0Y28AAAAAAAAAAQAAACwAAABhdWR0YQAAAFltZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAAJKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYwLjMuMTAw`;
+
+  // Tests the cached texture path in importExternalTexture: a second synchronous import of the
+  // same same-origin video element exercises the early-return path that reuses a cached
+  // GPUExternalTexture (bug 315368, PLATFORM(COCOA) only).
+  onload = async () => {
+    let adapter = await navigator.gpu.requestAdapter({});
+    let device = await adapter.requestDevice({});
+
+    let video = document.createElement('video');
+    video.src = veryBrightVideo;
+    video.muted = true;
+    await new Promise(resolve => { video.onloadeddata = () => { video.play(); resolve(); }; });
+
+    video.requestVideoFrameCallback(() => {
+      try {
+        let et1 = device.importExternalTexture({source: video}); // new texture path
+        let et2 = device.importExternalTexture({source: video}); // cached texture path (Cocoa)
+        log('Pass');
+      } catch (e) {
+        log('FAIL: ' + e.message);
+      }
+      globalThis.testRunner?.dumpAsText();
+      globalThis.testRunner?.notifyDone();
+    });
+  };
+</script>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/LayoutTests/fast/webgpu/regression/repro_315368-expected.txt b/LayoutTests/fast/webgpu/regression/repro_315368-expected.txt
new file mode 100644
index 000000000000..f3f8a6a97a34
--- /dev/null
+++ b/LayoutTests/fast/webgpu/regression/repro_315368-expected.txt
@@ -0,0 +1,2 @@
+CONSOLE MESSAGE: Pass
+
diff --git a/LayoutTests/fast/webgpu/regression/repro_315368.html b/LayoutTests/fast/webgpu/regression/repro_315368.html
new file mode 100644
index 000000000000..cecbedb144ce
--- /dev/null
+++ b/LayoutTests/fast/webgpu/regression/repro_315368.html
@@ -0,0 +1,29 @@
+<script>
+  globalThis.testRunner?.waitUntilDone();
+  const log = console.debug;
+
+  const veryBrightVideo = `data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAvG1kYXQAAAAfTgEFGkdWStxcTEM/lO/FETzRQ6gD7gAA7gIAA3EYgAAAAEgoAa8iNjAkszOL+e58c//cEe//0TT//scp1n/381P/RWP/zOW4QtxorfVogeh8nQDbQAAAAwAQMCcWUTAAAAMAAAMAAAMA84AAAAAVAgHQAyu+KT35E7gAADFgAAADABLQAAAAEgIB4AiS76MTkNbgAAF3AAAPSAAAABICAeAEn8+hBOTXYAADUgAAHRAAAAPibW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAAKcAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAw10cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAAKcAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAABAAAAAQAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAACnAAAAAAABAAAAAAKFbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAABdwAAAD6BVxAAAAAAAMWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABDb3JlIE1lZGlhIFZpZGVvAAAAAixtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAHsc3RibAAAARxzdHNkAAAAAAAAAAEAAAEMaHZjMQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAQABAASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAAHVodmNDAQIgAAAAsAAAAAAAPPAA/P36+gAACwOgAAEAGEABDAH//wIgAAADALAAAAMAAAMAPBXAkKEAAQAmQgEBAiAAAAMAsAAAAwAAAwA8oBQgQcCTDLYgV7kWVYC1CRAJAICiAAEACUQBwChkuNBTJAAAAApmaWVsAQAAAAATY29scm5jbHgACQAQAAkAAAAAEHBhc3AAAAABAAAAAQAAABRidHJ0AAAAAAAALPwAACz8AAAAKHN0dHMAAAAAAAAAAwAAAAIAAAPoAAAAAQAAAAEAAAABAAAD6AAAABRzdHNzAAAAAAAAAAEAAAABAAAAEHNkdHAAAAAAIBAQGAAAAChjdHRzAAAAAAAAAAMAAAABAAAAAAAAAAEAAAfQAAAAAgAAAAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAQAAAABAAAAJHN0c3oAAAAAAAAAAAAAAAQAAABvAAAAGQAAABYAAAAWAAAAFHN0Y28AAAAAAAAAAQAAACwAAABhdWR0YQAAAFltZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAAJKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYwLjMuMTAw`;
+
+  // Tests the new texture path in importExternalTexture: first import of a same-origin video
+  // element exercises the code path that creates a new GPUExternalTexture (bug 315368).
+  onload = async () => {
+    let adapter = await navigator.gpu.requestAdapter({});
+    let device = await adapter.requestDevice({});
+
+    let video = document.createElement('video');
+    video.src = veryBrightVideo;
+    video.muted = true;
+    await new Promise(resolve => { video.onloadeddata = () => { video.play(); resolve(); }; });
+
+    video.requestVideoFrameCallback(() => {
+      try {
+        device.importExternalTexture({source: video});
+        log('Pass');
+      } catch (e) {
+        log('FAIL: ' + e.message);
+      }
+      globalThis.testRunner?.dumpAsText();
+      globalThis.testRunner?.notifyDone();
+    });
+  };
+</script>
diff --git a/LayoutTests/fast/webgpu/regression/repro_315368b-expected.txt b/LayoutTests/fast/webgpu/regression/repro_315368b-expected.txt
new file mode 100644
index 000000000000..f3f8a6a97a34
--- /dev/null
+++ b/LayoutTests/fast/webgpu/regression/repro_315368b-expected.txt
@@ -0,0 +1,2 @@
+CONSOLE MESSAGE: Pass
+
diff --git a/LayoutTests/fast/webgpu/regression/repro_315368b.html b/LayoutTests/fast/webgpu/regression/repro_315368b.html
new file mode 100644
index 000000000000..b5efaa958908
--- /dev/null
+++ b/LayoutTests/fast/webgpu/regression/repro_315368b.html
@@ -0,0 +1,31 @@
+<script>
+  globalThis.testRunner?.waitUntilDone();
+  const log = console.debug;
+
+  const veryBrightVideo = `data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAvG1kYXQAAAAfTgEFGkdWStxcTEM/lO/FETzRQ6gD7gAA7gIAA3EYgAAAAEgoAa8iNjAkszOL+e58c//cEe//0TT//scp1n/381P/RWP/zOW4QtxorfVogeh8nQDbQAAAAwAQMCcWUTAAAAMAAAMAAAMA84AAAAAVAgHQAyu+KT35E7gAADFgAAADABLQAAAAEgIB4AiS76MTkNbgAAF3AAAPSAAAABICAeAEn8+hBOTXYAADUgAAHRAAAAPibW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAAKcAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAw10cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAAKcAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAABAAAAAQAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAACnAAAAAAABAAAAAAKFbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAABdwAAAD6BVxAAAAAAAMWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABDb3JlIE1lZGlhIFZpZGVvAAAAAixtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAHsc3RibAAAARxzdHNkAAAAAAAAAAEAAAEMaHZjMQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAQABAASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAAHVodmNDAQIgAAAAsAAAAAAAPPAA/P36+gAACwOgAAEAGEABDAH//wIgAAADALAAAAMAAAMAPBXAkKEAAQAmQgEBAiAAAAMAsAAAAwAAAwA8oBQgQcCTDLYgV7kWVYC1CRAJAICiAAEACUQBwChkuNBTJAAAAApmaWVsAQAAAAATY29scm5jbHgACQAQAAkAAAAAEHBhc3AAAAABAAAAAQAAABRidHJ0AAAAAAAALPwAACz8AAAAKHN0dHMAAAAAAAAAAwAAAAIAAAPoAAAAAQAAAAEAAAABAAAD6AAAABRzdHNzAAAAAAAAAAEAAAABAAAAEHNkdHAAAAAAIBAQGAAAAChjdHRzAAAAAAAAAAMAAAABAAAAAAAAAAEAAAfQAAAAAgAAAAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAQAAAABAAAAJHN0c3oAAAAAAAAAAAAAAAQAAABvAAAAGQAAABYAAAAWAAAAFHN0Y28AAAAAAAAAAQAAACwAAABhdWR0YQAAAFltZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAAJKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYwLjMuMTAw`;
+
+  // Tests the cached texture path in importExternalTexture: a second synchronous import of the
+  // same same-origin video element exercises the early-return path that reuses a cached
+  // GPUExternalTexture (bug 315368, PLATFORM(COCOA) only).
+  onload = async () => {
+    let adapter = await navigator.gpu.requestAdapter({});
+    let device = await adapter.requestDevice({});
+
+    let video = document.createElement('video');
+    video.src = veryBrightVideo;
+    video.muted = true;
+    await new Promise(resolve => { video.onloadeddata = () => { video.play(); resolve(); }; });
+
+    video.requestVideoFrameCallback(() => {
+      try {
+        let et1 = device.importExternalTexture({source: video}); // new texture path
+        let et2 = device.importExternalTexture({source: video}); // cached texture path (Cocoa)
+        log('Pass');
+      } catch (e) {
+        log('FAIL: ' + e.message);
+      }
+      globalThis.testRunner?.dumpAsText();
+      globalThis.testRunner?.notifyDone();
+    });
+  };
+</script>
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker.