From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../dedicated-worker/mediasource-message-util.js | 16 ++ .../mediasource-worker-detach-element.html | 75 +++++ .../mediasource-worker-detach-element.js | 79 ++++++ .../mediasource-worker-duration.html | 86 ++++++ .../mediasource-worker-duration.js | 290 +++++++++++++++++++ .../mediasource-worker-get-objecturl.js | 13 + .../mediasource-worker-handle-transfer-to-main.js | 10 + .../mediasource-worker-handle-transfer.html | 316 +++++++++++++++++++++ .../mediasource-worker-handle-transfer.js | 19 ++ .../mediasource-worker-handle.html | 61 ++++ .../dedicated-worker/mediasource-worker-handle.js | 70 +++++ .../mediasource-worker-must-fail-if-unsupported.js | 18 ++ .../mediasource-worker-objecturl.html | 52 ++++ .../mediasource-worker-objecturl.js | 33 +++ .../mediasource-worker-play-terminate-worker.html | 85 ++++++ .../mediasource-worker-play-terminate-worker.js | 15 + .../dedicated-worker/mediasource-worker-play.html | 49 ++++ .../dedicated-worker/mediasource-worker-play.js | 74 +++++ .../dedicated-worker/mediasource-worker-util.js | 60 ++++ 19 files changed, 1421 insertions(+) create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-message-util.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-detach-element.html create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-detach-element.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-duration.html create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-duration.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-get-objecturl.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-to-main.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.html create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle.html create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-must-fail-if-unsupported.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-objecturl.html create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-objecturl.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play.html create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play.js create mode 100644 testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-util.js (limited to 'testing/web-platform/tests/media-source/dedicated-worker') diff --git a/testing/web-platform/tests/media-source/dedicated-worker/mediasource-message-util.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-message-util.js new file mode 100644 index 0000000000..c62eb8e3f7 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-detach-element.html b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-detach-element.html new file mode 100644 index 0000000000..0f74d95372 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-detach-element.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-detach-element.js new file mode 100644 index 0000000000..54b1d815f2 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-duration.html b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-duration.html new file mode 100644 index 0000000000..c195775beb --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-duration.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-duration.js new file mode 100644 index 0000000000..2a2c7bac0b --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-get-objecturl.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-get-objecturl.js new file mode 100644 index 0000000000..e9a5af6c81 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-to-main.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer-to-main.js new file mode 100644 index 0000000000..15cccb1a0e --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.html b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.html new file mode 100644 index 0000000000..2db71c049d --- /dev/null +++ b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.html @@ -0,0 +1,316 @@ + + +Test MediaSourceHandle transfer characteristics + + + + + + + diff --git a/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle-transfer.js new file mode 100644 index 0000000000..803da44e23 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle.html b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle.html new file mode 100644 index 0000000000..6129e05ffb --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-handle.js new file mode 100644 index 0000000000..d35cb877c2 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-must-fail-if-unsupported.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-must-fail-if-unsupported.js new file mode 100644 index 0000000000..69c65f6aa2 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-objecturl.html b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-objecturl.html new file mode 100644 index 0000000000..ae60199672 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-objecturl.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-objecturl.js new file mode 100644 index 0000000000..2e70d99418 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html new file mode 100644 index 0000000000..d6496afd6f --- /dev/null +++ b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html @@ -0,0 +1,85 @@ + + +MediaSource-in-Worker looped playback test case with worker termination at various places + + + + + + + diff --git a/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.js new file mode 100644 index 0000000000..b453818191 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play.html b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play.html new file mode 100644 index 0000000000..455a224069 --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-play.js new file mode 100644 index 0000000000..5c4760bf7b --- /dev/null +++ b/testing/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/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-util.js b/testing/web-platform/tests/media-source/dedicated-worker/mediasource-worker-util.js new file mode 100644 index 0000000000..7adaf82508 --- /dev/null +++ b/testing/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(); + }); + } +} -- cgit v1.2.3