• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "modules/mediasource/MediaSource.h"
33 
34 #include "bindings/v8/ExceptionMessages.h"
35 #include "bindings/v8/ExceptionState.h"
36 #include "bindings/v8/ExceptionStatePlaceholder.h"
37 #include "core/dom/ExceptionCode.h"
38 #include "core/events/Event.h"
39 #include "core/events/GenericEventQueue.h"
40 #include "core/html/HTMLMediaElement.h"
41 #include "core/html/TimeRanges.h"
42 #include "modules/mediasource/MediaSourceRegistry.h"
43 #include "platform/ContentType.h"
44 #include "platform/Logging.h"
45 #include "platform/MIMETypeRegistry.h"
46 #include "platform/RuntimeEnabledFeatures.h"
47 #include "platform/TraceEvent.h"
48 #include "public/platform/WebMediaSource.h"
49 #include "public/platform/WebSourceBuffer.h"
50 #include "wtf/Uint8Array.h"
51 #include "wtf/text/CString.h"
52 
53 using blink::WebMediaSource;
54 using blink::WebSourceBuffer;
55 
56 namespace WebCore {
57 
throwExceptionIfClosedOrUpdating(bool isOpen,bool isUpdating,ExceptionState & exceptionState)58 static bool throwExceptionIfClosedOrUpdating(bool isOpen, bool isUpdating, ExceptionState& exceptionState)
59 {
60     if (!isOpen) {
61         exceptionState.throwDOMException(InvalidStateError, "The MediaSource's readyState is not 'open'.");
62         return true;
63     }
64     if (isUpdating) {
65         exceptionState.throwDOMException(InvalidStateError, "The 'updating' attribute is true on one or more of this MediaSource's SourceBuffers.");
66         return true;
67     }
68 
69     return false;
70 }
71 
openKeyword()72 const AtomicString& MediaSource::openKeyword()
73 {
74     DEFINE_STATIC_LOCAL(const AtomicString, open, ("open", AtomicString::ConstructFromLiteral));
75     return open;
76 }
77 
closedKeyword()78 const AtomicString& MediaSource::closedKeyword()
79 {
80     DEFINE_STATIC_LOCAL(const AtomicString, closed, ("closed", AtomicString::ConstructFromLiteral));
81     return closed;
82 }
83 
endedKeyword()84 const AtomicString& MediaSource::endedKeyword()
85 {
86     DEFINE_STATIC_LOCAL(const AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
87     return ended;
88 }
89 
create(ExecutionContext * context)90 PassRefPtrWillBeRawPtr<MediaSource> MediaSource::create(ExecutionContext* context)
91 {
92     RefPtrWillBeRawPtr<MediaSource> mediaSource(adoptRefWillBeRefCountedGarbageCollected(new MediaSource(context)));
93     mediaSource->suspendIfNeeded();
94     return mediaSource.release();
95 }
96 
MediaSource(ExecutionContext * context)97 MediaSource::MediaSource(ExecutionContext* context)
98     : ActiveDOMObject(context)
99     , m_readyState(closedKeyword())
100     , m_asyncEventQueue(GenericEventQueue::create(this))
101     , m_attachedElement(0)
102     , m_sourceBuffers(SourceBufferList::create(executionContext(), m_asyncEventQueue.get()))
103     , m_activeSourceBuffers(SourceBufferList::create(executionContext(), m_asyncEventQueue.get()))
104 {
105     WTF_LOG(Media, "MediaSource::MediaSource %p", this);
106     ScriptWrappable::init(this);
107 }
108 
~MediaSource()109 MediaSource::~MediaSource()
110 {
111     WTF_LOG(Media, "MediaSource::~MediaSource %p", this);
112     ASSERT(isClosed());
113 }
114 
addSourceBuffer(const String & type,ExceptionState & exceptionState)115 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionState& exceptionState)
116 {
117     WTF_LOG(Media, "MediaSource::addSourceBuffer(%s) %p", type.ascii().data(), this);
118 
119     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
120     // 1. If type is an empty string then throw an InvalidAccessError exception
121     // and abort these steps.
122     if (type.isEmpty()) {
123         exceptionState.throwDOMException(InvalidAccessError, "The type provided is empty.");
124         return 0;
125     }
126 
127     // 2. If type contains a MIME type that is not supported ..., then throw a
128     // NotSupportedError exception and abort these steps.
129     if (!isTypeSupported(type)) {
130         exceptionState.throwDOMException(NotSupportedError, "The type provided ('" + type + "') is unsupported.");
131         return 0;
132     }
133 
134     // 4. If the readyState attribute is not in the "open" state then throw an
135     // InvalidStateError exception and abort these steps.
136     if (!isOpen()) {
137         exceptionState.throwDOMException(InvalidStateError, "The MediaSource's readyState is not 'open'.");
138         return 0;
139     }
140 
141     // 5. Create a new SourceBuffer object and associated resources.
142     ContentType contentType(type);
143     Vector<String> codecs = contentType.codecs();
144     OwnPtr<WebSourceBuffer> webSourceBuffer = createWebSourceBuffer(contentType.type(), codecs, exceptionState);
145 
146     if (!webSourceBuffer) {
147         ASSERT(exceptionState.code() == NotSupportedError || exceptionState.code() == QuotaExceededError);
148         // 2. If type contains a MIME type that is not supported ..., then throw a NotSupportedError exception and abort these steps.
149         // 3. If the user agent can't handle any more SourceBuffer objects then throw a QuotaExceededError exception and abort these steps
150         return 0;
151     }
152 
153     RefPtrWillBeRawPtr<SourceBuffer> buffer = SourceBuffer::create(webSourceBuffer.release(), this, m_asyncEventQueue.get());
154     // 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
155     m_sourceBuffers->add(buffer);
156     m_activeSourceBuffers->add(buffer);
157     // 7. Return the new object to the caller.
158     return buffer.get();
159 }
160 
removeSourceBuffer(SourceBuffer * buffer,ExceptionState & exceptionState)161 void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionState& exceptionState)
162 {
163     WTF_LOG(Media, "MediaSource::removeSourceBuffer() %p", this);
164     RefPtr<SourceBuffer> protect(buffer);
165 
166     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-removeSourceBuffer-void-SourceBuffer-sourceBuffer
167 
168     // 1. If sourceBuffer specifies an object that is not in sourceBuffers then
169     // throw a NotFoundError exception and abort these steps.
170     if (!m_sourceBuffers->length() || !m_sourceBuffers->contains(buffer)) {
171         exceptionState.throwDOMException(NotFoundError, "The SourceBuffer provided is not contained in this MediaSource.");
172         return;
173     }
174 
175     // 2. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
176     buffer->abortIfUpdating();
177 
178     // Steps 3-8 are related to updating audioTracks, videoTracks, and textTracks which aren't implmented yet.
179     // FIXME(91649): support track selection
180 
181     // 9. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ...
182     m_activeSourceBuffers->remove(buffer);
183 
184     // 10. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer event
185     // on that object.
186     m_sourceBuffers->remove(buffer);
187 
188     // 11. Destroy all resources for sourceBuffer.
189     buffer->removedFromMediaSource();
190 }
191 
onReadyStateChange(const AtomicString & oldState,const AtomicString & newState)192 void MediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicString& newState)
193 {
194     if (isOpen()) {
195         scheduleEvent(EventTypeNames::sourceopen);
196         return;
197     }
198 
199     if (oldState == openKeyword() && newState == endedKeyword()) {
200         scheduleEvent(EventTypeNames::sourceended);
201         return;
202     }
203 
204     ASSERT(isClosed());
205 
206     m_activeSourceBuffers->clear();
207 
208     // Clear SourceBuffer references to this object.
209     for (unsigned long i = 0; i < m_sourceBuffers->length(); ++i)
210         m_sourceBuffers->item(i)->removedFromMediaSource();
211     m_sourceBuffers->clear();
212 
213     scheduleEvent(EventTypeNames::sourceclose);
214 }
215 
isUpdating() const216 bool MediaSource::isUpdating() const
217 {
218     // Return true if any member of |m_sourceBuffers| is updating.
219     for (unsigned long i = 0; i < m_sourceBuffers->length(); ++i) {
220         if (m_sourceBuffers->item(i)->updating())
221             return true;
222     }
223 
224     return false;
225 }
226 
isTypeSupported(const String & type)227 bool MediaSource::isTypeSupported(const String& type)
228 {
229     WTF_LOG(Media, "MediaSource::isTypeSupported(%s)", type.ascii().data());
230 
231     // Section 2.2 isTypeSupported() method steps.
232     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-isTypeSupported-boolean-DOMString-type
233     // 1. If type is an empty string, then return false.
234     if (type.isNull() || type.isEmpty())
235         return false;
236 
237     ContentType contentType(type);
238     String codecs = contentType.parameter("codecs");
239 
240     // 2. If type does not contain a valid MIME type string, then return false.
241     if (contentType.type().isEmpty())
242         return false;
243 
244     // 3. If type contains a media type or media subtype that the MediaSource does not support, then return false.
245     // 4. If type contains at a codec that the MediaSource does not support, then return false.
246     // 5. If the MediaSource does not support the specified combination of media type, media subtype, and codecs then return false.
247     // 6. Return true.
248     return MIMETypeRegistry::isSupportedMediaSourceMIMEType(contentType.type(), codecs);
249 }
250 
interfaceName() const251 const AtomicString& MediaSource::interfaceName() const
252 {
253     return EventTargetNames::MediaSource;
254 }
255 
executionContext() const256 ExecutionContext* MediaSource::executionContext() const
257 {
258     return ActiveDOMObject::executionContext();
259 }
260 
trace(Visitor * visitor)261 void MediaSource::trace(Visitor* visitor)
262 {
263     visitor->trace(m_asyncEventQueue);
264     visitor->trace(m_sourceBuffers);
265     visitor->trace(m_activeSourceBuffers);
266     EventTargetWithInlineData::trace(visitor);
267 }
268 
setWebMediaSourceAndOpen(PassOwnPtr<WebMediaSource> webMediaSource)269 void MediaSource::setWebMediaSourceAndOpen(PassOwnPtr<WebMediaSource> webMediaSource)
270 {
271     TRACE_EVENT_ASYNC_END0("media", "MediaSource::attachToElement", this);
272     ASSERT(webMediaSource);
273     ASSERT(!m_webMediaSource);
274     ASSERT(m_attachedElement);
275     m_webMediaSource = webMediaSource;
276     setReadyState(openKeyword());
277 }
278 
addedToRegistry()279 void MediaSource::addedToRegistry()
280 {
281     setPendingActivity(this);
282 }
283 
removedFromRegistry()284 void MediaSource::removedFromRegistry()
285 {
286     unsetPendingActivity(this);
287 }
288 
duration() const289 double MediaSource::duration() const
290 {
291     return isClosed() ? std::numeric_limits<float>::quiet_NaN() : m_webMediaSource->duration();
292 }
293 
buffered() const294 PassRefPtr<TimeRanges> MediaSource::buffered() const
295 {
296     // Implements MediaSource algorithm for HTMLMediaElement.buffered.
297     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#htmlmediaelement-extensions
298     Vector<RefPtr<TimeRanges> > ranges(m_activeSourceBuffers->length());
299     for (size_t i = 0; i < m_activeSourceBuffers->length(); ++i)
300         ranges[i] = m_activeSourceBuffers->item(i)->buffered(ASSERT_NO_EXCEPTION);
301 
302     // 1. If activeSourceBuffers.length equals 0 then return an empty TimeRanges object and abort these steps.
303     if (ranges.isEmpty())
304         return TimeRanges::create();
305 
306     // 2. Let active ranges be the ranges returned by buffered for each SourceBuffer object in activeSourceBuffers.
307     // 3. Let highest end time be the largest range end time in the active ranges.
308     double highestEndTime = -1;
309     for (size_t i = 0; i < ranges.size(); ++i) {
310         unsigned length = ranges[i]->length();
311         if (length)
312             highestEndTime = std::max(highestEndTime, ranges[i]->end(length - 1, ASSERT_NO_EXCEPTION));
313     }
314 
315     // Return an empty range if all ranges are empty.
316     if (highestEndTime < 0)
317         return TimeRanges::create();
318 
319     // 4. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
320     RefPtr<TimeRanges> intersectionRanges = TimeRanges::create(0, highestEndTime);
321 
322     // 5. For each SourceBuffer object in activeSourceBuffers run the following steps:
323     bool ended = readyState() == endedKeyword();
324     for (size_t i = 0; i < ranges.size(); ++i) {
325         // 5.1 Let source ranges equal the ranges returned by the buffered attribute on the current SourceBuffer.
326         TimeRanges* sourceRanges = ranges[i].get();
327 
328         // 5.2 If readyState is "ended", then set the end time on the last range in source ranges to highest end time.
329         if (ended && sourceRanges->length())
330             sourceRanges->add(sourceRanges->start(sourceRanges->length() - 1, ASSERT_NO_EXCEPTION), highestEndTime);
331 
332         // 5.3 Let new intersection ranges equal the the intersection between the intersection ranges and the source ranges.
333         // 5.4 Replace the ranges in intersection ranges with the new intersection ranges.
334         intersectionRanges->intersectWith(sourceRanges);
335     }
336 
337     return intersectionRanges.release();
338 }
339 
setDuration(double duration,ExceptionState & exceptionState)340 void MediaSource::setDuration(double duration, ExceptionState& exceptionState)
341 {
342     // 2.1 http://www.w3.org/TR/media-source/#widl-MediaSource-duration
343     // 1. If the value being set is negative or NaN then throw an InvalidAccessError
344     // exception and abort these steps.
345     if (std::isnan(duration)) {
346         exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::notAFiniteNumber(duration, "duration"));
347         return;
348     }
349     if (duration < 0.0) {
350         exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexExceedsMinimumBound("duration", duration, 0.0));
351         return;
352     }
353 
354     // 2. If the readyState attribute is not "open" then throw an InvalidStateError
355     // exception and abort these steps.
356     // 3. If the updating attribute equals true on any SourceBuffer in sourceBuffers,
357     // then throw an InvalidStateError exception and abort these steps.
358     if (throwExceptionIfClosedOrUpdating(isOpen(), isUpdating(), exceptionState))
359         return;
360 
361     // 4. Run the duration change algorithm with new duration set to the value being
362     // assigned to this attribute.
363     durationChangeAlgorithm(duration);
364 }
365 
durationChangeAlgorithm(double newDuration)366 void MediaSource::durationChangeAlgorithm(double newDuration)
367 {
368     // Section 2.6.4 Duration change
369     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#duration-change-algorithm
370     // 1. If the current value of duration is equal to new duration, then return.
371     if (newDuration == duration())
372         return;
373 
374     // 2. Set old duration to the current value of duration.
375     double oldDuration = duration();
376 
377     bool requestSeek = m_attachedElement->currentTime() > newDuration;
378 
379     // 3. Update duration to new duration.
380     m_webMediaSource->setDuration(newDuration);
381 
382     // 4. If the new duration is less than old duration, then call remove(new duration, old duration) on all all objects in sourceBuffers.
383     if (newDuration < oldDuration) {
384         for (size_t i = 0; i < m_sourceBuffers->length(); ++i)
385             m_sourceBuffers->item(i)->remove(newDuration, oldDuration, ASSERT_NO_EXCEPTION);
386     }
387 
388     // 5. If a user agent is unable to partially render audio frames or text cues that start before and end after the duration, then run the following steps:
389     // NOTE: Currently we assume that the media engine is able to render partial frames/cues. If a media
390     // engine gets added that doesn't support this, then we'll need to add logic to handle the substeps.
391 
392     // 6. Update the media controller duration to new duration and run the HTMLMediaElement duration change algorithm.
393     m_attachedElement->durationChanged(newDuration, requestSeek);
394 }
395 
setReadyState(const AtomicString & state)396 void MediaSource::setReadyState(const AtomicString& state)
397 {
398     ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword());
399 
400     AtomicString oldState = readyState();
401     WTF_LOG(Media, "MediaSource::setReadyState() %p : %s -> %s", this, oldState.ascii().data(), state.ascii().data());
402 
403     if (state == closedKeyword()) {
404         m_webMediaSource.clear();
405         m_attachedElement = 0;
406     }
407 
408     if (oldState == state)
409         return;
410 
411     m_readyState = state;
412 
413     onReadyStateChange(oldState, state);
414 }
415 
endOfStream(const AtomicString & error,ExceptionState & exceptionState)416 void MediaSource::endOfStream(const AtomicString& error, ExceptionState& exceptionState)
417 {
418     DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral));
419     DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral));
420 
421     if (error == network) {
422         endOfStreamInternal(blink::WebMediaSource::EndOfStreamStatusNetworkError, exceptionState);
423     } else if (error == decode) {
424         endOfStreamInternal(blink::WebMediaSource::EndOfStreamStatusDecodeError, exceptionState);
425     } else {
426         ASSERT_NOT_REACHED(); // IDL enforcement should prevent this case.
427     }
428 }
429 
endOfStream(ExceptionState & exceptionState)430 void MediaSource::endOfStream(ExceptionState& exceptionState)
431 {
432     endOfStreamInternal(blink::WebMediaSource::EndOfStreamStatusNoError, exceptionState);
433 }
434 
endOfStreamInternal(const blink::WebMediaSource::EndOfStreamStatus eosStatus,ExceptionState & exceptionState)435 void MediaSource::endOfStreamInternal(const blink::WebMediaSource::EndOfStreamStatus eosStatus, ExceptionState& exceptionState)
436 {
437     // 2.2 http://www.w3.org/TR/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
438     // 1. If the readyState attribute is not in the "open" state then throw an
439     // InvalidStateError exception and abort these steps.
440     // 2. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an
441     // InvalidStateError exception and abort these steps.
442     if (throwExceptionIfClosedOrUpdating(isOpen(), isUpdating(), exceptionState))
443         return;
444 
445     // 3. Run the end of stream algorithm with the error parameter set to error.
446     //   1. Change the readyState attribute value to "ended".
447     //   2. Queue a task to fire a simple event named sourceended at the MediaSource.
448     setReadyState(endedKeyword());
449 
450     //   3. Do various steps based on |eosStatus|.
451     m_webMediaSource->markEndOfStream(eosStatus);
452 }
453 
isOpen() const454 bool MediaSource::isOpen() const
455 {
456     return readyState() == openKeyword();
457 }
458 
isClosed() const459 bool MediaSource::isClosed() const
460 {
461     return readyState() == closedKeyword();
462 }
463 
close()464 void MediaSource::close()
465 {
466     setReadyState(closedKeyword());
467 }
468 
attachToElement(HTMLMediaElement * element)469 bool MediaSource::attachToElement(HTMLMediaElement* element)
470 {
471     if (m_attachedElement)
472         return false;
473 
474     ASSERT(isClosed());
475 
476     TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSource::attachToElement", this);
477     m_attachedElement = element;
478     return true;
479 }
480 
openIfInEndedState()481 void MediaSource::openIfInEndedState()
482 {
483     if (m_readyState != endedKeyword())
484         return;
485 
486     setReadyState(openKeyword());
487     m_webMediaSource->unmarkEndOfStream();
488 }
489 
hasPendingActivity() const490 bool MediaSource::hasPendingActivity() const
491 {
492     return m_attachedElement || m_webMediaSource
493         || m_asyncEventQueue->hasPendingEvents()
494         || ActiveDOMObject::hasPendingActivity();
495 }
496 
stop()497 void MediaSource::stop()
498 {
499     m_asyncEventQueue->close();
500     if (!isClosed())
501         setReadyState(closedKeyword());
502     m_webMediaSource.clear();
503 }
504 
createWebSourceBuffer(const String & type,const Vector<String> & codecs,ExceptionState & exceptionState)505 PassOwnPtr<WebSourceBuffer> MediaSource::createWebSourceBuffer(const String& type, const Vector<String>& codecs, ExceptionState& exceptionState)
506 {
507     WebSourceBuffer* webSourceBuffer = 0;
508 
509     switch (m_webMediaSource->addSourceBuffer(type, codecs, &webSourceBuffer)) {
510     case WebMediaSource::AddStatusOk:
511         return adoptPtr(webSourceBuffer);
512     case WebMediaSource::AddStatusNotSupported:
513         ASSERT(!webSourceBuffer);
514         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
515         // Step 2: If type contains a MIME type ... that is not supported with the types
516         // specified for the other SourceBuffer objects in sourceBuffers, then throw
517         // a NotSupportedError exception and abort these steps.
518         exceptionState.throwDOMException(NotSupportedError, "The type provided ('" + type + "') is not supported.");
519         return nullptr;
520     case WebMediaSource::AddStatusReachedIdLimit:
521         ASSERT(!webSourceBuffer);
522         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
523         // Step 3: If the user agent can't handle any more SourceBuffer objects then throw
524         // a QuotaExceededError exception and abort these steps.
525         exceptionState.throwDOMException(QuotaExceededError, "This MediaSource has reached the limit of SourceBuffer objects it can handle. No additional SourceBuffer objects may be added.");
526         return nullptr;
527     }
528 
529     ASSERT_NOT_REACHED();
530     return nullptr;
531 }
532 
scheduleEvent(const AtomicString & eventName)533 void MediaSource::scheduleEvent(const AtomicString& eventName)
534 {
535     ASSERT(m_asyncEventQueue);
536 
537     RefPtrWillBeRawPtr<Event> event = Event::create(eventName);
538     event->setTarget(this);
539 
540     m_asyncEventQueue->enqueueEvent(event.release());
541 }
542 
registry() const543 URLRegistry& MediaSource::registry() const
544 {
545     return MediaSourceRegistry::registry();
546 }
547 
548 } // namespace WebCore
549