Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / mediasource / SourceBuffer.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/SourceBuffer.h"
33
34 #include "bindings/v8/ExceptionState.h"
35 #include "core/dom/ExceptionCode.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/events/Event.h"
38 #include "core/events/GenericEventQueue.h"
39 #include "core/fileapi/FileReaderLoader.h"
40 #include "core/fileapi/Stream.h"
41 #include "core/html/TimeRanges.h"
42 #include "modules/mediasource/MediaSource.h"
43 #include "platform/Logging.h"
44 #include "platform/TraceEvent.h"
45 #include "public/platform/WebSourceBuffer.h"
46 #include "wtf/ArrayBuffer.h"
47 #include "wtf/ArrayBufferView.h"
48 #include "wtf/MathExtras.h"
49
50 #include <limits>
51
52 using blink::WebSourceBuffer;
53
54 namespace WebCore {
55
56 DEFINE_GC_INFO(SourceBuffer);
57
58 PassRefPtrWillBeRawPtr<SourceBuffer> SourceBuffer::create(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
59 {
60     RefPtrWillBeRawPtr<SourceBuffer> sourceBuffer(adoptRefCountedWillBeRefCountedGarbageCollected(new SourceBuffer(webSourceBuffer, source, asyncEventQueue)));
61     sourceBuffer->suspendIfNeeded();
62     return sourceBuffer.release();
63 }
64
65 SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
66     : ActiveDOMObject(source->executionContext())
67     , m_webSourceBuffer(webSourceBuffer)
68     , m_source(source)
69     , m_asyncEventQueue(asyncEventQueue)
70     , m_mode(segmentsKeyword())
71     , m_updating(false)
72     , m_timestampOffset(0)
73     , m_appendWindowStart(0)
74     , m_appendWindowEnd(std::numeric_limits<double>::infinity())
75     , m_appendBufferAsyncPartRunner(this, &SourceBuffer::appendBufferAsyncPart)
76     , m_pendingRemoveStart(-1)
77     , m_pendingRemoveEnd(-1)
78     , m_removeAsyncPartRunner(this, &SourceBuffer::removeAsyncPart)
79     , m_streamMaxSizeValid(false)
80     , m_streamMaxSize(0)
81     , m_appendStreamAsyncPartRunner(this, &SourceBuffer::appendStreamAsyncPart)
82 {
83     ASSERT(m_webSourceBuffer);
84     ASSERT(m_source);
85     ScriptWrappable::init(this);
86 }
87
88 SourceBuffer::~SourceBuffer()
89 {
90     ASSERT(isRemoved());
91     ASSERT(!m_loader);
92     ASSERT(!m_stream);
93 }
94
95 const AtomicString& SourceBuffer::segmentsKeyword()
96 {
97     DEFINE_STATIC_LOCAL(const AtomicString, segments, ("segments", AtomicString::ConstructFromLiteral));
98     return segments;
99 }
100
101 const AtomicString& SourceBuffer::sequenceKeyword()
102 {
103     DEFINE_STATIC_LOCAL(const AtomicString, sequence, ("sequence", AtomicString::ConstructFromLiteral));
104     return sequence;
105 }
106
107 void SourceBuffer::setMode(const AtomicString& newMode, ExceptionState& exceptionState)
108 {
109     // Section 3.1 On setting mode attribute steps.
110     // 1. Let new mode equal the new value being assigned to this attribute.
111     // 2. If new mode does not equal "segments" or "sequence", then throw an INVALID_ACCESS_ERR exception and abort
112     //    these steps.
113     //    Step 2 is unnecessary: IDL enforcement prevents this case and should just return immediately to script
114     //    without calling this method in this case.
115     // 3. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw
116     //    an INVALID_STATE_ERR exception and abort these steps.
117     // 4. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
118     if (isRemoved() || m_updating) {
119         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
120         return;
121     }
122
123     // 5. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
124     // 5.1 Set the readyState attribute of the parent media source to "open"
125     // 5.2 Queue a task to fire a simple event named sourceopen at the parent media source.
126     m_source->openIfInEndedState();
127
128     // 6. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
129     // 7. If the new mode equals "sequence", then set the group start timestamp to the highest presentation end timestamp.
130     WebSourceBuffer::AppendMode appendMode = WebSourceBuffer::AppendModeSegments;
131     if (newMode == sequenceKeyword())
132         appendMode = WebSourceBuffer::AppendModeSequence;
133     if (!m_webSourceBuffer->setMode(appendMode)) {
134         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
135         return;
136     }
137
138     // 8. Update the attribute to new mode.
139     m_mode = newMode;
140 }
141
142 PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionState& exceptionState) const
143 {
144     // Section 3.1 buffered attribute steps.
145     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
146     //    InvalidStateError exception and abort these steps.
147     if (isRemoved()) {
148         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
149         return 0;
150     }
151
152     // 2. Return a new static normalized TimeRanges object for the media segments buffered.
153     return TimeRanges::create(m_webSourceBuffer->buffered());
154 }
155
156 double SourceBuffer::timestampOffset() const
157 {
158     return m_timestampOffset;
159 }
160
161 void SourceBuffer::setTimestampOffset(double offset, ExceptionState& exceptionState)
162 {
163     // Section 3.1 timestampOffset attribute setter steps.
164     // 1. Let new timestamp offset equal the new value being assigned to this attribute.
165     // 2. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw an
166     //    InvalidStateError exception and abort these steps.
167     // 3. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
168     if (isRemoved() || m_updating) {
169         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
170         return;
171     }
172
173     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
174     // 4.1 Set the readyState attribute of the parent media source to "open"
175     // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
176     m_source->openIfInEndedState();
177
178     // 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
179     // 6. If the mode attribute equals "sequence", then set the group start timestamp to new timestamp offset.
180     if (!m_webSourceBuffer->setTimestampOffset(offset)) {
181         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
182         return;
183     }
184
185     // 7. Update the attribute to new timestamp offset.
186     m_timestampOffset = offset;
187 }
188
189 double SourceBuffer::appendWindowStart() const
190 {
191     return m_appendWindowStart;
192 }
193
194 void SourceBuffer::setAppendWindowStart(double start, ExceptionState& exceptionState)
195 {
196     // Enforce throwing an exception on restricted double values.
197     if (std::isnan(start)
198         || start == std::numeric_limits<double>::infinity()
199         || start == -std::numeric_limits<double>::infinity()) {
200         exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
201         return;
202     }
203
204     // Section 3.1 appendWindowStart attribute setter steps.
205     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
206     //    InvalidStateError exception and abort these steps.
207     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
208     if (isRemoved() || m_updating) {
209         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
210         return;
211     }
212
213     // 3. If the new value is less than 0 or greater than or equal to appendWindowEnd then throw an InvalidAccessError
214     //    exception and abort these steps.
215     if (start < 0 || start >= m_appendWindowEnd) {
216         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
217         return;
218     }
219
220     m_webSourceBuffer->setAppendWindowStart(start);
221
222     // 4. Update the attribute to the new value.
223     m_appendWindowStart = start;
224 }
225
226 double SourceBuffer::appendWindowEnd() const
227 {
228     return m_appendWindowEnd;
229 }
230
231 void SourceBuffer::setAppendWindowEnd(double end, ExceptionState& exceptionState)
232 {
233     // Section 3.1 appendWindowEnd attribute setter steps.
234     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
235     //    InvalidStateError exception and abort these steps.
236     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
237     if (isRemoved() || m_updating) {
238         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
239         return;
240     }
241
242     // 3. If the new value equals NaN, then throw an InvalidAccessError and abort these steps.
243     // 4. If the new value is less than or equal to appendWindowStart then throw an InvalidAccessError
244     //    exception and abort these steps.
245     if (std::isnan(end) || end <= m_appendWindowStart) {
246         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
247         return;
248     }
249
250     m_webSourceBuffer->setAppendWindowEnd(end);
251
252     // 5. Update the attribute to the new value.
253     m_appendWindowEnd = end;
254 }
255
256 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionState& exceptionState)
257 {
258     // Section 3.2 appendBuffer()
259     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
260     // 1. If data is null then throw an InvalidAccessError exception and abort these steps.
261     if (!data) {
262         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
263         return;
264     }
265
266     appendBufferInternal(static_cast<const unsigned char*>(data->data()), data->byteLength(), exceptionState);
267 }
268
269 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionState& exceptionState)
270 {
271     // Section 3.2 appendBuffer()
272     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
273     // 1. If data is null then throw an InvalidAccessError exception and abort these steps.
274     if (!data) {
275         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
276         return;
277     }
278
279     appendBufferInternal(static_cast<const unsigned char*>(data->baseAddress()), data->byteLength(), exceptionState);
280 }
281
282 void SourceBuffer::appendStream(PassRefPtr<Stream> stream, ExceptionState& exceptionState)
283 {
284     m_streamMaxSizeValid = false;
285     appendStreamInternal(stream, exceptionState);
286 }
287
288 void SourceBuffer::appendStream(PassRefPtr<Stream> stream, unsigned long long maxSize, ExceptionState& exceptionState)
289 {
290     m_streamMaxSizeValid = maxSize > 0;
291     if (m_streamMaxSizeValid)
292         m_streamMaxSize = maxSize;
293     appendStreamInternal(stream, exceptionState);
294 }
295
296 void SourceBuffer::abort(ExceptionState& exceptionState)
297 {
298     // Section 3.2 abort() method steps.
299     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
300     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source
301     //    then throw an InvalidStateError exception and abort these steps.
302     // 2. If the readyState attribute of the parent media source is not in the "open" state
303     //    then throw an InvalidStateError exception and abort these steps.
304     if (isRemoved() || !m_source->isOpen()) {
305         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
306         return;
307     }
308
309     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
310     abortIfUpdating();
311
312     // 4. Run the reset parser state algorithm.
313     m_webSourceBuffer->abort();
314
315     // 5. Set appendWindowStart to 0.
316     setAppendWindowStart(0, exceptionState);
317
318     // 6. Set appendWindowEnd to positive Infinity.
319     setAppendWindowEnd(std::numeric_limits<double>::infinity(), exceptionState);
320 }
321
322 void SourceBuffer::remove(double start, double end, ExceptionState& exceptionState)
323 {
324     // Section 3.2 remove() method steps.
325     // 1. If start is negative or greater than duration, then throw an InvalidAccessError exception and abort these steps.
326     // 2. If end is less than or equal to start, then throw an InvalidAccessError exception and abort these steps.
327     if (start < 0 || (m_source && (std::isnan(m_source->duration()) || start > m_source->duration())) || end <= start) {
328         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
329         return;
330     }
331
332     // 3. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
333     //    InvalidStateError exception and abort these steps.
334     // 4. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
335     if (isRemoved() || m_updating) {
336         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
337         return;
338     }
339
340     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::remove", this);
341
342     // 5. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
343     // 5.1. Set the readyState attribute of the parent media source to "open"
344     // 5.2. Queue a task to fire a simple event named sourceopen at the parent media source .
345     m_source->openIfInEndedState();
346
347     // 6. Set the updating attribute to true.
348     m_updating = true;
349
350     // 7. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
351     scheduleEvent(EventTypeNames::updatestart);
352
353     // 8. Return control to the caller and run the rest of the steps asynchronously.
354     m_pendingRemoveStart = start;
355     m_pendingRemoveEnd = end;
356     m_removeAsyncPartRunner.runAsync();
357 }
358
359 void SourceBuffer::abortIfUpdating()
360 {
361     // Section 3.2 abort() method step 3 substeps.
362     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
363
364     if (!m_updating)
365         return;
366
367     const char* traceEventName = 0;
368     if (!m_pendingAppendData.isEmpty()) {
369         traceEventName = "SourceBuffer::appendBuffer";
370     } else if (m_stream) {
371         traceEventName = "SourceBuffer::appendStream";
372     } else if (m_pendingRemoveStart != -1) {
373         traceEventName = "SourceBuffer::remove";
374     } else {
375         ASSERT_NOT_REACHED();
376     }
377
378     // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
379     m_appendBufferAsyncPartRunner.stop();
380     m_pendingAppendData.clear();
381
382     m_removeAsyncPartRunner.stop();
383     m_pendingRemoveStart = -1;
384     m_pendingRemoveEnd = -1;
385
386     m_appendStreamAsyncPartRunner.stop();
387     clearAppendStreamState();
388
389     // 3.2. Set the updating attribute to false.
390     m_updating = false;
391
392     // 3.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
393     scheduleEvent(EventTypeNames::abort);
394
395     // 3.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
396     scheduleEvent(EventTypeNames::updateend);
397
398     TRACE_EVENT_ASYNC_END0("media", traceEventName, this);
399 }
400
401 void SourceBuffer::removedFromMediaSource()
402 {
403     if (isRemoved())
404         return;
405
406     abortIfUpdating();
407
408     m_webSourceBuffer->removedFromMediaSource();
409     m_webSourceBuffer.clear();
410     m_source = 0;
411     m_asyncEventQueue = 0;
412 }
413
414 bool SourceBuffer::hasPendingActivity() const
415 {
416     return m_source;
417 }
418
419 void SourceBuffer::suspend()
420 {
421     m_appendBufferAsyncPartRunner.suspend();
422     m_removeAsyncPartRunner.suspend();
423     m_appendStreamAsyncPartRunner.suspend();
424 }
425
426 void SourceBuffer::resume()
427 {
428     m_appendBufferAsyncPartRunner.resume();
429     m_removeAsyncPartRunner.resume();
430     m_appendStreamAsyncPartRunner.resume();
431 }
432
433 void SourceBuffer::stop()
434 {
435     m_appendBufferAsyncPartRunner.stop();
436     m_removeAsyncPartRunner.stop();
437     m_appendStreamAsyncPartRunner.stop();
438 }
439
440 ExecutionContext* SourceBuffer::executionContext() const
441 {
442     return ActiveDOMObject::executionContext();
443 }
444
445 const AtomicString& SourceBuffer::interfaceName() const
446 {
447     return EventTargetNames::SourceBuffer;
448 }
449
450 bool SourceBuffer::isRemoved() const
451 {
452     return !m_source;
453 }
454
455 void SourceBuffer::scheduleEvent(const AtomicString& eventName)
456 {
457     ASSERT(m_asyncEventQueue);
458
459     RefPtr<Event> event = Event::create(eventName);
460     event->setTarget(this);
461
462     m_asyncEventQueue->enqueueEvent(event.release());
463 }
464
465 void SourceBuffer::appendBufferInternal(const unsigned char* data, unsigned size, ExceptionState& exceptionState)
466 {
467     // Section 3.2 appendBuffer()
468     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
469
470     // Step 1 is enforced by the caller.
471     // 2. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an InvalidStateError exception and abort these steps.
472     // 3. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
473     if (isRemoved() || m_updating) {
474         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
475         return;
476     }
477
478     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendBuffer", this);
479
480     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
481     m_source->openIfInEndedState();
482
483     // Steps 5-6
484
485     // 7. Add data to the end of the input buffer.
486     m_pendingAppendData.append(data, size);
487
488     // 8. Set the updating attribute to true.
489     m_updating = true;
490
491     // 9. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
492     scheduleEvent(EventTypeNames::updatestart);
493
494     // 10. Asynchronously run the buffer append algorithm.
495     m_appendBufferAsyncPartRunner.runAsync();
496
497     TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "waiting");
498 }
499
500 void SourceBuffer::appendBufferAsyncPart()
501 {
502     ASSERT(m_updating);
503
504     TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "appending");
505
506     // Section 3.5.4 Buffer Append Algorithm
507     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
508
509     // 1. Run the segment parser loop algorithm.
510     // Step 2 doesn't apply since we run Step 1 synchronously here.
511     size_t appendSize = m_pendingAppendData.size();
512     if (!appendSize) {
513         // Resize buffer for 0 byte appends so we always have a valid pointer.
514         // We need to convey all appends, even 0 byte ones to |m_webSourceBuffer|
515         // so that it can clear its end of stream state if necessary.
516         m_pendingAppendData.resize(1);
517     }
518     m_webSourceBuffer->append(m_pendingAppendData.data(), appendSize);
519
520     // 3. Set the updating attribute to false.
521     m_updating = false;
522     m_pendingAppendData.clear();
523
524     // 4. Queue a task to fire a simple event named update at this SourceBuffer object.
525     scheduleEvent(EventTypeNames::update);
526
527     // 5. Queue a task to fire a simple event named updateend at this SourceBuffer object.
528     scheduleEvent(EventTypeNames::updateend);
529     TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendBuffer", this);
530 }
531
532 void SourceBuffer::removeAsyncPart()
533 {
534     ASSERT(m_updating);
535     ASSERT(m_pendingRemoveStart >= 0);
536     ASSERT(m_pendingRemoveStart < m_pendingRemoveEnd);
537
538     // Section 3.2 remove() method steps
539     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-remove-void-double-start-double-end
540
541     // 9. Run the coded frame removal algorithm with start and end as the start and end of the removal range.
542     m_webSourceBuffer->remove(m_pendingRemoveStart, m_pendingRemoveEnd);
543
544     // 10. Set the updating attribute to false.
545     m_updating = false;
546     m_pendingRemoveStart = -1;
547     m_pendingRemoveEnd = -1;
548
549     // 11. Queue a task to fire a simple event named update at this SourceBuffer object.
550     scheduleEvent(EventTypeNames::update);
551
552     // 12. Queue a task to fire a simple event named updateend at this SourceBuffer object.
553     scheduleEvent(EventTypeNames::updateend);
554 }
555
556 void SourceBuffer::appendStreamInternal(PassRefPtr<Stream> stream, ExceptionState& exceptionState)
557 {
558     // Section 3.2 appendStream()
559     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendStream-void-Stream-stream-unsigned-long-long-maxSize
560     // 1. If stream is null then throw an InvalidAccessError exception and abort these steps.
561     if (!stream || stream->isNeutered()) {
562         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
563         return;
564     }
565
566     // 2. Run the prepare append algorithm.
567     //  Section 3.5.4 Prepare Append Algorithm.
568     //  https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-prepare-append
569     //  1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an InvalidStateError exception and abort these steps.
570     //  2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
571     if (isRemoved() || m_updating) {
572         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
573         return;
574     }
575
576     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendStream", this);
577
578     //  3. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
579     m_source->openIfInEndedState();
580
581     // Steps 4-5 of the prepare append algorithm are handled by m_webSourceBuffer.
582
583     // 3. Set the updating attribute to true.
584     m_updating = true;
585
586     // 4. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
587     scheduleEvent(EventTypeNames::updatestart);
588
589     // 5. Asynchronously run the stream append loop algorithm with stream and maxSize.
590
591     stream->neuter();
592     m_loader = adoptPtr(new FileReaderLoader(FileReaderLoader::ReadByClient, this));
593     m_stream = stream;
594     m_appendStreamAsyncPartRunner.runAsync();
595 }
596
597 void SourceBuffer::appendStreamAsyncPart()
598 {
599     ASSERT(m_updating);
600     ASSERT(m_loader);
601     ASSERT(m_stream);
602
603     // Section 3.5.6 Stream Append Loop
604     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-stream-append-loop
605
606     // 1. If maxSize is set, then let bytesLeft equal maxSize.
607     // 2. Loop Top: If maxSize is set and bytesLeft equals 0, then jump to the loop done step below.
608     if (m_streamMaxSizeValid && !m_streamMaxSize) {
609         appendStreamDone(true);
610         return;
611     }
612
613     // Steps 3-11 are handled by m_loader.
614     // Note: Passing 0 here signals that maxSize was not set. (i.e. Read all the data in the stream).
615     m_loader->start(executionContext(), *m_stream, m_streamMaxSizeValid ? m_streamMaxSize : 0);
616 }
617
618 void SourceBuffer::appendStreamDone(bool success)
619 {
620     ASSERT(m_updating);
621     ASSERT(m_loader);
622     ASSERT(m_stream);
623
624     clearAppendStreamState();
625
626     if (!success) {
627         // Section 3.5.3 Append Error Algorithm
628         // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-append-error
629         //
630         // 1. Run the reset parser state algorithm. (Handled by caller)
631         // 2. Set the updating attribute to false.
632         m_updating = false;
633
634         // 3. Queue a task to fire a simple event named error at this SourceBuffer object.
635         scheduleEvent(EventTypeNames::error);
636
637         // 4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
638         scheduleEvent(EventTypeNames::updateend);
639         TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
640         return;
641     }
642
643     // Section 3.5.6 Stream Append Loop
644     // Steps 1-11 are handled by appendStreamAsyncPart(), |m_loader|, and |m_webSourceBuffer|.
645     // 12. Loop Done: Set the updating attribute to false.
646     m_updating = false;
647
648     // 13. Queue a task to fire a simple event named update at this SourceBuffer object.
649     scheduleEvent(EventTypeNames::update);
650
651     // 14. Queue a task to fire a simple event named updateend at this SourceBuffer object.
652     scheduleEvent(EventTypeNames::updateend);
653     TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
654 }
655
656 void SourceBuffer::clearAppendStreamState()
657 {
658     m_streamMaxSizeValid = false;
659     m_streamMaxSize = 0;
660     m_loader.clear();
661     m_stream = 0;
662 }
663
664 void SourceBuffer::didStartLoading()
665 {
666     WTF_LOG(Media, "SourceBuffer::didStartLoading() %p", this);
667 }
668
669 void SourceBuffer::didReceiveDataForClient(const char* data, unsigned dataLength)
670 {
671     WTF_LOG(Media, "SourceBuffer::didReceiveDataForClient(%d) %p", dataLength, this);
672     ASSERT(m_updating);
673     ASSERT(m_loader);
674
675     m_webSourceBuffer->append(reinterpret_cast<const unsigned char*>(data), dataLength);
676 }
677
678 void SourceBuffer::didFinishLoading()
679 {
680     WTF_LOG(Media, "SourceBuffer::didFinishLoading() %p", this);
681     appendStreamDone(true);
682 }
683
684 void SourceBuffer::didFail(FileError::ErrorCode errorCode)
685 {
686     WTF_LOG(Media, "SourceBuffer::didFail(%d) %p", errorCode, this);
687     appendStreamDone(false);
688 }
689
690 void SourceBuffer::trace(Visitor* visitor)
691 {
692     visitor->trace(m_source);
693 }
694
695 } // namespace WebCore