Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / mediasource / MediaSource.cpp
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/core/v8/ExceptionMessages.h"
35 #include "bindings/core/v8/ExceptionState.h"
36 #include "bindings/core/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 blink {
57
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
72 const AtomicString& MediaSource::openKeyword()
73 {
74     DEFINE_STATIC_LOCAL(const AtomicString, open, ("open", AtomicString::ConstructFromLiteral));
75     return open;
76 }
77
78 const AtomicString& MediaSource::closedKeyword()
79 {
80     DEFINE_STATIC_LOCAL(const AtomicString, closed, ("closed", AtomicString::ConstructFromLiteral));
81     return closed;
82 }
83
84 const AtomicString& MediaSource::endedKeyword()
85 {
86     DEFINE_STATIC_LOCAL(const AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
87     return ended;
88 }
89
90 MediaSource* MediaSource::create(ExecutionContext* context)
91 {
92     MediaSource* mediaSource = new MediaSource(context);
93     mediaSource->suspendIfNeeded();
94     return mediaSource;
95 }
96
97 MediaSource::MediaSource(ExecutionContext* context)
98     : ActiveDOMObject(context)
99     , m_readyState(closedKeyword())
100     , m_asyncEventQueue(GenericEventQueue::create(this))
101     , m_attachedElement(nullptr)
102     , m_sourceBuffers(SourceBufferList::create(executionContext(), m_asyncEventQueue.get()))
103     , m_activeSourceBuffers(SourceBufferList::create(executionContext(), m_asyncEventQueue.get()))
104     , m_isAddedToRegistry(false)
105 {
106     WTF_LOG(Media, "MediaSource::MediaSource %p", this);
107 }
108
109 MediaSource::~MediaSource()
110 {
111     WTF_LOG(Media, "MediaSource::~MediaSource %p", this);
112 #if !ENABLE(OILPAN)
113     ASSERT(isClosed());
114 #endif
115 }
116
117 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionState& exceptionState)
118 {
119     WTF_LOG(Media, "MediaSource::addSourceBuffer(%s) %p", type.ascii().data(), this);
120
121     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
122     // 1. If type is an empty string then throw an InvalidAccessError exception
123     // and abort these steps.
124     if (type.isEmpty()) {
125         exceptionState.throwDOMException(InvalidAccessError, "The type provided is empty.");
126         return 0;
127     }
128
129     // 2. If type contains a MIME type that is not supported ..., then throw a
130     // NotSupportedError exception and abort these steps.
131     if (!isTypeSupported(type)) {
132         exceptionState.throwDOMException(NotSupportedError, "The type provided ('" + type + "') is unsupported.");
133         return 0;
134     }
135
136     // 4. If the readyState attribute is not in the "open" state then throw an
137     // InvalidStateError exception and abort these steps.
138     if (!isOpen()) {
139         exceptionState.throwDOMException(InvalidStateError, "The MediaSource's readyState is not 'open'.");
140         return 0;
141     }
142
143     // 5. Create a new SourceBuffer object and associated resources.
144     ContentType contentType(type);
145     Vector<String> codecs = contentType.codecs();
146     OwnPtr<WebSourceBuffer> webSourceBuffer = createWebSourceBuffer(contentType.type(), codecs, exceptionState);
147
148     if (!webSourceBuffer) {
149         ASSERT(exceptionState.code() == NotSupportedError || exceptionState.code() == QuotaExceededError);
150         // 2. If type contains a MIME type that is not supported ..., then throw a NotSupportedError exception and abort these steps.
151         // 3. If the user agent can't handle any more SourceBuffer objects then throw a QuotaExceededError exception and abort these steps
152         return 0;
153     }
154
155     SourceBuffer* buffer = SourceBuffer::create(webSourceBuffer.release(), this, m_asyncEventQueue.get());
156     // 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
157     m_sourceBuffers->add(buffer);
158
159     // 7. Return the new object to the caller.
160     return buffer;
161 }
162
163 void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionState& exceptionState)
164 {
165     WTF_LOG(Media, "MediaSource::removeSourceBuffer() %p", this);
166
167     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-removeSourceBuffer-void-SourceBuffer-sourceBuffer
168
169     // 1. If sourceBuffer specifies an object that is not in sourceBuffers then
170     // throw a NotFoundError exception and abort these steps.
171     if (!m_sourceBuffers->length() || !m_sourceBuffers->contains(buffer)) {
172         exceptionState.throwDOMException(NotFoundError, "The SourceBuffer provided is not contained in this MediaSource.");
173         return;
174     }
175
176     // 2. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
177     buffer->abortIfUpdating();
178
179     // Steps 3-8 are related to updating audioTracks, videoTracks, and textTracks which aren't implmented yet.
180     // FIXME(91649): support track selection
181
182     // 9. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ...
183     m_activeSourceBuffers->remove(buffer);
184
185     // 10. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer event
186     // on that object.
187     m_sourceBuffers->remove(buffer);
188
189     // 11. Destroy all resources for sourceBuffer.
190     buffer->removedFromMediaSource();
191 }
192
193 void MediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicString& newState)
194 {
195     if (isOpen()) {
196         scheduleEvent(EventTypeNames::sourceopen);
197         return;
198     }
199
200     if (oldState == openKeyword() && newState == endedKeyword()) {
201         scheduleEvent(EventTypeNames::sourceended);
202         return;
203     }
204
205     ASSERT(isClosed());
206
207     m_activeSourceBuffers->clear();
208
209     // Clear SourceBuffer references to this object.
210     for (unsigned long i = 0; i < m_sourceBuffers->length(); ++i)
211         m_sourceBuffers->item(i)->removedFromMediaSource();
212     m_sourceBuffers->clear();
213
214     scheduleEvent(EventTypeNames::sourceclose);
215 }
216
217 bool MediaSource::isUpdating() const
218 {
219     // Return true if any member of |m_sourceBuffers| is updating.
220     for (unsigned long i = 0; i < m_sourceBuffers->length(); ++i) {
221         if (m_sourceBuffers->item(i)->updating())
222             return true;
223     }
224
225     return false;
226 }
227
228 bool MediaSource::isTypeSupported(const String& type)
229 {
230     WTF_LOG(Media, "MediaSource::isTypeSupported(%s)", type.ascii().data());
231
232     // Section 2.2 isTypeSupported() method steps.
233     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-isTypeSupported-boolean-DOMString-type
234     // 1. If type is an empty string, then return false.
235     if (type.isNull() || type.isEmpty())
236         return false;
237
238     ContentType contentType(type);
239     String codecs = contentType.parameter("codecs");
240
241     // 2. If type does not contain a valid MIME type string, then return false.
242     if (contentType.type().isEmpty())
243         return false;
244
245     // Note: MediaSource.isTypeSupported() returning true implies that HTMLMediaElement.canPlayType() will return "maybe" or "probably"
246     // since it does not make sense for a MediaSource to support a type the HTMLMediaElement knows it cannot play.
247     if (HTMLMediaElement::supportsType(contentType, String()) == WebMimeRegistry::IsNotSupported)
248         return false;
249
250     // 3. If type contains a media type or media subtype that the MediaSource does not support, then return false.
251     // 4. If type contains at a codec that the MediaSource does not support, then return false.
252     // 5. If the MediaSource does not support the specified combination of media type, media subtype, and codecs then return false.
253     // 6. Return true.
254     return MIMETypeRegistry::isSupportedMediaSourceMIMEType(contentType.type(), codecs);
255 }
256
257 const AtomicString& MediaSource::interfaceName() const
258 {
259     return EventTargetNames::MediaSource;
260 }
261
262 ExecutionContext* MediaSource::executionContext() const
263 {
264     return ActiveDOMObject::executionContext();
265 }
266
267 void MediaSource::clearWeakMembers(Visitor* visitor)
268 {
269 #if ENABLE(OILPAN)
270     // Oilpan: If the MediaSource survived, but its attached media
271     // element did not, signal the element that it can safely
272     // notify its MediaSource during finalization by calling close().
273     if (m_attachedElement && !visitor->isAlive(m_attachedElement)) {
274         m_attachedElement->setCloseMediaSourceWhenFinalizing();
275         m_attachedElement.clear();
276     }
277 #endif
278 }
279
280 void MediaSource::trace(Visitor* visitor)
281 {
282 #if ENABLE(OILPAN)
283     visitor->trace(m_asyncEventQueue);
284 #endif
285     visitor->trace(m_sourceBuffers);
286     visitor->trace(m_activeSourceBuffers);
287     visitor->registerWeakMembers<MediaSource, &MediaSource::clearWeakMembers>(this);
288     EventTargetWithInlineData::trace(visitor);
289 }
290
291 void MediaSource::setWebMediaSourceAndOpen(PassOwnPtr<WebMediaSource> webMediaSource)
292 {
293     TRACE_EVENT_ASYNC_END0("media", "MediaSource::attachToElement", this);
294     ASSERT(webMediaSource);
295     ASSERT(!m_webMediaSource);
296     ASSERT(m_attachedElement);
297     m_webMediaSource = webMediaSource;
298     setReadyState(openKeyword());
299 }
300
301 void MediaSource::addedToRegistry()
302 {
303     ASSERT(!m_isAddedToRegistry);
304     m_isAddedToRegistry = true;
305 }
306
307 void MediaSource::removedFromRegistry()
308 {
309     ASSERT(m_isAddedToRegistry);
310     m_isAddedToRegistry = false;
311 }
312
313 double MediaSource::duration() const
314 {
315     return isClosed() ? std::numeric_limits<float>::quiet_NaN() : m_webMediaSource->duration();
316 }
317
318 PassRefPtrWillBeRawPtr<TimeRanges> MediaSource::buffered() const
319 {
320     // Implements MediaSource algorithm for HTMLMediaElement.buffered.
321     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#htmlmediaelement-extensions
322     WillBeHeapVector<RefPtrWillBeMember<TimeRanges> > ranges(m_activeSourceBuffers->length());
323     for (size_t i = 0; i < m_activeSourceBuffers->length(); ++i)
324         ranges[i] = m_activeSourceBuffers->item(i)->buffered(ASSERT_NO_EXCEPTION);
325
326     // 1. If activeSourceBuffers.length equals 0 then return an empty TimeRanges object and abort these steps.
327     if (ranges.isEmpty())
328         return TimeRanges::create();
329
330     // 2. Let active ranges be the ranges returned by buffered for each SourceBuffer object in activeSourceBuffers.
331     // 3. Let highest end time be the largest range end time in the active ranges.
332     double highestEndTime = -1;
333     for (size_t i = 0; i < ranges.size(); ++i) {
334         unsigned length = ranges[i]->length();
335         if (length)
336             highestEndTime = std::max(highestEndTime, ranges[i]->end(length - 1, ASSERT_NO_EXCEPTION));
337     }
338
339     // Return an empty range if all ranges are empty.
340     if (highestEndTime < 0)
341         return TimeRanges::create();
342
343     // 4. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
344     RefPtrWillBeRawPtr<TimeRanges> intersectionRanges = TimeRanges::create(0, highestEndTime);
345
346     // 5. For each SourceBuffer object in activeSourceBuffers run the following steps:
347     bool ended = readyState() == endedKeyword();
348     for (size_t i = 0; i < ranges.size(); ++i) {
349         // 5.1 Let source ranges equal the ranges returned by the buffered attribute on the current SourceBuffer.
350         TimeRanges* sourceRanges = ranges[i].get();
351
352         // 5.2 If readyState is "ended", then set the end time on the last range in source ranges to highest end time.
353         if (ended && sourceRanges->length())
354             sourceRanges->add(sourceRanges->start(sourceRanges->length() - 1, ASSERT_NO_EXCEPTION), highestEndTime);
355
356         // 5.3 Let new intersection ranges equal the the intersection between the intersection ranges and the source ranges.
357         // 5.4 Replace the ranges in intersection ranges with the new intersection ranges.
358         intersectionRanges->intersectWith(sourceRanges);
359     }
360
361     return intersectionRanges.release();
362 }
363
364 void MediaSource::setDuration(double duration, ExceptionState& exceptionState)
365 {
366     // 2.1 http://www.w3.org/TR/media-source/#widl-MediaSource-duration
367     // 1. If the value being set is negative or NaN then throw an InvalidAccessError
368     // exception and abort these steps.
369     if (std::isnan(duration)) {
370         exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::notAFiniteNumber(duration, "duration"));
371         return;
372     }
373     if (duration < 0.0) {
374         exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexExceedsMinimumBound("duration", duration, 0.0));
375         return;
376     }
377
378     // 2. If the readyState attribute is not "open" then throw an InvalidStateError
379     // exception and abort these steps.
380     // 3. If the updating attribute equals true on any SourceBuffer in sourceBuffers,
381     // then throw an InvalidStateError exception and abort these steps.
382     if (throwExceptionIfClosedOrUpdating(isOpen(), isUpdating(), exceptionState))
383         return;
384
385     // 4. Run the duration change algorithm with new duration set to the value being
386     // assigned to this attribute.
387     durationChangeAlgorithm(duration);
388 }
389
390 void MediaSource::durationChangeAlgorithm(double newDuration)
391 {
392     // Section 2.6.4 Duration change
393     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#duration-change-algorithm
394     // 1. If the current value of duration is equal to new duration, then return.
395     if (newDuration == duration())
396         return;
397
398     // 2. Set old duration to the current value of duration.
399     double oldDuration = duration();
400
401     bool requestSeek = m_attachedElement->currentTime() > newDuration;
402
403     // 3. Update duration to new duration.
404     m_webMediaSource->setDuration(newDuration);
405
406     // 4. If the new duration is less than old duration, then call remove(new duration, old duration) on all all objects in sourceBuffers.
407     if (newDuration < oldDuration) {
408         for (size_t i = 0; i < m_sourceBuffers->length(); ++i)
409             m_sourceBuffers->item(i)->remove(newDuration, oldDuration, ASSERT_NO_EXCEPTION);
410     }
411
412     // 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:
413     // NOTE: Currently we assume that the media engine is able to render partial frames/cues. If a media
414     // engine gets added that doesn't support this, then we'll need to add logic to handle the substeps.
415
416     // 6. Update the media controller duration to new duration and run the HTMLMediaElement duration change algorithm.
417     m_attachedElement->durationChanged(newDuration, requestSeek);
418 }
419
420 void MediaSource::setReadyState(const AtomicString& state)
421 {
422     ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword());
423
424     AtomicString oldState = readyState();
425     WTF_LOG(Media, "MediaSource::setReadyState() %p : %s -> %s", this, oldState.ascii().data(), state.ascii().data());
426
427     if (state == closedKeyword()) {
428         m_webMediaSource.clear();
429         m_attachedElement.clear();
430     }
431
432     if (oldState == state)
433         return;
434
435     m_readyState = state;
436
437     onReadyStateChange(oldState, state);
438 }
439
440 void MediaSource::endOfStream(const AtomicString& error, ExceptionState& exceptionState)
441 {
442     DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral));
443     DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral));
444
445     if (error == network) {
446         endOfStreamInternal(WebMediaSource::EndOfStreamStatusNetworkError, exceptionState);
447     } else if (error == decode) {
448         endOfStreamInternal(WebMediaSource::EndOfStreamStatusDecodeError, exceptionState);
449     } else {
450         ASSERT_NOT_REACHED(); // IDL enforcement should prevent this case.
451     }
452 }
453
454 void MediaSource::endOfStream(ExceptionState& exceptionState)
455 {
456     endOfStreamInternal(WebMediaSource::EndOfStreamStatusNoError, exceptionState);
457 }
458
459 void MediaSource::endOfStreamInternal(const WebMediaSource::EndOfStreamStatus eosStatus, ExceptionState& exceptionState)
460 {
461     // 2.2 http://www.w3.org/TR/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
462     // 1. If the readyState attribute is not in the "open" state then throw an
463     // InvalidStateError exception and abort these steps.
464     // 2. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an
465     // InvalidStateError exception and abort these steps.
466     if (throwExceptionIfClosedOrUpdating(isOpen(), isUpdating(), exceptionState))
467         return;
468
469     // 3. Run the end of stream algorithm with the error parameter set to error.
470     //   1. Change the readyState attribute value to "ended".
471     //   2. Queue a task to fire a simple event named sourceended at the MediaSource.
472     setReadyState(endedKeyword());
473
474     //   3. Do various steps based on |eosStatus|.
475     m_webMediaSource->markEndOfStream(eosStatus);
476 }
477
478 bool MediaSource::isOpen() const
479 {
480     return readyState() == openKeyword();
481 }
482
483 void MediaSource::setSourceBufferActive(SourceBuffer* sourceBuffer)
484 {
485     ASSERT(!m_activeSourceBuffers->contains(sourceBuffer));
486
487     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-activeSourceBuffers
488     // SourceBuffer objects in SourceBuffer.activeSourceBuffers must appear in
489     // the same order as they appear in SourceBuffer.sourceBuffers.
490     // SourceBuffer transitions to active are not guaranteed to occur in the
491     // same order as buffers in |m_sourceBuffers|, so this method needs to
492     // insert |sourceBuffer| into |m_activeSourceBuffers|.
493     size_t indexInSourceBuffers = m_sourceBuffers->find(sourceBuffer);
494     ASSERT(indexInSourceBuffers != kNotFound);
495
496     size_t insertPosition = 0;
497     while (insertPosition < m_activeSourceBuffers->length()
498         && m_sourceBuffers->find(m_activeSourceBuffers->item(insertPosition)) < indexInSourceBuffers) {
499         ++insertPosition;
500     }
501
502     m_activeSourceBuffers->insert(insertPosition, sourceBuffer);
503 }
504
505 bool MediaSource::isClosed() const
506 {
507     return readyState() == closedKeyword();
508 }
509
510 void MediaSource::close()
511 {
512     setReadyState(closedKeyword());
513 }
514
515 bool MediaSource::attachToElement(HTMLMediaElement* element)
516 {
517     if (m_attachedElement)
518         return false;
519
520     ASSERT(isClosed());
521
522     TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSource::attachToElement", this);
523     m_attachedElement = element;
524     return true;
525 }
526
527 void MediaSource::openIfInEndedState()
528 {
529     if (m_readyState != endedKeyword())
530         return;
531
532     setReadyState(openKeyword());
533     m_webMediaSource->unmarkEndOfStream();
534 }
535
536 bool MediaSource::hasPendingActivity() const
537 {
538     return m_attachedElement || m_webMediaSource
539         || m_asyncEventQueue->hasPendingEvents()
540         || m_isAddedToRegistry;
541 }
542
543 void MediaSource::stop()
544 {
545     m_asyncEventQueue->close();
546     if (!isClosed())
547         setReadyState(closedKeyword());
548     m_webMediaSource.clear();
549 }
550
551 PassOwnPtr<WebSourceBuffer> MediaSource::createWebSourceBuffer(const String& type, const Vector<String>& codecs, ExceptionState& exceptionState)
552 {
553     WebSourceBuffer* webSourceBuffer = 0;
554
555     switch (m_webMediaSource->addSourceBuffer(type, codecs, &webSourceBuffer)) {
556     case WebMediaSource::AddStatusOk:
557         return adoptPtr(webSourceBuffer);
558     case WebMediaSource::AddStatusNotSupported:
559         ASSERT(!webSourceBuffer);
560         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
561         // Step 2: If type contains a MIME type ... that is not supported with the types
562         // specified for the other SourceBuffer objects in sourceBuffers, then throw
563         // a NotSupportedError exception and abort these steps.
564         exceptionState.throwDOMException(NotSupportedError, "The type provided ('" + type + "') is not supported.");
565         return nullptr;
566     case WebMediaSource::AddStatusReachedIdLimit:
567         ASSERT(!webSourceBuffer);
568         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
569         // Step 3: If the user agent can't handle any more SourceBuffer objects then throw
570         // a QuotaExceededError exception and abort these steps.
571         exceptionState.throwDOMException(QuotaExceededError, "This MediaSource has reached the limit of SourceBuffer objects it can handle. No additional SourceBuffer objects may be added.");
572         return nullptr;
573     }
574
575     ASSERT_NOT_REACHED();
576     return nullptr;
577 }
578
579 void MediaSource::scheduleEvent(const AtomicString& eventName)
580 {
581     ASSERT(m_asyncEventQueue);
582
583     RefPtrWillBeRawPtr<Event> event = Event::create(eventName);
584     event->setTarget(this);
585
586     m_asyncEventQueue->enqueueEvent(event.release());
587 }
588
589 URLRegistry& MediaSource::registry() const
590 {
591     return MediaSourceRegistry::registry();
592 }
593
594 } // namespace blink