diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations index 792bb271d5a21..38ad642940b0e 100644 --- a/LayoutTests/TestExpectations +++ b/LayoutTests/TestExpectations @@ -1311,6 +1311,10 @@ http/wpt/offscreen-canvas/transferToImageBitmap-webgl.html [ ImageOnlyFailure Fa imported/w3c/web-platform-tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html [ Failure Pass ] imported/w3c/web-platform-tests/html/canvas/offscreen/the-offscreen-canvas/size.large.html [ Skip ] +# MSE in worker is only supported on platform with the GPU process active and where MSE is enabled by default +# (this excludes iPhone (no MediaSource) and Linux/Windows (no GPUP)) +imported/w3c/web-platform-tests/media-source/dedicated-worker [ Skip ] + # Tests a setting behind the ENABLE_FULLSCREEN_API flag, which is WK2-only media/video-supports-fullscreen.html [ Skip ] diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-message-util.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-message-util.js new file mode 100644 index 0000000000000..c62eb8e3f7dc9 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-message-util.js @@ -0,0 +1,16 @@ +// This script provides an object with common message subjects to assist main +// and worker thread communication. + +const messageSubject = { + ERROR: "error", // info field may contain more detail + OBJECT_URL: "object url", // info field contains object URL + HANDLE: "handle", // info field contains the MediaSourceHandle + STARTED_BUFFERING: "started buffering", + FINISHED_BUFFERING: "finished buffering", + VERIFY_DURATION: "verify duration", // info field contains expected duration + AWAIT_DURATION: "await duration", // wait for element duration to match the expected duration in the info field + VERIFY_HAVE_NOTHING: "verify have nothing readyState", + VERIFY_AT_LEAST_HAVE_METADATA: "verify readyState is at least HAVE_METADATA", + ACK_VERIFIED: "verified", // info field contains the message values that requested the verification + WORKER_DONE: "worker done", // this lets worker signal main to successfully end the test +}; diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element-expected.txt new file mode 100644 index 0000000000000..bb9add98faa48 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element-expected.txt @@ -0,0 +1,18 @@ + + +PASS Test element detachment from worker MediaSource after at least 0 main thread setTimeouts, starting counting after setting srcObject +PASS Test element detachment from worker MediaSource after at least 1 main thread setTimeouts, starting counting after setting srcObject +PASS Test element detachment from worker MediaSource after at least 2 main thread setTimeouts, starting counting after setting srcObject +PASS Test element detachment from worker MediaSource after at least 3 main thread setTimeouts, starting counting after setting srcObject +PASS Test element detachment from worker MediaSource after at least 4 main thread setTimeouts, starting counting after setting srcObject +PASS Test element detachment from worker MediaSource after at least 0 main thread setTimeouts, starting counting after receiving Started Buffering message from worker +PASS Test element detachment from worker MediaSource after at least 1 main thread setTimeouts, starting counting after receiving Started Buffering message from worker +PASS Test element detachment from worker MediaSource after at least 2 main thread setTimeouts, starting counting after receiving Started Buffering message from worker +PASS Test element detachment from worker MediaSource after at least 3 main thread setTimeouts, starting counting after receiving Started Buffering message from worker +PASS Test element detachment from worker MediaSource after at least 4 main thread setTimeouts, starting counting after receiving Started Buffering message from worker +PASS Test element detachment from worker MediaSource after at least 0 main thread setTimeouts, starting counting after receiving Finished Buffering message from worker +PASS Test element detachment from worker MediaSource after at least 1 main thread setTimeouts, starting counting after receiving Finished Buffering message from worker +PASS Test element detachment from worker MediaSource after at least 2 main thread setTimeouts, starting counting after receiving Finished Buffering message from worker +PASS Test element detachment from worker MediaSource after at least 3 main thread setTimeouts, starting counting after receiving Finished Buffering message from worker +PASS Test element detachment from worker MediaSource after at least 4 main thread setTimeouts, starting counting after receiving Finished Buffering message from worker + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element.html b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element.html new file mode 100644 index 0000000000000..0f74d953723a4 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element.html @@ -0,0 +1,75 @@ + + +MediaSource-in-Worker buffering test case with media element detachment at various places + + + + + + + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element.js new file mode 100644 index 0000000000000..54b1d815f2529 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element.js @@ -0,0 +1,79 @@ +// This is similar to mediasource-worker-play.js, except that the buffering is +// longer and done in tiny chunks to enable a better chance of the main thread +// detaching the element while interesting buffering work is still occurring. To +// assist the main thread understanding when the buffering has started already +// or has completed already, we also perform extra messaging. +importScripts("mediasource-worker-util.js"); + +onmessage = function(evt) { + postMessage({ subject: messageSubject.ERROR, info: "No message expected by Worker" }); +}; + +let util = new MediaSourceWorkerUtil(); + +let sentStartedBufferingMessage = false; + +util.mediaSource.addEventListener("sourceopen", () => { + let sourceBuffer; + try { + sourceBuffer = util.mediaSource.addSourceBuffer(util.mediaMetadata.type); + } catch(e) { + // Detachment may have already begun, so allow exception here. + // TODO(https://crbug.com/878133): Consider a distinct readyState for the case + // where exception occurs due to "Worker MediaSource attachment is closing". + // That would assist API users and narrow the exception handling here. + return; + } + + sourceBuffer.onerror = (err) => { + postMessage({ subject: messageSubject.ERROR, info: err }); + }; + util.mediaLoadPromise.then(mediaData => bufferInto(sourceBuffer, mediaData, 100, 0), + err => { postMessage({ subject: messageSubject.ERROR, info: err }) } ); +}, { once : true }); + +let handle = util.mediaSource.handle; + +postMessage({ subject: messageSubject.HANDLE, info: handle }, { transfer: [handle] } ); + +// Append increasingly large pieces at a time, starting/continuing at |position|. +// This allows buffering the test media without timeout, but also with enough +// operations to gain coverage on detachment concurrency with append. +function bufferInto(sourceBuffer, mediaData, appendSize, position) { + if (position >= mediaData.byteLength) { + postMessage({ subject: messageSubject.FINISHED_BUFFERING }); + try { + util.mediaSource.endOfStream(); + } catch(e) { + // Detachment may have already begun, so allow exception here. + // TODO(https://crbug.com/878133): Consider a distinct readyState for the case + // where exception occurs due to "Worker MediaSource attachment is closing". + // That would assist API users and narrow the exception handling here. + // FALL-THROUGH - return. + } + return; + } + + var nextPosition = position + appendSize; + const pieceToAppend = mediaData.slice(position, nextPosition); + position = nextPosition; + appendSize += 100; + + sourceBuffer.addEventListener("updateend", () => { + if (!sentStartedBufferingMessage) { + postMessage({ subject: messageSubject.STARTED_BUFFERING}); + sentStartedBufferingMessage = true; + } + bufferInto(sourceBuffer, mediaData, appendSize, position); + }, { once : true }); + + try { + sourceBuffer.appendBuffer(pieceToAppend); + } catch(e) { + // Detachment may have already begun, so allow exception here. + // TODO(https://crbug.com/878133): Consider a distinct readyState for the case + // where exception occurs due to "Worker MediaSource attachment is closing". + // That would assist API users and narrow the exception handling here. + // FALL-THROUGH - return. + } +} diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration-expected.txt new file mode 100644 index 0000000000000..3ec2eb68a3881 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration-expected.txt @@ -0,0 +1,4 @@ + + +PASS Test worker MediaSource duration updates before and after HAVE_METADATA + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration.html b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration.html new file mode 100644 index 0000000000000..c195775bebb23 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration.html @@ -0,0 +1,86 @@ + + +Test MediaSource-in-Worker duration updates before and after HAVE_METADATA + + + + + + + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration.js new file mode 100644 index 0000000000000..2a2c7bac0b279 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration.js @@ -0,0 +1,290 @@ +importScripts("mediasource-worker-util.js"); + +// Note, we do not use testharness.js utilities within the worker context +// because it also communicates using postMessage to the main HTML document's +// harness, and would confuse the test case message parsing there. + +let util = new MediaSourceWorkerUtil(); +let sourceBuffer; + +// Phases of this test case, in sequence: +const testPhase = { + // Main thread verifies initial unattached HTMLMediaElement duration is NaN + // and readyState is HAVE_NOTHING, then starts this worker. + // This worker creates a MediaSource, verifies its initial duration + // is NaN, creates an object URL for the MediaSource and sends the URL to the + // main thread. + kInitial: "Initial", + + // Main thread receives MediaSourceHandle, re-verifies that the media element + // duration is still NaN and readyState is still HAVE_NOTHING, and then sets + // the handle as the srcObject of the media element, eventually causing worker + // mediaSource 'onsourceopen' event dispatch. + kAttaching: "Awaiting sourceopen event that signals attachment is setup", + + kRequestNaNDurationCheck: + "Sending request to main thread to verify expected duration of the freshly setup attachment", + kConfirmNaNDurationResult: + "Checking that main thread correctly ACK'ed the freshly setup attachment's duration verification request", + + kRequestHaveNothingReadyStateCheck: + "Sending request to main thread to verify expected readyState of HAVE_NOTHING of the freshly setup attachment", + kConfirmHaveNothingReadyStateResult: + "Checking that main thread correctly ACK'ed the freshly setup attachment's readyState HAVE_NOTHING verification request", + + kRequestSetDurationCheck: + "Sending request to main thread to verify explicitly set duration before any media data has been appended", + kConfirmSetDurationResult: + "Checking that main thread correctly ACK'ed the duration verification request of explicitly set duration before any media data has been appended", + + kRequestHaveNothingReadyStateRecheck: + "Sending request to main thread to recheck that the readyState is still HAVE_NOTHING", + kConfirmHaveNothingReadyStateRecheckResult: + "Checking that main thread correctly ACK'ed the request to recheck readyState of HAVE_NOTHING", + + kRequestAwaitNewDurationCheck: + "Buffering media and then sending request to main thread to await duration reaching the expected value due to buffering", + kConfirmAwaitNewDurationResult: + "Checking that main thread correctly ACK'ed the request to await duration reaching the expected value due to buffering", + + kRequestAtLeastHaveMetadataReadyStateCheck: + "Sending request to main thread to verify expected readyState of at least HAVE_METADATA due to buffering", + kConfirmAtLeastHaveMetadataReadyStateResult: + "Checking that main thread correctly ACK'ed the request to verify expected readyState of at least HAVE_METADATA due to buffering", + +}; + +let phase = testPhase.kInitial; + +// Setup handler for receipt of attachment completion. +util.mediaSource.addEventListener("sourceopen", () => { + assert(phase === testPhase.kAttaching, "Unexpected sourceopen received by Worker mediaSource."); + phase = testPhase.kRequestNaNDurationCheck; + processPhase(); +}, { once : true }); + +// Setup handler for receipt of acknowledgement of successful verifications from +// main thread. |ackVerificationData| contains the round-tripped verification +// request that the main thread just sent, and is used in processPhase to ensure +// the ACK for this phase matched the request for verification. +let ackVerificationData; +onmessage = e => { + if (e.data === undefined || e.data.subject !== messageSubject.ACK_VERIFIED || e.data.info === undefined) { + postMessage({ + subject: messageSubject.ERROR, + info: "Invalid message received by Worker" + }); + return; + } + + ackVerificationData = e.data.info; + processPhase(/* isResponseToAck */ true); +}; + +processPhase(); + + +// Returns true if checks succeed, false otherwise. +function checkAckVerificationData(expectedRequest) { + + // Compares only subject and info fields. Uses logic similar to testharness.js's + // same_value(x,y) to correctly handle NaN, but doesn't distinguish +0 from -0. + function messageValuesEqual(m1, m2) { + if (m1.subject !== m1.subject) { + // NaN case + if (m2.subject === m2.subject) + return false; + } else if (m1.subject !== m2.subject) { + return false; + } + + if (m1.info !== m1.info) { + // NaN case + return (m2.info !== m2.info); + } + + return m1.info === m2.info; + } + + if (messageValuesEqual(expectedRequest, ackVerificationData)) { + ackVerificationData = undefined; + return true; + } + + postMessage({ + subject: messageSubject.ERROR, + info: "ACK_VERIFIED message from main thread was for a mismatching request for this phase. phase=[" + phase + + "], expected request that would produce ACK in this phase=[" + JSON.stringify(expectedRequest) + + "], actual request reported with the ACK=[" + JSON.stringify(ackVerificationData) + "]" + }); + + ackVerificationData = undefined; + return false; +} + +function bufferMediaAndSendDurationVerificationRequest() { + sourceBuffer = util.mediaSource.addSourceBuffer(util.mediaMetadata.type); + sourceBuffer.onerror = (err) => { + postMessage({ subject: messageSubject.ERROR, info: err }); + }; + sourceBuffer.onupdateend = () => { + // Sanity check the duration. + // Unnecessary for this buffering, except helps with test coverage. + var duration = util.mediaSource.duration; + if (isNaN(duration) || duration <= 0.0) { + postMessage({ + subject: messageSubject.ERROR, + info: "mediaSource.duration " + duration + " is not within expected range (0,1)" + }); + return; + } + + // Await the main thread media element duration matching the worker + // mediaSource duration. + postMessage(getAwaitCurrentDurationRequest()); + }; + + util.mediaLoadPromise.then(mediaData => { sourceBuffer.appendBuffer(mediaData); }, + err => { postMessage({ subject: messageSubject.ERROR, info: err }) }); +} + + +function getAwaitCurrentDurationRequest() { + // Sanity check that we have a numeric duration value now. + const dur = util.mediaSource.duration; + assert(!Number.isNaN(dur), "Unexpected NaN duration in worker"); + return { subject: messageSubject.AWAIT_DURATION, info: dur }; +} + +function assert(conditionBool, description) { + if (conditionBool !== true) { + postMessage({ + subject: messageSubject.ERROR, + info: "Current test phase [" + phase + "] failed worker assertion. " + description + }); + } +} + +function processPhase(isResponseToAck = false) { + assert(!isResponseToAck || (phase !== testPhase.kInitial && phase !== testPhase.kAttaching), + "Phase does not expect verification ack receipt from main thread"); + + // Some static request messages useful in transmission and ACK verification. + const nanDurationCheckRequest = { subject: messageSubject.VERIFY_DURATION, info: NaN }; + const haveNothingReadyStateCheckRequest = { subject: messageSubject.VERIFY_HAVE_NOTHING }; + const setDurationCheckRequest = { subject: messageSubject.AWAIT_DURATION, info: 0.1 }; + const atLeastHaveMetadataReadyStateCheckRequest = { subject: messageSubject.VERIFY_AT_LEAST_HAVE_METADATA }; + + switch (phase) { + + case testPhase.kInitial: + assert(Number.isNaN(util.mediaSource.duration), "Initial unattached MediaSource duration must be NaN, but instead is " + util.mediaSource.duration); + phase = testPhase.kAttaching; + let handle = util.mediaSource.handle; + postMessage({ subject: messageSubject.HANDLE, info: handle }, { transfer: [handle] } ); + break; + + case testPhase.kAttaching: + postMessage({ + subject: messageSubject.ERROR, + info: "kAttaching phase is handled by main thread and by worker onsourceopen, not this switch case." + }); + break; + + case testPhase.kRequestNaNDurationCheck: + assert(!isResponseToAck); + postMessage(nanDurationCheckRequest); + phase = testPhase.kConfirmNaNDurationResult; + break; + + case testPhase.kConfirmNaNDurationResult: + assert(isResponseToAck); + if (checkAckVerificationData(nanDurationCheckRequest)) { + phase = testPhase.kRequestHaveNothingReadyStateCheck; + processPhase(); + } + break; + + case testPhase.kRequestHaveNothingReadyStateCheck: + assert(!isResponseToAck); + postMessage(haveNothingReadyStateCheckRequest); + phase = testPhase.kConfirmHaveNothingReadyStateResult; + break; + + case testPhase.kConfirmHaveNothingReadyStateResult: + assert(isResponseToAck); + if (checkAckVerificationData(haveNothingReadyStateCheckRequest)) { + phase = testPhase.kRequestSetDurationCheck; + processPhase(); + } + break; + + case testPhase.kRequestSetDurationCheck: + assert(!isResponseToAck); + const newDuration = setDurationCheckRequest.info; + assert(!Number.isNaN(newDuration) && newDuration > 0); + + // Set the duration, then request verification. + util.mediaSource.duration = newDuration; + postMessage(setDurationCheckRequest); + phase = testPhase.kConfirmSetDurationResult; + break; + + case testPhase.kConfirmSetDurationResult: + assert(isResponseToAck); + if (checkAckVerificationData(setDurationCheckRequest)) { + phase = testPhase.kRequestHaveNothingReadyStateRecheck; + processPhase(); + } + break; + + case testPhase.kRequestHaveNothingReadyStateRecheck: + assert(!isResponseToAck); + postMessage(haveNothingReadyStateCheckRequest); + phase = testPhase.kConfirmHaveNothingReadyStateRecheckResult; + break; + + case testPhase.kConfirmHaveNothingReadyStateRecheckResult: + assert(isResponseToAck); + if (checkAckVerificationData(haveNothingReadyStateCheckRequest)) { + phase = testPhase.kRequestAwaitNewDurationCheck; + processPhase(); + } + break; + + case testPhase.kRequestAwaitNewDurationCheck: + assert(!isResponseToAck); + bufferMediaAndSendDurationVerificationRequest(); + phase = testPhase.kConfirmAwaitNewDurationResult; + break; + + case testPhase.kConfirmAwaitNewDurationResult: + assert(isResponseToAck); + if (checkAckVerificationData(getAwaitCurrentDurationRequest())) { + phase = testPhase.kRequestAtLeastHaveMetadataReadyStateCheck; + processPhase(); + } + break; + + case testPhase.kRequestAtLeastHaveMetadataReadyStateCheck: + assert(!isResponseToAck); + postMessage(atLeastHaveMetadataReadyStateCheckRequest); + phase = testPhase.kConfirmAtLeastHaveMetadataReadyStateResult; + break; + + case testPhase.kConfirmAtLeastHaveMetadataReadyStateResult: + assert(isResponseToAck); + if (checkAckVerificationData(atLeastHaveMetadataReadyStateCheckRequest)) { + postMessage({ subject: messageSubject.WORKER_DONE }); + } + phase = "No further phase processing should occur once WORKER_DONE message has been sent"; + break; + + default: + postMessage({ + subject: messageSubject.ERROR, + info: "Unexpected test phase in worker:" + phase, + }); + } + +} diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-get-objecturl.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-get-objecturl.js new file mode 100644 index 0000000000000..e9a5af6c81295 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-get-objecturl.js @@ -0,0 +1,13 @@ +importScripts("mediasource-worker-util.js"); + +// Note, we do not use testharness.js utilities within the worker context +// because it also communicates using postMessage to the main HTML document's +// harness, and would confuse the test case message parsing there. + +onmessage = function(evt) { + postMessage({ subject: messageSubject.ERROR, info: "No message expected by Worker"}); +}; + +let util = new MediaSourceWorkerUtil(); + +postMessage({ subject: messageSubject.OBJECT_URL, info: URL.createObjectURL(util.mediaSource) }); diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-expected.txt new file mode 100644 index 0000000000000..34807810723d4 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-expected.txt @@ -0,0 +1,11 @@ + +PASS Test main context receipt of postMessage'd MediaSourceHandle from DedicatedWorker MediaSource +PASS Test main-thread has MediaSourceHandle defined +PASS Test main-thread MediaSource does not have handle getter +PASS MediaSource in DedicatedWorker context must have true-valued canConstructInDedicatedWorker if Window context had it +PASS MediaSource prototype in DedicatedWorker context must have 'handle', and worker must have MediaSourceHandle +PASS MediaSource construction succeeds with initial closed readyState in DedicatedWorker +PASS mediaSource.handle in DedicatedWorker returns a MediaSourceHandle +PASS mediaSource.handle observes SameObject property correctly +PASS Attempt to set MediaSource handle property should fail to change it, since it is readonly + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-expected.txt new file mode 100644 index 0000000000000..67d54ad5e0d23 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-expected.txt @@ -0,0 +1,16 @@ + + +Harness Error (TIMEOUT), message = null + +PASS MediaSourceHandle serialization without transfer must fail, tested in window context +PASS Same MediaSourceHandle transferred multiple times in single postMessage must fail, tested in window context +PASS Attempt to transfer detached MediaSourceHandle must fail, tested in window context +FAIL MediaSourceHandle cannot be transferred, immediately after set as srcObject, even if srcObject immediately reset to null assert_throws_dom: transferring handle that is currently srcObject fails function "function() { + worker.postMessage(handle, [handle]); + }" did not throw +PASS MediaSourceHandle cannot be transferred, if it was srcObject when asynchronous load starts (loadstart), even if srcObject is then immediately reset to null +TIMEOUT A detached (already transferred away) MediaSourceHandle cannot successfully load when assigned to srcObject Test timed out +NOTRUN Precisely one load of the same MediaSourceHandle assigned synchronously to multiple media element srcObjects succeeds +PASS MediaSourceHandle serialization without transfer must fail, tested in worker +PASS Same MediaSourceHandle transferred multiple times in single postMessage must fail, tested in worker + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-to-main.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-to-main.js new file mode 100644 index 0000000000000..15cccb1a0e3f7 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-to-main.js @@ -0,0 +1,10 @@ +importScripts('mediasource-message-util.js'); + +// Note, we do not use testharness.js utilities within the worker context +// because it also communicates using postMessage to the main HTML document's +// harness, and would confuse the test case message parsing there. + +// Just obtain a MediaSourceHandle and transfer it to creator of our context. +let handle = new MediaSource().handle; +postMessage( + {subject: messageSubject.HANDLE, info: handle}, {transfer: [handle]}); diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.html b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.html new file mode 100644 index 0000000000000..2db71c049d8b4 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.html @@ -0,0 +1,316 @@ + + +Test MediaSourceHandle transfer characteristics + + + + + + + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.js new file mode 100644 index 0000000000000..803da44e23c23 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.js @@ -0,0 +1,19 @@ +importScripts('/resources/testharness.js'); + +test(t => { + let handle = new MediaSource().handle; + assert_true(handle instanceof MediaSourceHandle); + assert_throws_dom('DataCloneError', function() { + postMessage(handle); + }, 'serializing handle without transfer'); +}, 'MediaSourceHandle serialization without transfer must fail, tested in worker'); + +test(t => { + let handle = new MediaSource().handle; + assert_true(handle instanceof MediaSourceHandle); + assert_throws_dom('DataCloneError', function() { + postMessage(handle, [handle, handle]); + }, 'transferring same handle more than once in same postMessage'); +}, 'Same MediaSourceHandle transferred multiple times in single postMessage must fail, tested in worker'); + +done(); diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle.html b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle.html new file mode 100644 index 0000000000000..6129e05ffb4a8 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle.html @@ -0,0 +1,61 @@ + + +Test MediaSource object and handle creation, with MediaSource in dedicated worker + + + + + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle.js new file mode 100644 index 0000000000000..d35cb877c2a73 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle.js @@ -0,0 +1,70 @@ +importScripts("/resources/testharness.js"); + +test(t => { + // The Window test html conditionally fetches and runs these tests only if the + // implementation exposes a true-valued static canConstructInDedicatedWorker + // attribute on MediaSource in the Window context. So, the implementation must + // agree on support here in the dedicated worker context. + + // Ensure we're executing in a dedicated worker context. + assert_true(self instanceof DedicatedWorkerGlobalScope, "self instanceof DedicatedWorkerGlobalScope"); + assert_true(MediaSource.hasOwnProperty("canConstructInDedicatedWorker", "DedicatedWorker MediaSource hasOwnProperty 'canConstructInDedicatedWorker'")); + assert_true(MediaSource.canConstructInDedicatedWorker, "DedicatedWorker MediaSource.canConstructInDedicatedWorker"); +}, "MediaSource in DedicatedWorker context must have true-valued canConstructInDedicatedWorker if Window context had it"); + +test(t => { + assert_true( + 'handle' in MediaSource.prototype, + 'dedicated worker MediaSource must have handle in prototype'); + assert_true(self.hasOwnProperty("MediaSourceHandle"), "dedicated worker must have MediaSourceHandle visibility"); +}, 'MediaSource prototype in DedicatedWorker context must have \'handle\', and worker must have MediaSourceHandle'); + +test(t => { + const ms = new MediaSource(); + assert_equals(ms.readyState, "closed"); +}, "MediaSource construction succeeds with initial closed readyState in DedicatedWorker"); + +test(t => { + const ms = new MediaSource(); + const handle = ms.handle; + assert_not_equals(handle, null, 'must have a non-null \'handle\' attribute'); + assert_true(handle instanceof MediaSourceHandle, "must be a MediaSourceHandle"); +}, 'mediaSource.handle in DedicatedWorker returns a MediaSourceHandle'); + +test(t => { + const msA = new MediaSource(); + const msB = new MediaSource(); + const handleA1 = msA.handle; + const handleA2 = msA.handle; + const handleA3 = msA['handle']; + const handleB1 = msB.handle; + const handleB2 = msB.handle; + assert_true( + handleA1 === handleA2 && handleB1 === handleB2 && handleA1 != handleB1, + 'SameObject is observed for mediaSource.handle, and different MediaSource instances have different handles'); + assert_true( + handleA1 === handleA3, + 'SameObject is observed even when accessing handle differently'); + assert_true( + handleA1 instanceof MediaSourceHandle && + handleB1 instanceof MediaSourceHandle, + 'handle property returns MediaSourceHandles'); +}, 'mediaSource.handle observes SameObject property correctly'); + +test(t => { + const ms1 = new MediaSource(); + const handle1 = ms1.handle; + const ms2 = new MediaSource(); + const handle2 = ms2.handle; + assert_true( + handle1 !== handle2, + 'distinct MediaSource instances must have distinct handles'); + + // Verify attempt to change value of the handle property does not succeed. + ms1.handle = handle2; + assert_true( + ms1.handle === handle1 && ms2.handle === handle2, + 'MediaSource handle is readonly, so should not have changed'); +}, 'Attempt to set MediaSource handle property should fail to change it, since it is readonly'); + +done(); diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-must-fail-if-unsupported.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-must-fail-if-unsupported.js new file mode 100644 index 0000000000000..69c65f6aa24b4 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-must-fail-if-unsupported.js @@ -0,0 +1,18 @@ +importScripts("/resources/testharness.js"); + +test(t => { + // The Window test html conditionally fetches and runs these tests only if the + // implementation does not have a true-valued static + // canConstructInDedicatedWorker property on MediaSource in the Window + // context. So, the implementation must agree on lack of support here in the + // dedicated worker context. + + // Ensure we're executing in a dedicated worker context. + assert_true(self instanceof DedicatedWorkerGlobalScope, "self instanceof DedicatedWorkerGlobalScope"); + assert_true(self.MediaSource === undefined, "MediaSource is undefined in DedicatedWorker"); + assert_throws_js(ReferenceError, + function() { var ms = new MediaSource(); }, + "MediaSource construction in DedicatedWorker throws exception"); +}, "MediaSource construction in DedicatedWorker context must fail if Window context did not claim MSE supported in DedicatedWorker"); + +done(); diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl-expected.txt new file mode 100644 index 0000000000000..824a01a4b8168 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl-expected.txt @@ -0,0 +1,8 @@ + + +PASS Test main context load of a DedicatedWorker MediaSource object URL should fail +PASS MediaSource in DedicatedWorker context must have true-valued canConstructInDedicatedWorker if Window context had it +PASS MediaSource construction succeeds with initial closed readyState in DedicatedWorker +PASS URL.createObjectURL(mediaSource) in DedicatedWorker does not throw exception +PASS URL.revokeObjectURL(mediaSource) in DedicatedWorker with two url for same MediaSource + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl.html b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl.html new file mode 100644 index 0000000000000..ae6019967252e --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl.html @@ -0,0 +1,52 @@ + + +Test MediaSource object and objectUrl creation and load via that url should fail, with MediaSource in dedicated worker + + + + + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl.js new file mode 100644 index 0000000000000..2e70d99418173 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl.js @@ -0,0 +1,33 @@ +importScripts("/resources/testharness.js"); + +test(t => { + // The Window test html conditionally fetches and runs these tests only if the + // implementation exposes a true-valued static canConstructInDedicatedWorker + // attribute on MediaSource in the Window context. So, the implementation must + // agree on support here in the dedicated worker context. + + // Ensure we're executing in a dedicated worker context. + assert_true(self instanceof DedicatedWorkerGlobalScope, "self instanceof DedicatedWorkerGlobalScope"); + assert_true(MediaSource.hasOwnProperty("canConstructInDedicatedWorker", "DedicatedWorker MediaSource hasOwnProperty 'canConstructInDedicatedWorker'")); + assert_true(MediaSource.canConstructInDedicatedWorker, "DedicatedWorker MediaSource.canConstructInDedicatedWorker"); +}, "MediaSource in DedicatedWorker context must have true-valued canConstructInDedicatedWorker if Window context had it"); + +test(t => { + const ms = new MediaSource(); + assert_equals(ms.readyState, "closed"); +}, "MediaSource construction succeeds with initial closed readyState in DedicatedWorker"); + +test(t => { + const ms = new MediaSource(); + const url = URL.createObjectURL(ms); +}, "URL.createObjectURL(mediaSource) in DedicatedWorker does not throw exception"); + +test(t => { + const ms = new MediaSource(); + const url1 = URL.createObjectURL(ms); + const url2 = URL.createObjectURL(ms); + URL.revokeObjectURL(url1); + URL.revokeObjectURL(url2); +}, "URL.revokeObjectURL(mediaSource) in DedicatedWorker with two url for same MediaSource"); + +done(); diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-expected.txt new file mode 100644 index 0000000000000..6552c2579a584 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-expected.txt @@ -0,0 +1,6 @@ + + +Harness Error (TIMEOUT), message = null + +TIMEOUT Test worker MediaSource construction, attachment, buffering and basic playback Test timed out + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker-expected.txt new file mode 100644 index 0000000000000..f621a66be5dc4 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker-expected.txt @@ -0,0 +1,35 @@ + + +Harness Error (TIMEOUT), message = null + +PASS Test worker MediaSource termination after at least 0 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 1 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 2 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 3 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 4 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 5 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 6 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 7 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 8 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 9 main thread setTimeouts, starting counting before setting srcObject +PASS Test worker MediaSource termination after at least 0 main thread setTimeouts, starting counting after setting srcObject +PASS Test worker MediaSource termination after at least 1 main thread setTimeouts, starting counting after setting srcObject +PASS Test worker MediaSource termination after at least 2 main thread setTimeouts, starting counting after setting srcObject +PASS Test worker MediaSource termination after at least 3 main thread setTimeouts, starting counting after setting srcObject +PASS Test worker MediaSource termination after at least 4 main thread setTimeouts, starting counting after setting srcObject +PASS Test worker MediaSource termination after at least 5 main thread setTimeouts, starting counting after setting srcObject +PASS Test worker MediaSource termination after at least 6 main thread setTimeouts, starting counting after setting srcObject +PASS Test worker MediaSource termination after at least 7 main thread setTimeouts, starting counting after setting srcObject +PASS Test worker MediaSource termination after at least 8 main thread setTimeouts, starting counting after setting srcObject +PASS Test worker MediaSource termination after at least 9 main thread setTimeouts, starting counting after setting srcObject +TIMEOUT Test worker MediaSource termination after at least 0 main thread setTimeouts, starting counting after first ended event Test timed out +TIMEOUT Test worker MediaSource termination after at least 1 main thread setTimeouts, starting counting after first ended event Test timed out +TIMEOUT Test worker MediaSource termination after at least 2 main thread setTimeouts, starting counting after first ended event Test timed out +TIMEOUT Test worker MediaSource termination after at least 3 main thread setTimeouts, starting counting after first ended event Test timed out +TIMEOUT Test worker MediaSource termination after at least 4 main thread setTimeouts, starting counting after first ended event Test timed out +TIMEOUT Test worker MediaSource termination after at least 5 main thread setTimeouts, starting counting after first ended event Test timed out +TIMEOUT Test worker MediaSource termination after at least 6 main thread setTimeouts, starting counting after first ended event Test timed out +TIMEOUT Test worker MediaSource termination after at least 7 main thread setTimeouts, starting counting after first ended event Test timed out +TIMEOUT Test worker MediaSource termination after at least 8 main thread setTimeouts, starting counting after first ended event Test timed out +TIMEOUT Test worker MediaSource termination after at least 9 main thread setTimeouts, starting counting after first ended event Test timed out + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html new file mode 100644 index 0000000000000..fb3dc8add15b9 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html @@ -0,0 +1,90 @@ + + +MediaSource-in-Worker looped playback test case with worker termination at various places + + + + + + + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.js new file mode 100644 index 0000000000000..b45381819106f --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.js @@ -0,0 +1,15 @@ +// This worker script is intended to be used by the +// mediasource-worker-play-terminate-worker.html test case. The script import +// may itself be terminated by the main thread terminating our context, +// producing a NetworkError, so we catch and ignore a NetworkError here. Note +// that any dependency on globals defined in the imported scripts may result in +// test harness error flakiness if an undefined variable (due to termination +// causing importScripts to fail) is accessed. Hence this script just imports +// and handles import errors, since such nondeterministic worker termination is +// central to the test case. +try { + importScripts("mediasource-worker-play.js"); +} catch(e) { + if (e.name != "NetworkError") + throw e; +} diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play.html b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play.html new file mode 100644 index 0000000000000..455a224069d11 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play.html @@ -0,0 +1,49 @@ + + +Simple MediaSource-in-Worker playback test case + + + + + + + diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play.js new file mode 100644 index 0000000000000..5c4760bf7b1a4 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play.js @@ -0,0 +1,74 @@ +importScripts("mediasource-worker-util.js"); + +// Note, we do not use testharness.js utilities within the worker context +// because it also communicates using postMessage to the main HTML document's +// harness, and would confuse the test case message parsing there. + +onmessage = function(evt) { + postMessage({ subject: messageSubject.ERROR, info: "No message expected by Worker"}); +}; + +let util = new MediaSourceWorkerUtil(); +let handle = util.mediaSource.handle; + +util.mediaSource.addEventListener('sourceopen', () => { + // Immediately re-verify the SameObject property of the handle we transferred. + if (handle !== util.mediaSource.handle) { + postMessage({ + subject: messageSubject.ERROR, + info: 'mediaSource.handle changed from the original value' + }); + } + + // Also verify that transferring the already-transferred handle instance is + // prevented correctly. + try { + postMessage( + { + subject: messageSubject.ERROR, + info: + 'This postMessage should fail: the handle has already been transferred', + extra_info: util.mediaSource.handle + }, + {transfer: [util.mediaSource.handle]}); + } catch (e) { + if (e.name != 'DataCloneError') { + postMessage({ + subject: messageSubject.ERROR, + info: 'Expected handle retransfer exception did not occur' + }); + } + } + + sourceBuffer = util.mediaSource.addSourceBuffer(util.mediaMetadata.type); + sourceBuffer.onerror = (err) => { + postMessage({ subject: messageSubject.ERROR, info: err }); + }; + sourceBuffer.onupdateend = () => { + // Reset the parser. Unnecessary for this buffering, except helps with test + // coverage. + sourceBuffer.abort(); + // Shorten the buffered media and test playback duration to avoid timeouts. + sourceBuffer.remove(0.5, Infinity); + sourceBuffer.onupdateend = () => { + util.mediaSource.duration = 0.5; + // Issue changeType to the same type that we've already buffered. + // Unnecessary for this buffering, except helps with test coverage. + sourceBuffer.changeType(util.mediaMetadata.type); + util.mediaSource.endOfStream(); + // Sanity check the duration. + // Unnecessary for this buffering, except helps with test coverage. + var duration = util.mediaSource.duration; + if (isNaN(duration) || duration <= 0.0 || duration >= 1.0) { + postMessage({ + subject: messageSubject.ERROR, + info: "mediaSource.duration " + duration + " is not within expected range (0,1)" + }); + } + }; + }; + util.mediaLoadPromise.then(mediaData => { sourceBuffer.appendBuffer(mediaData); }, + err => { postMessage({ subject: messageSubject.ERROR, info: err }) }); +}, {once: true}); + +postMessage({ subject: messageSubject.HANDLE, info: handle }, { transfer: [handle] }); diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-util.js b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-util.js new file mode 100644 index 0000000000000..7adaf82508d0d --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-util.js @@ -0,0 +1,60 @@ +// This script is intended to be imported into a worker's script, and provides +// common preparation for multiple test cases. Errors encountered are either +// postMessaged with subject of messageSubject.ERROR, or in the case of failed +// mediaLoadPromise, result in promise rejection. + +importScripts("mediasource-message-util.js"); + +if (!this.MediaSource) + postMessage({ subject: messageSubject.ERROR, info: "MediaSource API missing from Worker" }); + +let MEDIA_LIST = [ + { + url: '../mp4/test.mp4', + type: 'video/mp4; codecs="mp4a.40.2,avc1.4d400d"', + }, + { + url: '../webm/test.webm', + type: 'video/webm; codecs="vp8, vorbis"', + }, +]; + +class MediaSourceWorkerUtil { + constructor() { + this.mediaSource = new MediaSource(); + + // Find supported test media, if any. + this.foundSupportedMedia = false; + for (let i = 0; i < MEDIA_LIST.length; ++i) { + this.mediaMetadata = MEDIA_LIST[i]; + if (MediaSource.isTypeSupported(this.mediaMetadata.type)) { + this.foundSupportedMedia = true; + break; + } + } + + // Begin asynchronous fetch of the test media. + if (this.foundSupportedMedia) { + this.mediaLoadPromise = MediaSourceWorkerUtil.loadBinaryAsync(this.mediaMetadata.url); + } else { + postMessage({ subject: messageSubject.ERROR, info: "No supported test media" }); + } + } + + static loadBinaryAsync(url) { + return new Promise((resolve, reject) => { + let request = new XMLHttpRequest(); + request.open("GET", url, true); + request.responseType = "arraybuffer"; + request.onerror = event => { reject(event); }; + request.onload = () => { + if (request.status != 200) { + reject("Unexpected loadData_ status code : " + request.status); + } + let response = new Uint8Array(request.response); + resolve(response); + }; + request.send(); + }); + } +} diff --git a/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/w3c-import.log b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/w3c-import.log new file mode 100644 index 0000000000000..a55d669f0798b --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/w3c-import.log @@ -0,0 +1,35 @@ +The tests in this directory were imported from the W3C repository. +Do NOT modify these tests directly in WebKit. +Instead, create a pull request on the WPT github: + https://github.com/web-platform-tests/wpt + +Then run the Tools/Scripts/import-w3c-tests in WebKit to reimport + +Do NOT modify or remove this file. + +------------------------------------------------------------------------ +Properties requiring vendor prefixes: +None +Property values requiring vendor prefixes: +None +------------------------------------------------------------------------ +List of files: +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-message-util.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element.html +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-detach-element.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration.html +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-duration.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-get-objecturl.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-to-main.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.html +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle.html +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-handle.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-must-fail-if-unsupported.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl.html +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-objecturl.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play.html +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play.js +/LayoutTests/imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-util.js diff --git a/LayoutTests/platform/mac-wk2/TestExpectations b/LayoutTests/platform/mac-wk2/TestExpectations index 94bb64bc5b580..a20ba9333b809 100644 --- a/LayoutTests/platform/mac-wk2/TestExpectations +++ b/LayoutTests/platform/mac-wk2/TestExpectations @@ -92,6 +92,9 @@ imported/w3c/web-platform-tests/payment-request/payment-request-abort-method.htt imported/w3c/web-platform-tests/payment-request/payment-request-show-method.https.html [ WontFix ] imported/w3c/web-platform-tests/payment-request/rejects_if_not_active.https.html [ WontFix ] +imported/w3c/web-platform-tests/media-source/dedicated-worker/ [ Pass ] +# Test has non deterministic output as it measures how many main thread loops it takes to execute action in worker. +imported/w3c/web-platform-tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html [ Pass Failure ] http/tests/security/contentSecurityPolicy/manifest-src-allowed.html [ Pass ] http/tests/security/contentSecurityPolicy/manifest-src-blocked.html [ Pass ] http/wpt/content-security-policy/sandbox-manifest-blocked.html [ Pass ] diff --git a/LayoutTests/platform/mac/TestExpectations b/LayoutTests/platform/mac/TestExpectations index 6ce4efc7c3bdb..203b3565dfd8b 100644 --- a/LayoutTests/platform/mac/TestExpectations +++ b/LayoutTests/platform/mac/TestExpectations @@ -833,6 +833,7 @@ platform/mac/fast/text/systemFont.html [ Pass Failure ] ## --- Start W3C Imported Media Source Tests # Failing Media Source tests imported/w3c/web-platform-tests/media-source/ [ Pass ] +imported/w3c/web-platform-tests/media-source/dedicated-worker/ [ Skip ] webkit.org/b/161725 imported/w3c/web-platform-tests/media-source/URL-createObjectURL-revoke.html [ Skip ] webkit.org/b/161725 imported/w3c/web-platform-tests/media-source/mediasource-append-buffer.html [ Skip ] webkit.org/b/161725 imported/w3c/web-platform-tests/media-source/mediasource-appendbuffer-quota-exceeded.html [ Skip ] diff --git a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml index 66bcb34ee9455..bfc21153867d0 100644 --- a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml +++ b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml @@ -4629,7 +4629,7 @@ MediaSourceEnabled: MediaSourceInWorkerEnabled: type: bool - status: unstable + status: preview category: media humanReadableName: "MediaSource in a Worker" humanReadableDescription: "MediaSource in a Worker" diff --git a/Source/WebCore/Modules/mediasource/MediaSource.cpp b/Source/WebCore/Modules/mediasource/MediaSource.cpp index 4411b5cf2fc4d..5b4c4d109fa93 100644 --- a/Source/WebCore/Modules/mediasource/MediaSource.cpp +++ b/Source/WebCore/Modules/mediasource/MediaSource.cpp @@ -207,6 +207,16 @@ MediaSource::MediaSource(ScriptExecutionContext& context) MediaSource::~MediaSource() { ALWAYS_LOG(LOGIDENTIFIER); +#if ENABLE(MEDIA_SOURCE_IN_WORKERS) + if (!isMainThread()) { + // When deleted on a worker; the HTMLMediaElement wouldn't have started the deletion. + // We need to manually detach ourselves and notify the HTMLMediaElement. + ensureWeakOnHTMLMediaElementContext([](auto& mediaElement) { + mediaElement.mediaSourceWasDetached(); + }); + detachFromElement(); + } +#endif ASSERT(isClosed()); } @@ -249,13 +259,16 @@ void MediaSource::setPrivateAndOpen(Ref&& mediaSourcePrivate return; } + // ↳ Otherwise + // 1. Set the MediaSource's [[has ever been attached]] internal slot to true. #if ENABLE(MEDIA_SOURCE_IN_WORKERS) - if (m_handle) + if (m_handle) { + m_handle->setHasEverBeenAssignedAsSrcObject(); m_handle->mediaSourceDidOpen(*m_private); + } #endif - // ↳ Otherwise - // 1. Set the media element's delaying-the-load-event-flag to false. + // 2. Set the media element's delaying-the-load-event-flag to false. ensureWeakOnHTMLMediaElementContext([](auto& mediaElement) { mediaElement.setShouldDelayLoadEvent(false); }); @@ -1145,10 +1158,6 @@ bool MediaSource::attachToElement(WeakPtr&& element) ASSERT(isClosed()); m_mediaElement = WTFMove(element); -#if ENABLE(MEDIA_SOURCE_IN_WORKERS) - if (m_handle) - m_handle->setHasEverBeenAssignedAsSrcObject(); -#endif return true; } diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp index ed4075b0388cc..e9c857197e3fc 100644 --- a/Source/WebCore/html/HTMLMediaElement.cpp +++ b/Source/WebCore/html/HTMLMediaElement.cpp @@ -1484,8 +1484,15 @@ void HTMLMediaElement::selectMediaResource() [this](RefPtr source) { m_mediaSource = MediaSourceInterfaceMainThread::create(source.releaseNonNull()); }, #endif #if ENABLE(MEDIA_SOURCE_IN_WORKERS) - [this](RefPtr handle) { - m_mediaSource = MediaSourceInterfaceWorker::create(handle.releaseNonNull()); + [this, &logSiteIdentifier](RefPtr handle) { + // If the media provider object is a MediaSourceHandle whose [[Detached]] internal slot is true + // Run the "If the media data cannot be fetched at all, due to network errors, causing the user agent to give up trying to fetch the resource" steps of the resource fetch algorithm's media data processing steps list. + // If the media provider object is a MediaSourceHandle whose underlying MediaSource's [[has ever been attached]] internal slot is true + // Run the "If the media data cannot be fetched at all, due to network errors, causing the user agent to give up trying to fetch the resource" steps of the resource fetch algorithm's media data processing steps list. + if (!handle->isDetached() && !handle->hasEverBeenAssignedAsSrcObject()) + m_mediaSource = MediaSourceInterfaceWorker::create(handle.releaseNonNull()); + else + ALWAYS_LOG(logSiteIdentifier, "Attempting to use a detached or a previously attached MediaSourceHandle"); }, #endif [this](RefPtr blob) { m_blob = blob; } @@ -1709,6 +1716,13 @@ void HTMLMediaElement::loadResource(const URL& initialURL, ContentType& contentT mediaPlayerRenderingModeChanged(); } +void HTMLMediaElement::mediaSourceWasDetached() +{ + // The steps on what happen when a MediaSource goes missing are not defined in the current spec. + // https://github.com/w3c/media-source/issues/348 ; we do what's the most sensible for now. + userCancelledLoad(); +} + struct HTMLMediaElement::CueData { WTF_MAKE_STRUCT_FAST_ALLOCATED; PODIntervalTree> cueTree; diff --git a/Source/WebCore/html/HTMLMediaElement.h b/Source/WebCore/html/HTMLMediaElement.h index 8cedc455ec303..4c08952c3a730 100644 --- a/Source/WebCore/html/HTMLMediaElement.h +++ b/Source/WebCore/html/HTMLMediaElement.h @@ -675,6 +675,8 @@ class HTMLMediaElement const String& spatialTrackingLabel() const; void setSpatialTrackingLabel(String&&); + void mediaSourceWasDetached(); + protected: HTMLMediaElement(const QualifiedName&, Document&, bool createdByParser); virtual ~HTMLMediaElement(); diff --git a/Source/WebCore/html/track/TrackBase.cpp b/Source/WebCore/html/track/TrackBase.cpp index 8b92326340bd0..a95635f8623d3 100644 --- a/Source/WebCore/html/track/TrackBase.cpp +++ b/Source/WebCore/html/track/TrackBase.cpp @@ -44,10 +44,15 @@ static int s_uniqueId = 0; static bool isValidBCP47LanguageTag(const String&); #if !RELEASE_LOG_DISABLED -static RefPtr& nullLogger() +static Ref nullLogger(TrackBase& track) { - static NeverDestroyed> logger; - return logger; + static std::once_flag onceKey; + static LazyNeverDestroyed> logger; + std::call_once(onceKey, [&] { + logger.construct(Logger::create(&track)); + logger.get()->setEnabled(&track, false); + }); + return logger.get(); } #endif @@ -66,12 +71,7 @@ TrackBase::TrackBase(ScriptExecutionContext* context, Type type, const std::opti m_type = type; #if !RELEASE_LOG_DISABLED - if (!nullLogger().get()) { - nullLogger() = Logger::create(this); - nullLogger()->setEnabled(this, false); - } - - m_logger = nullLogger().get(); + m_logger = nullLogger(*this); #endif }