Changeset 171033 in webkit


Ignore:
Timestamp:
Jul 12, 2014 3:39:43 PM (10 years ago)
Author:
jer.noble@apple.com
Message:

[MSE] http/tests/media/media-source/mediasource-duration.html is failing.
https://bugs.webkit.org/show_bug.cgi?id=134852

Reviewed by Eric Carlson.

Source/WebCore:
Fixes the following tests:
http/tests/media/media-source/mediasource-config-change-mp4-a-bitrate.html
http/tests/media/media-source/mediasource-config-change-mp4-av-audio-bitrate.html
http/tests/media/media-source/mediasource-config-change-mp4-av-video-bitrate.html
http/tests/media/media-source/mediasource-config-change-mp4-v-bitrate.html
http/tests/media/media-source/mediasource-config-change-mp4-v-framerate.html
http/tests/media/media-source/mediasource-duration.html
http/tests/media/media-source/mediasource-play.html

The primary change necessary to fix the mediasource-duration.html test was to add support
for delaying the completion of a seek operation until the HTMLMediaElement's readyState
rises to > HAVE_CURRENT_DATA. This is accomplished by modifying MediaSourcePrivate to have
waitForSeekCompleted() and seekCompleted() virtual methods. These are called by MediaSource
when a seek operation results in the current time moving outside the currently buffered time
ranges, and when an append operation results in the readyState changing, respectively.

A number of other drive-by fixes were necessary to get this test fully passing, as noted
below.

Make the MediaSource the primary owner of the media's duration, rather than the MediaSourcePrivate.
Move the MediaSourcePrivateClient pointer to the MediaSourcePrivate from the MediaPlayerPrivate, so
the MediaSource's duration can be retrieved. While we're at it, do the same thing for buffered.

  • Modules/mediasource/MediaSource.cpp:

(WebCore::MediaSource::MediaSource): Initialize m_duration.
(WebCore::MediaSource::duration): Simple accessor.
(WebCore::MediaSource::setDurationInternal): Bring 'duration change algorithm' up to spec.
(WebCore::MediaSource::setReadyState): Reset m_duration on close.

  • Modules/mediasource/MediaSource.h:
  • platform/graphics/MediaSourcePrivate.h:
  • platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:

(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::load): Do not call setPrivateAndOpen().
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::durationDouble): Pass through to MediaSourcePrivateAVFObjC.
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::buffered): Ditto.

  • platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h:
  • platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:

(WebCore::MediaSourcePrivateAVFObjC::create): Call setPrivateAndOpen().
(WebCore::MediaSourcePrivateAVFObjC::MediaSourcePrivateAVFObjC): Set m_client.
(WebCore::MediaSourcePrivateAVFObjC::duration): Pass through to MediaSourcePrivateClient.
(WebCore::MediaSourcePrivateAVFObjC::buffered): Ditto.
(WebCore::MediaSourcePrivateAVFObjC::durationChanged): Pass through to MediaPlayerPrivateMediaSourceAVFObjC.
(WebCore::MediaSourcePrivateAVFObjC::setDuration): Deleted.

  • platform/graphics/gstreamer/MediaSourceGStreamer.cpp:

(WebCore::MediaSourceGStreamer::open): Pass in MediaSourcePrivateClient.
(WebCore::MediaSourceGStreamer::MediaSourceGStreamer): Initialize m_mediaSource.
(WebCore::MediaSourceGStreamer::durationChanged): Retrieve the duration from MediaSourcePrivateClient.
(WebCore::MediaSourceGStreamer::markEndOfStream): Remove unnecssary ASSERT.
(WebCore::MediaSourceGStreamer::unmarkEndOfStream): Ditto.
(WebCore::MediaSourceGStreamer::setDuration): Deleted.

  • platform/graphics/gstreamer/MediaSourceGStreamer.h:
  • platform/mock/mediasource/MockMediaPlayerMediaSource.cpp:

(WebCore::MockMediaPlayerMediaSource::load): Do not call setPrivateAndOpen().
(WebCore::MockMediaPlayerMediaSource::buffered): Pass through to MockMediaSourcePrivate.
(WebCore::MockMediaPlayerMediaSource::durationDouble): Ditto.
(WebCore::MockMediaPlayerMediaSource::advanceCurrentTime): Ditto.

  • platform/mock/mediasource/MockMediaSourcePrivate.cpp:

(WebCore::MockMediaSourcePrivate::create): Call setPrivateAndOpen().
(WebCore::MockMediaSourcePrivate::MockMediaSourcePrivate): Set m_client.
(WebCore::MockMediaSourcePrivate::duration): Pass through to MediaSourcePrivateClient.
(WebCore::MockMediaSourcePrivate::buffered): Ditto.
(WebCore::MockMediaSourcePrivate::durationChanged): Pass thorugh to MockMediaPlayerMediaSource.
(WebCore::MockMediaSourcePrivate::setDuration): Deleted.

Route seekToTime through MediaSource, rather than through MediaSourcePrivate, so that
the time can be compared against the buffered ranges, and trigger the delay of the seek
operation if necessary. Add a seekTimer to MediaPlayerPrivateMediaSourceAVFObjC, as this
guarantees the order of asynchronous operations, rather than callOnMainThread, which can
cause async operations to occur out of order.

  • Modules/mediasource/MediaSource.cpp:

(WebCore::MediaSource::seekToTime): Bring up to spec.
(WebCore::MediaSource::completeSeek): Ditto.
(WebCore::MediaSource::monitorSourceBuffers): Call completeSeek() when appropriate.

  • Modules/mediasource/SourceBuffer.cpp:

(WebCore::SourceBuffer::sourceBufferPrivateSeekToTime): Deleted.
(WebCore::SourceBuffer::seekToTime): Renamed from sourceBufferPrivateSeekToTime().

  • platform/graphics/MediaSourcePrivate.h:
  • platform/graphics/MediaSourcePrivateClient.h:
  • platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
  • platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:

(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::MediaPlayerPrivateMediaSourceAVFObjC): Add seekTimer. Only

call timeChanged() if no longer seeking, thereby triggering a 'seeked' event.

(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::~MediaPlayerPrivateMediaSourceAVFObjC): Clear m_seekTimer.
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekWithTolerance): Use m_seekTimer.
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekTimerFired): Call seekInternal.
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekInternal): Add logging.
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::waitForSeekCompleted): Added.
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekCompleted): Added; trigger 'seeked'.
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::setReadyState): No longer attempt to finish seek when

readyState changes here; this has been moved up to MediaSource.cpp.

  • platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h:
  • platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:

(WebCore::MediaSourcePrivateAVFObjC::waitForSeekCompleted): Pass through to MediaPlayerPrivateMediaSourceAVFObjC.
(WebCore::MediaSourcePrivateAVFObjC::seekCompleted): Ditto.
(WebCore::MediaSourcePrivateAVFObjC::seekToTime): Pass through to MediaSourcePrivateClient.
(WebCore::MediaSourcePrivateAVFObjC::fastSeekTimeForMediaTime): Ditto.

  • platform/mock/mediasource/MockMediaPlayerMediaSource.cpp:

(WebCore::MockMediaPlayerMediaSource::MockMediaPlayerMediaSource): Initialize m_seekCompleted.
(WebCore::MockMediaPlayerMediaSource::seeking): Check for an uncompleted seek operation.
(WebCore::MockMediaPlayerMediaSource::seekWithTolerance): Ditto.
(WebCore::MockMediaPlayerMediaSource::waitForSeekCompleted): Added.
(WebCore::MockMediaPlayerMediaSource::seekCompleted): Added; trigger 'seeked'.

  • platform/mock/mediasource/MockMediaPlayerMediaSource.h:
  • platform/mock/mediasource/MockMediaSourcePrivate.cpp:

(WebCore::MockMediaSourcePrivate::waitForSeekCompleted): Pass through to MockMediaPlayerMediaSource.
(WebCore::MockMediaSourcePrivate::seekCompleted): Ditto.

  • platform/mock/mediasource/MockMediaSourcePrivate.h:

Drive-by fixes.

  • Modules/mediasource/MediaSource.cpp:

(WebCore::MediaSource::streamEndedWithError): Re-order the steps in streamEndedWithError()

to avoid the MediaSource being closed and re-opened by the resulting duration change
operation.

  • Modules/mediasource/MediaSource.h:
  • Modules/mediasource/SourceBuffer.cpp:

(WebCore::SourceBuffer::remove): Added logging.
(WebCore::SourceBuffer::removeCodedFrames): Ditto.
(WebCore::SourceBuffer::hasFutureTime): Swap an ASSERT for an early-return; it's possible

for currentTime() to be outside of a buffered area.

  • Modules/mediasource/SourceBuffer.h:
  • html/HTMLMediaElement.cpp:

(WebCore::HTMLMediaElement::parseAttribute): Do not issue an additional 'timeupdate' event

after finishSeek() issues one of its own.

  • platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:

(WebCore::globalDataParserQueue): Allow parsing operations to happen concurrently on

background queues.

LayoutTests:
Eliminate flakiness in the mediasource-duration.html test by not playing
the media while testing seeking and duration.

  • http/tests/media/media-source/mediasource-duration.html:

Update testharness.js to the latest W3C version:

  • http/tests/w3c/resources/testharness.js:
Location:
trunk
Files:
22 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r171014 r171033  
     12014-07-11  Jer Noble  <jer.noble@apple.com>
     2
     3        [MSE] http/tests/media/media-source/mediasource-duration.html is failing.
     4        https://bugs.webkit.org/show_bug.cgi?id=134852
     5
     6        Reviewed by Eric Carlson.
     7
     8        Eliminate flakiness in the mediasource-duration.html test by not playing
     9        the media while testing seeking and duration.
     10        * http/tests/media/media-source/mediasource-duration.html:
     11
     12        Update testharness.js to the latest W3C version:
     13        * http/tests/w3c/resources/testharness.js:
     14
    1152014-07-11  Zalan Bujtas  <zalan@apple.com>
    216
  • trunk/LayoutTests/http/tests/media/media-source/mediasource-duration.html

    r170542 r171033  
    1919                  var truncatedDuration = seekTo / 2.0;
    2020
    21                   mediaElement.play();
    22 
    2321                  // Append all the segments
    2422                  test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
    25                   test.expectEvent(mediaElement, 'playing', 'Playing triggered');
    2623                  sourceBuffer.appendBuffer(mediaData);
    2724
     
    127124              test.waitForExpectedEvents(function()
    128125              {
    129                   assert_equals(mediaElement.currentTime, truncatedDuration,
     126                  assert_approx_equals(mediaElement.currentTime, truncatedDuration, 0.05,
    130127                                'Playback time has reached truncatedDuration');
    131                   assert_equals(mediaElement.duration, truncatedDuration,
     128                  assert_approx_equals(mediaElement.duration, truncatedDuration, 0.05,
    132129                                'mediaElement truncatedDuration after seek to it');
    133                   assert_equals(mediaSource.duration, truncatedDuration,
     130                  assert_approx_equals(mediaSource.duration, truncatedDuration, 0.05,
    134131                                'mediaSource truncatedDuration after seek to it');
    135132                  assert_false(mediaElement.seeking, 'mediaElement.seeking after seeked to truncatedDuration');
     
    155152              });
    156153
    157               mediaElement.play();
    158 
    159154              // Append all the segments
    160155              test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
    161               test.expectEvent(mediaElement, 'playing', 'Playing triggered');
    162156              sourceBuffer.appendBuffer(mediaData);
    163157
     
    202196                  }
    203197
     198                  mediaElement.play();
     199
    204200                  // Allow media to play to end while counting 'durationchange' events.
     201                  test.expectEvent(mediaElement, 'playing', 'Playing triggered');
    205202                  test.expectEvent(mediaElement, 'ended', 'Playback ended');
    206203                  test.waitForExpectedEvents(function()
     
    212209              });
    213210          }, 'Test setting same duration multiple times does not fire duplicate durationchange', {timeout: 2500});
     211
    214212        </script>
    215213    </body>
  • trunk/LayoutTests/http/tests/w3c/resources/testharness.js

    r151122 r171033  
    6060 *
    6161 * Additionally, test-specific metadata can be passed in the properties. These
    62  * are used when the individual test has different metadata from that stored 
     62 * are used when the individual test has different metadata from that stored
    6363 * in the <head>.
    6464 * The recognized metadata properties are:
    6565 *
    66  *    help - The url of the part of the specification being tested
    67  *
    68  *    assert - A human readable description of what the test is attempting
     66 *    help - The url or an array of urls of the part(s) of the specification(s)
     67 *           being tested
     68 *
     69 *    assert - A human readable description of what the test is attempting
    6970 *             to prove
    7071 *
    7172 *    author - Name and contact information for the author of the test in the
    7273 *             format: "Name <email_addr>" or "Name http://contact/url"
    73  *
    74  *    flags - space separated list of test flags in addition to any present in
    75  *            the head metadata
    7674 *
    7775 * == Asynchronous Tests ==
     
    137135 *       and may cause a file to stop testing.
    138136 *
     137 * == Cleanup ==
     138 *
     139 * Occasionally tests may create state that will persist beyond the test itself.
     140 * In order to ensure that tests are independent, such state should be cleaned
     141 * up once the test has a result. This can be achieved by adding cleanup
     142 * callbacks to the test. Such callbacks are registered using the add_cleanup
     143 * function on the test object. All registered callbacks will be run as soon as
     144 * the test result is known. For example
     145 * test(function() {
     146 *          window.some_global = "example";
     147 *          this.add_cleanup(function() {delete window.some_global});
     148 *          assert_true(false);
     149 *      });
     150 *
     151 * == Harness Timeout ==
     152 *
     153 * The overall harness admits two timeout values "normal" (the
     154 * default) and "long", used for tests which have an unusually long
     155 * runtime. After the timeout is reached, the harness will stop
     156 * waiting for further async tests to complete. By default the
     157 * timeouts are set to 10s and 60s, respectively, but may be changed
     158 * when the test is run on hardware with different performance
     159 * characteristics to a common desktop computer.  In order to opt-in
     160 * to the longer test timeout, the test must specify a meta element:
     161 * <meta name="timeout" content="long">
     162 *
    139163 * == Setup ==
    140164 *
     
    150174 * harness. Currently recognised properties are:
    151175 *
    152  * timeout - The time in ms after which the harness should stop waiting for
    153  *           tests to complete (this is different to the per-test timeout
    154  *           because async tests do not start their timer until .step is called)
    155176 *
    156177 * explicit_done - Wait for an explicit call to done() before declaring all
     
    167188 *                    that has its own timeout mechanism).
    168189 *
     190 * allow_uncaught_exception - don't treat an uncaught exception as an error;
     191 *                            needed when e.g. testing the window.onerror
     192 *                            handler.
     193 *
     194 * timeout_multiplier - Multiplier to apply to per-test timeouts.
     195 *
    169196 * == Determining when all tests are complete ==
    170197 *
     
    205232 * the test.
    206233 *
    207  * The properties argument is identical to that for test(). This may be a 
     234 * The properties argument is identical to that for test(). This may be a
    208235 * single object (used for all generated tests) or an array.
    209236 *
     
    290317 * assert_approx_equals(actual, expected, epsilon, description)
    291318 *   asserts that /actual/ is a number within +/- /epsilon/ of /expected/
     319 *
     320 * assert_less_than(actual, expected, description)
     321 *   asserts that /actual/ is a number less than /expected/
     322 *
     323 * assert_greater_than(actual, expected, description)
     324 *   asserts that /actual/ is a number greater than /expected/
     325 *
     326 * assert_less_than_equal(actual, expected, description)
     327 *   asserts that /actual/ is a number less than or equal to /expected/
     328 *
     329 * assert_greater_than_equal(actual, expected, description)
     330 *   asserts that /actual/ is a number greater than or equal to /expected/
    292331 *
    293332 * assert_regexp_match(actual, expected, description)
     
    332371 *   with signature assert_func(actual, expected, args_1, ..., args_N). Note that tests
    333372 *   with multiple allowed pass conditions are bad practice unless the spec specifically
    334  *   allows multiple behaviours. Test authors should not use this method simply to hide 
     373 *   allows multiple behaviours. Test authors should not use this method simply to hide
    335374 *   UA bugs.
    336375 *
     
    347386{
    348387    var debug = false;
    349     // default timeout is 5 seconds, test can override if needed
     388    // default timeout is 10 seconds, test can override if needed
    350389    var settings = {
    351       output:true,
    352       timeout:5000,
    353       test_timeout:2000
     390        output:true,
     391        harness_timeout:{
     392            "normal":10000,
     393            "long":60000
     394        },
     395        test_timeout:null
    354396    };
    355397
     
    363405    {
    364406        var scripts = document.getElementsByTagName("script");
    365         for (var i = 0; i < scripts.length; i++)
    366         {
    367             if (scripts[i].src)
    368             {
     407        for (var i = 0; i < scripts.length; i++) {
     408            if (scripts[i].src) {
    369409                var src = scripts[i].src;
    370             }
    371             else if (scripts[i].href)
    372             {
     410            } else if (scripts[i].href) {
    373411                //SVG case
    374412                var src = scripts[i].href.baseVal;
    375413            }
    376             if (src && src.slice(src.length - "testharness.js".length) === "testharness.js")
    377             {
     414
     415            if (src && src.slice(src.length - "testharness.js".length) === "testharness.js") {
    378416                script_prefix = src.slice(0, src.length - "testharness.js".length);
    379417                break;
     
    390428    {
    391429        //Don't use document.title to work around an Opera bug in XHTML documents
    392         var prefix = document.getElementsByTagName("title").length > 0 ?
    393                          document.getElementsByTagName("title")[0].firstChild.data :
    394                          "Untitled";
     430        var title = document.getElementsByTagName("title")[0];
     431        var prefix = (title && title.firstChild && title.firstChild.data) || "Untitled";
    395432        var suffix = name_counter > 0 ? " " + name_counter : "";
    396433        name_counter++;
     
    403440        properties = properties ? properties : {};
    404441        var test_obj = new Test(test_name, properties);
    405         test_obj.step(func);
    406         if (test_obj.status === test_obj.NOTRUN) {
     442        test_obj.step(func, test_obj, test_obj);
     443        if (test_obj.phase === test_obj.phases.STARTED) {
    407444            test_obj.done();
    408445        }
     
    432469            func = func_or_properties;
    433470            properties = maybe_properties;
    434         } else if (func_or_properties instanceof Function){
     471        } else if (func_or_properties instanceof Function) {
    435472            func = func_or_properties;
    436473        } else {
     
    452489                         {
    453490                             func.apply(this, x.slice(1));
    454                          }, 
    455                          name, 
     491                         },
     492                         name,
    456493                         Array.isArray(properties) ? properties[i] : properties);
    457494                });
     
    460497    function on_event(object, event, callback)
    461498    {
    462       object.addEventListener(event, callback, false);
     499        object.addEventListener(event, callback, false);
    463500    }
    464501
     
    483520
    484521    /*
     522     * Return true if object is probably a Node object.
     523     */
     524    function is_node(object)
     525    {
     526        // I use duck-typing instead of instanceof, because
     527        // instanceof doesn't work if the node is from another window (like an
     528        // iframe's contentWindow):
     529        // http://www.w3.org/Bugs/Public/show_bug.cgi?id=12295
     530        if ("nodeType" in object
     531        && "nodeName" in object
     532        && "nodeValue" in object
     533        && "childNodes" in object) {
     534            try {
     535                object.nodeType;
     536            } catch (e) {
     537                // The object is probably Node.prototype or another prototype
     538                // object that inherits from it, and not a Node instance.
     539                return false;
     540            }
     541            return true;
     542        }
     543        return false;
     544    }
     545
     546    /*
    485547     * Convert a value to a nice, human-readable string
    486548     */
    487     function format_value(val)
    488     {
    489         if (Array.isArray(val))
    490         {
    491             return "[" + val.map(format_value).join(", ") + "]";
    492         }
    493 
    494         switch (typeof val)
    495         {
     549    function format_value(val, seen)
     550    {
     551        if (!seen) {
     552            seen = [];
     553        }
     554        if (typeof val === "object" && val !== null) {
     555            if (seen.indexOf(val) >= 0) {
     556                return "[...]";
     557            }
     558            seen.push(val);
     559        }
     560        if (Array.isArray(val)) {
     561            return "[" + val.map(function(x) {return format_value(x, seen)}).join(", ") + "]";
     562        }
     563
     564        switch (typeof val) {
    496565        case "string":
    497566            val = val.replace("\\", "\\\\");
    498             for (var i = 0; i < 32; i++)
    499             {
     567            for (var i = 0; i < 32; i++) {
    500568                var replace = "\\";
    501569                switch (i) {
     
    542610            // In JavaScript, -0 === 0 and String(-0) == "0", so we have to
    543611            // special-case.
    544             if (val === -0 && 1/val === -Infinity)
    545             {
     612            if (val === -0 && 1/val === -Infinity) {
    546613                return "-0";
    547614            }
    548615            return String(val);
    549616        case "object":
    550             if (val === null)
    551             {
     617            if (val === null) {
    552618                return "null";
    553619            }
    554620
    555621            // Special-case Node objects, since those come up a lot in my tests.  I
    556             // ignore namespaces.  I use duck-typing instead of instanceof, because
    557             // instanceof doesn't work if the node is from another window (like an
    558             // iframe's contentWindow):
    559             // http://www.w3.org/Bugs/Public/show_bug.cgi?id=12295
    560             if ("nodeType" in val
    561             && "nodeName" in val
    562             && "nodeValue" in val
    563             && "childNodes" in val)
    564             {
    565                 switch (val.nodeType)
    566                 {
     622            // ignore namespaces.
     623            if (is_node(val)) {
     624                switch (val.nodeType) {
    567625                case Node.ELEMENT_NODE:
    568626                    var ret = "<" + val.tagName.toLowerCase();
    569                     for (var i = 0; i < val.attributes.length; i++)
    570                     {
     627                    for (var i = 0; i < val.attributes.length; i++) {
    571628                        ret += " " + val.attributes[i].name + '="' + val.attributes[i].value + '"';
    572629                    }
     
    616673
    617674    function same_value(x, y) {
    618         if (y !== y)
    619         {
     675        if (y !== y) {
    620676            //NaN case
    621677            return x !== x;
    622678        }
    623         else if (x === 0 && y === 0) {
     679        if (x === 0 && y === 0) {
    624680            //Distinguish +0 and -0
    625681            return 1/x === 1/y;
    626682        }
    627         else
    628         {
    629             //typical case
    630             return x === y;
    631         }
     683        return x === y;
    632684    }
    633685
     
    638690          * are the same object
    639691          */
    640         if (typeof actual != typeof expected)
    641         {
     692        if (typeof actual != typeof expected) {
    642693            assert(false, "assert_equals", description,
    643694                          "expected (" + typeof expected + ") ${expected} but got (" + typeof actual + ") ${actual}",
     
    679730
    680731             var p;
    681              for (p in actual)
    682              {
     732             for (p in actual) {
    683733                 assert(expected.hasOwnProperty(p), "assert_object_equals", description,
    684734                                                    "unexpected property ${p}", {p:p});
    685735
    686                  if (typeof actual[p] === "object" && actual[p] !== null)
    687                  {
    688                      if (stack.indexOf(actual[p]) === -1)
    689                      {
     736                 if (typeof actual[p] === "object" && actual[p] !== null) {
     737                     if (stack.indexOf(actual[p]) === -1) {
    690738                         check_equal(actual[p], expected[p], stack);
    691739                     }
    692                  }
    693                  else
    694                  {
     740                 } else {
    695741                     assert(same_value(actual[p], expected[p]), "assert_object_equals", description,
    696742                                                       "property ${p} expected ${expected} got ${actual}",
     
    698744                 }
    699745             }
    700              for (p in expected)
    701              {
     746             for (p in expected) {
    702747                 assert(actual.hasOwnProperty(p),
    703748                        "assert_object_equals", description,
     
    717762               {expected:expected.length, actual:actual.length});
    718763
    719         for (var i=0; i < actual.length; i++)
    720         {
     764        for (var i = 0; i < actual.length; i++) {
    721765            assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i),
    722766                   "assert_array_equals", description,
     
    749793    expose(assert_approx_equals, "assert_approx_equals");
    750794
     795    function assert_less_than(actual, expected, description)
     796    {
     797        /*
     798         * Test if a primitive number is less than another
     799         */
     800        assert(typeof actual === "number",
     801               "assert_less_than", description,
     802               "expected a number but got a ${type_actual}",
     803               {type_actual:typeof actual});
     804
     805        assert(actual < expected,
     806               "assert_less_than", description,
     807               "expected a number less than ${expected} but got ${actual}",
     808               {expected:expected, actual:actual});
     809    };
     810    expose(assert_less_than, "assert_less_than");
     811
     812    function assert_greater_than(actual, expected, description)
     813    {
     814        /*
     815         * Test if a primitive number is greater than another
     816         */
     817        assert(typeof actual === "number",
     818               "assert_greater_than", description,
     819               "expected a number but got a ${type_actual}",
     820               {type_actual:typeof actual});
     821
     822        assert(actual > expected,
     823               "assert_greater_than", description,
     824               "expected a number greater than ${expected} but got ${actual}",
     825               {expected:expected, actual:actual});
     826    };
     827    expose(assert_greater_than, "assert_greater_than");
     828
     829    function assert_less_than_equal(actual, expected, description)
     830    {
     831        /*
     832         * Test if a primitive number is less than or equal to another
     833         */
     834        assert(typeof actual === "number",
     835               "assert_less_than_equal", description,
     836               "expected a number but got a ${type_actual}",
     837               {type_actual:typeof actual});
     838
     839        assert(actual <= expected,
     840               "assert_less_than", description,
     841               "expected a number less than or equal to ${expected} but got ${actual}",
     842               {expected:expected, actual:actual});
     843    };
     844    expose(assert_less_than_equal, "assert_less_than_equal");
     845
     846    function assert_greater_than_equal(actual, expected, description)
     847    {
     848        /*
     849         * Test if a primitive number is greater than or equal to another
     850         */
     851        assert(typeof actual === "number",
     852               "assert_greater_than_equal", description,
     853               "expected a number but got a ${type_actual}",
     854               {type_actual:typeof actual});
     855
     856        assert(actual >= expected,
     857               "assert_greater_than_equal", description,
     858               "expected a number greater than or equal to ${expected} but got ${actual}",
     859               {expected:expected, actual:actual});
     860    };
     861    expose(assert_greater_than_equal, "assert_greater_than_equal");
     862
    751863    function assert_regexp_match(actual, expected, description) {
    752864        /*
     
    822934                    "changing property ${p} succeeded",
    823935                    {p:property_name});
    824          }
    825          finally
    826          {
     936         } finally {
    827937             object[property_name] = initial_value;
    828938         }
     
    832942    function assert_throws(code, func, description)
    833943    {
    834         try
    835         {
     944        try {
    836945            func.call(this);
    837946            assert(false, "assert_throws", description,
    838947                   "${func} did not throw", {func:func});
    839         }
    840         catch(e)
    841         {
     948        } catch (e) {
    842949            if (e instanceof AssertionError) {
    843                 throw(e);
    844             }
    845             if (code === null)
    846             {
     950                throw e;
     951            }
     952            if (code === null) {
    847953                return;
    848954            }
    849             if (typeof code === "object")
    850             {
     955            if (typeof code === "object") {
    851956                assert(typeof e == "object" && "name" in e && e.name == code.name,
    852957                       "assert_throws", description,
     
    9151020            };
    9161021
    917             if (!(name in name_code_map))
    918             {
     1022            if (!(name in name_code_map)) {
    9191023                throw new AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()');
    9201024            }
     
    9231027
    9241028            if (required_props.code === 0
    925             || ("name" in e && e.name !== e.name.toUpperCase() && e.name !== "DOMException"))
    926             {
     1029            || ("name" in e && e.name !== e.name.toUpperCase() && e.name !== "DOMException")) {
    9271030                // New style exception: also test the name property.
    9281031                required_props.name = name;
     
    9391042                   {func:func, e:e, type:typeof e});
    9401043
    941             for (var prop in required_props)
    942             {
     1044            for (var prop in required_props) {
    9431045                assert(typeof e == "object" && prop in e && e[prop] == required_props[prop],
    9441046                       "assert_throws", description,
     
    9561058    expose(assert_unreached, "assert_unreached");
    9571059
    958     function assert_any(assert_func, actual, expected_array) 
     1060    function assert_any(assert_func, actual, expected_array)
    9591061    {
    9601062        var args = [].slice.call(arguments, 3)
    9611063        var errors = []
    9621064        var passed = false;
    963         forEach(expected_array, 
     1065        forEach(expected_array,
    9641066                function(expected)
    9651067                {
     
    9671069                        assert_func.apply(this, [actual, expected].concat(args))
    9681070                        passed = true;
    969                     } catch(e) {
     1071                    } catch (e) {
    9701072                        errors.push(e.message);
    9711073                    }
     
    9801082    {
    9811083        this.name = name;
     1084
     1085        this.phases = {
     1086            INITIAL:0,
     1087            STARTED:1,
     1088            HAS_RESULT:2,
     1089            COMPLETE:3
     1090        };
     1091        this.phase = this.phases.INITIAL;
     1092
    9821093        this.status = this.NOTRUN;
    9831094        this.timeout_id = null;
    984         this.is_done = false;
    9851095
    9861096        this.properties = properties;
    987         this.timeout_length = properties.timeout ? properties.timeout : settings.test_timeout;
     1097        var timeout = properties.timeout ? properties.timeout : settings.test_timeout
     1098        if (timeout != null) {
     1099            this.timeout_length = timeout * tests.timeout_multiplier;
     1100        } else {
     1101            this.timeout_length = null;
     1102        }
    9881103
    9891104        this.message = null;
     
    9911106        var this_obj = this;
    9921107        this.steps = [];
     1108
     1109        this.cleanup_callbacks = [];
    9931110
    9941111        tests.push(this);
     
    10061123    Test.prototype.structured_clone = function()
    10071124    {
    1008         if(!this._structured_clone)
    1009         {
     1125        if (!this._structured_clone) {
    10101126            var msg = this.message;
    10111127            msg = msg ? String(msg) : msg;
     
    10211137    Test.prototype.step = function(func, this_obj)
    10221138    {
    1023         //In case the test has already failed
    1024         if (this.status !== this.NOTRUN)
    1025         {
    1026           return;
    1027         }
     1139        if (this.phase > this.phases.STARTED) {
     1140            return;
     1141        }
     1142        this.phase = this.phases.STARTED;
     1143        //If we don't get a result before the harness times out that will be a test timout
     1144        this.set_status(this.TIMEOUT, "Test timed out");
    10281145
    10291146        tests.started = true;
     
    10351152        this.steps.push(func);
    10361153
    1037         if (arguments.length === 1)
    1038         {
     1154        if (arguments.length === 1) {
    10391155            this_obj = this;
    10401156        }
    10411157
    1042         try
    1043         {
    1044             func.apply(this_obj, Array.prototype.slice.call(arguments, 2));
    1045         }
    1046         catch(e)
    1047         {
    1048             //This can happen if something called synchronously invoked another
    1049             //step
    1050             if (this.status !== this.NOTRUN)
    1051             {
     1158        try {
     1159            return func.apply(this_obj, Array.prototype.slice.call(arguments, 2));
     1160        } catch (e) {
     1161            if (this.phase >= this.phases.HAS_RESULT) {
    10521162                return;
    10531163            }
    1054             this.status = this.FAIL;
    1055             this.message = (typeof e === "object" && e !== null) ? e.message : e;
     1164            var message = (typeof e === "object" && e !== null) ? e.message : e;
    10561165            if (typeof e.stack != "undefined" && typeof e.message == "string") {
    10571166                //Try to make it more informative for some exceptions, at least
     
    10591168                //just errors like "Cannot read property 'parentNode' of null"
    10601169                //or "root is null".  Makes it a lot longer, of course.
    1061                 this.message += "(stack: " + e.stack + ")";
    1062             }
     1170                message += "(stack: " + e.stack + ")";
     1171            }
     1172            this.set_status(this.FAIL, message);
     1173            this.phase = this.phases.HAS_RESULT;
    10631174            this.done();
    1064             if (debug && e.constructor !== AssertionError) {
    1065                 throw e;
    1066             }
    10671175        }
    10681176    };
     
    10721180        var test_this = this;
    10731181
    1074         if (arguments.length === 1)
    1075         {
     1182        if (arguments.length === 1) {
    10761183            this_obj = test_this;
    10771184        }
     
    10881195        var test_this = this;
    10891196
    1090         if (arguments.length === 1)
    1091         {
     1197        if (arguments.length === 1) {
    10921198            this_obj = test_this;
    10931199        }
     
    10951201        return function()
    10961202        {
    1097             test_this.step.apply(test_this, [func, this_obj].concat(
    1098                 Array.prototype.slice.call(arguments)));
     1203            if (func) {
     1204                test_this.step.apply(test_this, [func, this_obj].concat(
     1205                    Array.prototype.slice.call(arguments)));
     1206            }
    10991207            test_this.done();
    11001208        };
    1101     };
     1209    }
     1210
     1211    Test.prototype.add_cleanup = function(callback) {
     1212        this.cleanup_callbacks.push(callback);
     1213    }
    11021214
    11031215    Test.prototype.set_timeout = function()
    11041216    {
    1105         var this_obj = this;
    1106         this.timeout_id = setTimeout(function()
    1107                                      {
    1108                                          this_obj.timeout();
    1109                                      }, this.timeout_length);
    1110     };
     1217        if (this.timeout_length !== null) {
     1218            var this_obj = this;
     1219            this.timeout_id = setTimeout(function()
     1220                                         {
     1221                                             this_obj.timeout();
     1222                                         }, this.timeout_length);
     1223        }
     1224    }
     1225
     1226    Test.prototype.set_status = function(status, message)
     1227    {
     1228        this.status = status;
     1229        this.message = message;
     1230    }
    11111231
    11121232    Test.prototype.timeout = function()
    11131233    {
    1114         this.status = this.TIMEOUT;
    11151234        this.timeout_id = null;
    1116         this.message = "Test timed out";
     1235        this.set_status(this.TIMEOUT, "Test timed out")
     1236        this.phase = this.phases.HAS_RESULT;
    11171237        this.done();
    11181238    };
     
    11201240    Test.prototype.done = function()
    11211241    {
    1122         if (this.is_done) {
     1242        if (this.phase == this.phases.COMPLETE) {
    11231243            return;
    11241244        }
     1245
     1246        if (this.phase <= this.phases.STARTED) {
     1247            this.set_status(this.PASS, null);
     1248        }
     1249
     1250        if (this.status == this.NOTRUN) {
     1251            alert(this.phase);
     1252        }
     1253
     1254        this.phase = this.phases.COMPLETE;
     1255
    11251256        clearTimeout(this.timeout_id);
    1126         if (this.status === this.NOTRUN)
    1127         {
    1128             this.status = this.PASS;
    1129         }
    1130         this.is_done = true;
    11311257        tests.result(this);
    1132     };
    1133 
     1258        this.cleanup();
     1259    };
     1260
     1261    Test.prototype.cleanup = function() {
     1262        forEach(this.cleanup_callbacks,
     1263                function(cleanup_callback) {
     1264                    cleanup_callback()
     1265                });
     1266    }
    11341267
    11351268    /*
     
    11531286    TestsStatus.prototype.structured_clone = function()
    11541287    {
    1155         if(!this._structured_clone)
    1156         {
     1288        if (!this._structured_clone) {
    11571289            var msg = this.message;
    11581290            msg = msg ? String(msg) : msg;
     
    11861318        this.processing_callbacks = false;
    11871319
    1188         this.timeout_length = settings.timeout;
     1320        this.allow_uncaught_exception = false;
     1321
     1322        this.timeout_multiplier = 1;
     1323        this.timeout_length = this.get_timeout();
    11891324        this.timeout_id = null;
    11901325
     
    12121347    Tests.prototype.setup = function(func, properties)
    12131348    {
    1214         if (this.phase >= this.phases.HAVE_RESULTS)
    1215         {
     1349        if (this.phase >= this.phases.HAVE_RESULTS) {
    12161350            return;
    12171351        }
    1218         if (this.phase < this.phases.SETUP)
    1219         {
     1352
     1353        if (this.phase < this.phases.SETUP) {
    12201354            this.phase = this.phases.SETUP;
    12211355        }
    12221356
    1223         for (var p in properties)
    1224         {
    1225             if (properties.hasOwnProperty(p))
    1226             {
    1227                 this.properties[p] = properties[p];
    1228             }
    1229         }
    1230 
    1231         if (properties.timeout)
    1232         {
    1233             this.timeout_length = properties.timeout;
    1234         }
    1235         if (properties.explicit_done)
    1236         {
    1237             this.wait_for_finish = true;
    1238         }
    1239         if (properties.explicit_timeout) {
    1240             this.timeout_length = null;
    1241         }
    1242 
    1243         if (func)
    1244         {
    1245             try
    1246             {
     1357        this.properties = properties;
     1358
     1359        for (var p in properties) {
     1360            if (properties.hasOwnProperty(p)) {
     1361                var value = properties[p]
     1362                if (p == "allow_uncaught_exception") {
     1363                    this.allow_uncaught_exception = value;
     1364                } else if (p == "explicit_done" && value) {
     1365                    this.wait_for_finish = true;
     1366                } else if (p == "explicit_timeout" && value) {
     1367                    this.timeout_length = null;
     1368                    if (this.timeout_id)
     1369                    {
     1370                        clearTimeout(this.timeout_id);
     1371                    }
     1372                } else if (p == "timeout_multiplier") {
     1373                    this.timeout_multiplier = value;
     1374                }
     1375            }
     1376        }
     1377
     1378        if (func) {
     1379            try {
    12471380                func();
    1248             } catch(e)
    1249             {
     1381            } catch (e) {
    12501382                this.status.status = this.status.ERROR;
    1251                 this.status.message = e;
     1383                this.status.message = String(e);
    12521384            };
    12531385        }
    12541386        this.set_timeout();
    12551387    };
     1388
     1389    Tests.prototype.get_timeout = function()
     1390    {
     1391        var metas = document.getElementsByTagName("meta");
     1392        for (var i = 0; i < metas.length; i++) {
     1393            if (metas[i].name == "timeout") {
     1394                if (metas[i].content == "long") {
     1395                    return settings.harness_timeout.long;
     1396                }
     1397                break;
     1398            }
     1399        }
     1400        return settings.harness_timeout.normal;
     1401    }
    12561402
    12571403    Tests.prototype.set_timeout = function()
     
    12591405        var this_obj = this;
    12601406        clearTimeout(this.timeout_id);
    1261         if (this.timeout_length !== null)
    1262         {
     1407        if (this.timeout_length !== null) {
    12631408            this.timeout_id = setTimeout(function() {
    12641409                                             this_obj.timeout();
     
    13091454                function(w, is_same_origin)
    13101455                {
    1311                     if(is_same_origin && w.start_callback)
    1312                     {
    1313                         try
    1314                         {
     1456                    if (is_same_origin && w.start_callback) {
     1457                        try {
    13151458                            w.start_callback(this_obj.properties);
    1316                         }
    1317                         catch(e)
    1318                         {
    1319                             if (debug)
    1320                             {
    1321                                 throw(e);
     1459                        } catch (e) {
     1460                            if (debug) {
     1461                                throw e;
    13221462                            }
    13231463                        }
    13241464                    }
    1325                     if (supports_post_message(w))
    1326                     {
     1465                    if (supports_post_message(w) && w !== self) {
    13271466                        w.postMessage({
    13281467                            type: "start",
     
    13351474    Tests.prototype.result = function(test)
    13361475    {
    1337         if (this.phase > this.phases.HAVE_RESULTS)
    1338         {
     1476        if (this.phase > this.phases.HAVE_RESULTS) {
    13391477            return;
    13401478        }
     
    13561494                function(w, is_same_origin)
    13571495                {
    1358                     if(is_same_origin && w.result_callback)
    1359                     {
    1360                         try
    1361                         {
     1496                    if (is_same_origin && w.result_callback) {
     1497                        try {
    13621498                            w.result_callback(test);
    1363                         }
    1364                         catch(e)
    1365                         {
    1366                             if(debug) {
     1499                        } catch (e) {
     1500                            if (debug) {
    13671501                                throw e;
    13681502                            }
    13691503                        }
    13701504                    }
    1371                     if (supports_post_message(w))
    1372                     {
     1505                    if (supports_post_message(w) && w !== self) {
    13731506                        w.postMessage({
    13741507                            type: "result",
     
    13781511                });
    13791512        this.processing_callbacks = false;
    1380         if (this_obj.all_done())
    1381         {
     1513        if (this_obj.all_done()) {
    13821514            this_obj.complete();
    13831515        }
     
    13931525            function(x)
    13941526            {
    1395                 if(x.status === x.NOTRUN)
    1396                 {
     1527                if (x.status === x.NOTRUN) {
    13971528                    this_obj.notify_result(x);
     1529                    x.cleanup();
    13981530                }
    13991531            }
     
    14111543                            return test.structured_clone();
    14121544                        });
    1413         if (this.status.status === null)
    1414         {
     1545        if (this.status.status === null) {
    14151546            this.status.status = this.status.OK;
    14161547        }
     
    14251556                function(w, is_same_origin)
    14261557                {
    1427                     if(is_same_origin && w.completion_callback)
    1428                     {
    1429                         try
    1430                         {
     1558                    if (is_same_origin && w.completion_callback) {
     1559                        try {
    14311560                            w.completion_callback(this_obj.tests, this_obj.status);
    1432                         }
    1433                         catch(e)
    1434                         {
    1435                             if (debug)
    1436                             {
     1561                        } catch (e) {
     1562                            if (debug) {
    14371563                                throw e;
    14381564                            }
    14391565                        }
    14401566                    }
    1441                     if (supports_post_message(w))
    1442                     {
     1567                    if (supports_post_message(w) && w !== self) {
    14431568                        w.postMessage({
    14441569                            type: "complete",
     
    14521577    var tests = new Tests();
    14531578
     1579    window.onerror = function(msg) {
     1580        if (!tests.allow_uncaught_exception) {
     1581            tests.status.status = tests.status.ERROR;
     1582            tests.status.message = msg;
     1583            tests.complete();
     1584        }
     1585    }
     1586
    14541587    function timeout() {
    1455         if (tests.timeout_length === null)
    1456         {
     1588        if (tests.timeout_length === null) {
    14571589            tests.timeout();
    14581590        }
     
    14831615
    14841616    function Output() {
    1485       this.output_document = document;
    1486       this.output_node = null;
    1487       this.done_count = 0;
    1488       this.enabled = settings.output;
    1489       this.phase = this.INITIAL;
     1617        this.output_document = document;
     1618        this.output_node = null;
     1619        this.done_count = 0;
     1620        this.enabled = settings.output;
     1621        this.phase = this.INITIAL;
    14901622    }
    14911623
     
    15221654    {
    15231655        var output_document;
    1524         if (typeof this.output_document === "function")
    1525         {
     1656        if (typeof this.output_document === "function") {
    15261657            output_document = this.output_document.apply(undefined);
    1527         } else
    1528         {
     1658        } else {
    15291659            output_document = this.output_document;
    15301660        }
    1531         if (!output_document)
    1532         {
     1661        if (!output_document) {
    15331662            return;
    15341663        }
    15351664        var node = output_document.getElementById("log");
    1536         if (node)
    1537         {
     1665        if (node) {
    15381666            this.output_document = output_document;
    15391667            this.output_node = node;
     
    15431671    Output.prototype.show_status = function(test)
    15441672    {
    1545         if (this.phase < this.STARTED)
    1546         {
     1673        if (this.phase < this.STARTED) {
    15471674            this.init();
    15481675        }
    1549         if (!this.enabled)
    1550         {
     1676        if (!this.enabled) {
    15511677            return;
    15521678        }
    1553         if (this.phase < this.HAVE_RESULTS)
    1554         {
     1679        if (this.phase < this.HAVE_RESULTS) {
    15551680            this.resolve_log();
    15561681            this.phase = this.HAVE_RESULTS;
    15571682        }
    15581683        this.done_count++;
    1559         if (this.output_node)
    1560         {
     1684        if (this.output_node) {
    15611685            if (this.done_count < 100
    15621686            || (this.done_count < 1000 && this.done_count % 100 == 0)
     
    15741698            return;
    15751699        }
    1576         if (!this.enabled)
    1577         {
     1700        if (!this.enabled) {
    15781701            return;
    15791702        }
     
    15841707
    15851708        var log = this.output_node;
    1586         if (!log)
    1587         {
     1709        if (!log) {
    15881710            return;
    15891711        }
    15901712        var output_document = this.output_document;
    15911713
    1592         while (log.lastChild)
    1593         {
     1714        while (log.lastChild) {
    15941715            log.removeChild(log.lastChild);
    15951716        }
     
    16051726        }
    16061727
     1728        var status_text_harness = {};
     1729        status_text_harness[harness_status.OK] = "OK";
     1730        status_text_harness[harness_status.ERROR] = "Error";
     1731        status_text_harness[harness_status.TIMEOUT] = "Timeout";
     1732
    16071733        var status_text = {};
    16081734        status_text[Test.prototype.PASS] = "Pass";
     
    16121738
    16131739        var status_number = {};
    1614         forEach(tests, function(test) {
     1740        forEach(tests,
     1741                function(test) {
    16151742                    var status = status_text[test.status];
    1616                     if (status_number.hasOwnProperty(status))
    1617                     {
     1743                    if (status_number.hasOwnProperty(status)) {
    16181744                        status_number[status] += 1;
    16191745                    } else {
     
    16291755        var summary_template = ["section", {"id":"summary"},
    16301756                                ["h2", {}, "Summary"],
     1757                                function(vars)
     1758                                {
     1759                                    if (harness_status.status === harness_status.OK) {
     1760                                        return null;
     1761                                    }
     1762
     1763                                    var status = status_text_harness[harness_status.status];
     1764                                    var rv = [["p", {"class":status_class(status)}]];
     1765
     1766                                    if (harness_status.status === harness_status.ERROR) {
     1767                                        rv[0].push("Harness encountered an error:");
     1768                                        rv.push(["pre", {}, harness_status.message]);
     1769                                    } else if (harness_status.status === harness_status.TIMEOUT) {
     1770                                        rv[0].push("Harness timed out.");
     1771                                    } else {
     1772                                        rv[0].push("Harness got an unexpected status.");
     1773                                    }
     1774
     1775                                    return rv;
     1776                                },
    16311777                                ["p", {}, "Found ${num_tests} tests"],
    16321778                                function(vars) {
    16331779                                    var rv = [["div", {}]];
    1634                                     var i=0;
     1780                                    var i = 0;
    16351781                                    while (status_text.hasOwnProperty(i)) {
    16361782                                        if (status_number.hasOwnProperty(status_text[i])) {
     
    16541800                             function(e)
    16551801                             {
    1656                                  if (output_document.getElementById("results") === null)
    1657                                  {
     1802                                 if (output_document.getElementById("results") === null) {
    16581803                                     e.preventDefault();
    16591804                                     return;
     
    16941839            return false;
    16951840        }
    1696        
     1841
    16971842        function get_assertion(test)
    16981843        {
     
    17051850            return '';
    17061851        }
    1707        
     1852
    17081853        log.appendChild(document.createElementNS(xhtml_ns, "section"));
    17091854        var assertions = has_assertions();
     
    17831928        if (typeof template === "function") {
    17841929            var replacement = template(substitutions);
    1785             if (replacement)
    1786             {
    1787                 var rv = substitute(replacement, substitutions);
    1788                 return rv;
    1789             }
    1790             else
    1791             {
     1930            if (!replacement) {
    17921931                return null;
    17931932            }
    1794         }
    1795         else if (is_single_node(template))
    1796         {
     1933
     1934            return substitute(replacement, substitutions);
     1935        }
     1936
     1937        if (is_single_node(template)) {
    17971938            return substitute_single(template, substitutions);
    17981939        }
    1799         else
    1800         {
    1801             return filter(map(template, function(x) {
    1802                                   return substitute(x, substitutions);
    1803                               }), function(x) {return x !== null;});
    1804         }
     1940
     1941        return filter(map(template, function(x) {
     1942                              return substitute(x, substitutions);
     1943                          }), function(x) {return x !== null;});
    18051944    }
    18061945
     
    18121951            var components = input.split(substitution_re);
    18131952            var rv = [];
    1814             for (var i=0; i<components.length; i+=2)
    1815             {
     1953            for (var i = 0; i < components.length; i += 2) {
    18161954                rv.push(components[i]);
    1817                 if (components[i+1])
    1818                 {
    1819                     rv.push(String(substitutions[components[i+1]]));
     1955                if (components[i + 1]) {
     1956                    rv.push(String(substitutions[components[i + 1]]));
    18201957                }
    18211958            }
     
    18361973        {
    18371974            rv[1] = {};
    1838             for (var name in template[1])
    1839             {
    1840                 if (attrs.hasOwnProperty(name))
    1841                 {
     1975            for (var name in template[1]) {
     1976                if (attrs.hasOwnProperty(name)) {
    18421977                    var new_name = do_substitution(name).join("");
    18431978                    var new_value = do_substitution(attrs[name]).join("");
     
    18491984        function substitute_children(children, rv)
    18501985        {
    1851             for (var i=0; i<children.length; i++)
    1852             {
     1986            for (var i = 0; i < children.length; i++) {
    18531987                if (children[i] instanceof Object) {
    18541988                    var replacement = substitute(children[i], substitutions);
    1855                     if (replacement !== null)
    1856                     {
    1857                         if (is_single_node(replacement))
    1858                         {
     1989                    if (replacement !== null) {
     1990                        if (is_single_node(replacement)) {
    18591991                            rv.push(replacement);
    1860                         }
    1861                         else
    1862                         {
     1992                        } else {
    18631993                            extend(rv, replacement);
    18641994                        }
    18651995                    }
    1866                 }
    1867                 else
    1868                 {
     1996                } else {
    18691997                    extend(rv, do_substitution(String(children[i])));
    18701998                }
     
    18762004    }
    18772005
    1878  function make_dom_single(template, doc)
    1879  {
    1880      var output_document = doc || document;
    1881      if (template[0] === "{text}")
    1882      {
    1883          var element = output_document.createTextNode("");
    1884          for (var i=1; i<template.length; i++)
    1885          {
    1886              element.data += template[i];
    1887          }
    1888      }
    1889      else
    1890      {
    1891          var element = output_document.createElementNS(xhtml_ns, template[0]);
    1892          for (var name in template[1]) {
    1893              if (template[1].hasOwnProperty(name))
    1894              {
    1895                  element.setAttribute(name, template[1][name]);
    1896              }
    1897          }
    1898          for (var i=2; i<template.length; i++)
    1899          {
    1900              if (template[i] instanceof Object)
    1901              {
    1902                  var sub_element = make_dom(template[i]);
    1903                  element.appendChild(sub_element);
    1904              }
    1905              else
    1906              {
    1907                  var text_node = output_document.createTextNode(template[i]);
    1908                  element.appendChild(text_node);
    1909              }
    1910          }
    1911      }
    1912 
    1913      return element;
    1914  }
    1915 
    1916 
    1917 
    1918  function make_dom(template, substitutions, output_document)
    1919     {
    1920         if (is_single_node(template))
    1921         {
     2006    function make_dom_single(template, doc)
     2007    {
     2008        var output_document = doc || document;
     2009        if (template[0] === "{text}") {
     2010            var element = output_document.createTextNode("");
     2011            for (var i = 1; i < template.length; i++) {
     2012                element.data += template[i];
     2013            }
     2014        } else {
     2015            var element = output_document.createElementNS(xhtml_ns, template[0]);
     2016            for (var name in template[1]) {
     2017                if (template[1].hasOwnProperty(name)) {
     2018                    element.setAttribute(name, template[1][name]);
     2019                }
     2020            }
     2021            for (var i = 2; i < template.length; i++) {
     2022                if (template[i] instanceof Object) {
     2023                    var sub_element = make_dom(template[i]);
     2024                    element.appendChild(sub_element);
     2025                } else {
     2026                    var text_node = output_document.createTextNode(template[i]);
     2027                    element.appendChild(text_node);
     2028                }
     2029            }
     2030        }
     2031
     2032        return element;
     2033    }
     2034
     2035
     2036
     2037    function make_dom(template, substitutions, output_document)
     2038    {
     2039        if (is_single_node(template)) {
    19222040            return make_dom_single(template, output_document);
    19232041        }
    1924         else
    1925         {
    1926             return map(template, function(x) {
    1927                            return make_dom_single(x, output_document);
    1928                        });
    1929         }
    1930     }
    1931 
    1932  function render(template, substitutions, output_document)
     2042
     2043        return map(template, function(x) {
     2044                       return make_dom_single(x, output_document);
     2045                   });
     2046    }
     2047
     2048    function render(template, substitutions, output_document)
    19332049    {
    19342050        return make_dom(substitute(template, substitutions), output_document);
     
    19402056    function assert(expected_true, function_name, description, error, substitutions)
    19412057    {
    1942         if (expected_true !== true)
    1943         {
     2058        if (expected_true !== true) {
    19442059            throw new AssertionError(make_message(function_name, description,
    19452060                                                  error, substitutions));
     
    19682083    function filter(array, callable, thisObj) {
    19692084        var rv = [];
    1970         for (var i=0; i<array.length; i++)
    1971         {
    1972             if (array.hasOwnProperty(i))
    1973             {
     2085        for (var i = 0; i < array.length; i++) {
     2086            if (array.hasOwnProperty(i)) {
    19742087                var pass = callable.call(thisObj, array[i], i, array);
    19752088                if (pass) {
     
    19852098        var rv = [];
    19862099        rv.length = array.length;
    1987         for (var i=0; i<array.length; i++)
    1988         {
    1989             if (array.hasOwnProperty(i))
    1990             {
     2100        for (var i = 0; i < array.length; i++) {
     2101            if (array.hasOwnProperty(i)) {
    19912102                rv[i] = callable.call(thisObj, array[i], i, array);
    19922103            }
     
    20022113    function forEach (array, callback, thisObj)
    20032114    {
    2004         for (var i=0; i<array.length; i++)
    2005         {
    2006             if (array.hasOwnProperty(i))
    2007             {
     2115        for (var i = 0; i < array.length; i++) {
     2116            if (array.hasOwnProperty(i)) {
    20082117                callback.call(thisObj, array[i], i, array);
    20092118            }
     
    20152124        var rv = {};
    20162125        var p;
    2017         for (p in a)
    2018         {
     2126        for (p in a) {
    20192127            rv[p] = a[p];
    20202128        }
     
    20292137        var components = name.split(".");
    20302138        var target = window;
    2031         for (var i=0; i<components.length - 1; i++)
    2032         {
    2033             if (!(components[i] in target))
    2034             {
     2139        for (var i = 0; i < components.length - 1; i++) {
     2140            if (!(components[i] in target)) {
    20352141                target[components[i]] = {};
    20362142            }
     
    20522158            var so;
    20532159            var origins = location.ancestorOrigins;
    2054             while (w != w.parent)
    2055             {
     2160            while (w != w.parent) {
    20562161                w = w.parent;
    20572162                // In WebKit, calls to parent windows' properties that aren't on the same
     
    20662171                // the origins of enclosing windows. See:
    20672172                // http://trac.webkit.org/changeset/113945.
    2068                 if(origins) {
     2173                if (origins) {
    20692174                    so = (location.origin == origins[i]);
    2070                 }
    2071                 else
    2072                 {
     2175                } else {
    20732176                    so = is_same_origin(w);
    20742177                }
     
    20772180            }
    20782181            w = window.opener;
    2079             if(w)
    2080             {
     2182            if (w) {
    20812183                // window.opener isn't included in the `location.ancestorOrigins` prop.
    20822184                // We'll just have to deal with a simple check and an error msg on WebKit
     
    20982200            'random_prop' in w;
    20992201            return true;
    2100         } catch(e) {
     2202        } catch (e) {
    21012203            return false;
    21022204        }
     
    21152217        // browser. So just doing an existence test here won't do, you also need
    21162218        // to wrap it in a try..cacth block.
    2117         try
    2118         {
     2219        try {
    21192220            type = typeof w.postMessage;
    2120             if (type === "function")
    2121             {
     2221            if (type === "function") {
    21222222                supports = true;
    21232223            }
     2224
    21242225            // IE8 supports postMessage, but implements it as a host object which
    21252226            // returns "object" as its `typeof`.
    2126             else if (type === "object")
    2127             {
     2227            else if (type === "object") {
    21282228                supports = true;
    21292229            }
     2230
    21302231            // This is the case where postMessage isn't supported AND accessing a
    21312232            // window property across origins does NOT throw (e.g. old Safari browser).
    2132             else
    2133             {
     2233            else {
    21342234                supports = false;
    21352235            }
    2136         }
    2137         catch(e) {
     2236        } catch (e) {
    21382237            // This is the case where postMessage isn't supported AND accessing a
    21392238            // window property across origins throws (e.g. old Firefox browser).
  • trunk/Source/WebCore/ChangeLog

    r171032 r171033  
     12014-07-11  Jer Noble  <jer.noble@apple.com>
     2
     3        [MSE] http/tests/media/media-source/mediasource-duration.html is failing.
     4        https://bugs.webkit.org/show_bug.cgi?id=134852
     5
     6        Reviewed by Eric Carlson.
     7
     8        Fixes the following tests:
     9        http/tests/media/media-source/mediasource-config-change-mp4-a-bitrate.html
     10        http/tests/media/media-source/mediasource-config-change-mp4-av-audio-bitrate.html
     11        http/tests/media/media-source/mediasource-config-change-mp4-av-video-bitrate.html
     12        http/tests/media/media-source/mediasource-config-change-mp4-v-bitrate.html
     13        http/tests/media/media-source/mediasource-config-change-mp4-v-framerate.html
     14        http/tests/media/media-source/mediasource-duration.html
     15        http/tests/media/media-source/mediasource-play.html
     16
     17        The primary change necessary to fix the mediasource-duration.html test was to add support
     18        for delaying the completion of a seek operation until the HTMLMediaElement's readyState
     19        rises to > HAVE_CURRENT_DATA. This is accomplished by modifying MediaSourcePrivate to have
     20        waitForSeekCompleted() and seekCompleted() virtual methods. These are called by MediaSource
     21        when a seek operation results in the current time moving outside the currently buffered time
     22        ranges, and when an append operation results in the readyState changing, respectively.
     23
     24        A number of other drive-by fixes were necessary to get this test fully passing, as noted
     25        below.
     26
     27        Make the MediaSource the primary owner of the media's duration, rather than the MediaSourcePrivate.
     28        Move the MediaSourcePrivateClient pointer to the MediaSourcePrivate from the MediaPlayerPrivate, so
     29        the MediaSource's duration can be retrieved.  While we're at it, do the same thing for buffered.
     30
     31        * Modules/mediasource/MediaSource.cpp:
     32        (WebCore::MediaSource::MediaSource): Initialize m_duration.
     33        (WebCore::MediaSource::duration): Simple accessor.
     34        (WebCore::MediaSource::setDurationInternal): Bring 'duration change algorithm' up to spec.
     35        (WebCore::MediaSource::setReadyState): Reset m_duration on close.
     36        * Modules/mediasource/MediaSource.h:
     37        * platform/graphics/MediaSourcePrivate.h:
     38        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
     39        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::load): Do not call setPrivateAndOpen().
     40        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::durationDouble): Pass through to MediaSourcePrivateAVFObjC.
     41        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::buffered): Ditto.
     42        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h:
     43        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
     44        (WebCore::MediaSourcePrivateAVFObjC::create): Call setPrivateAndOpen().
     45        (WebCore::MediaSourcePrivateAVFObjC::MediaSourcePrivateAVFObjC): Set m_client.
     46        (WebCore::MediaSourcePrivateAVFObjC::duration): Pass through to MediaSourcePrivateClient.
     47        (WebCore::MediaSourcePrivateAVFObjC::buffered): Ditto.
     48        (WebCore::MediaSourcePrivateAVFObjC::durationChanged): Pass through to MediaPlayerPrivateMediaSourceAVFObjC.
     49        (WebCore::MediaSourcePrivateAVFObjC::setDuration): Deleted.
     50        * platform/graphics/gstreamer/MediaSourceGStreamer.cpp:
     51        (WebCore::MediaSourceGStreamer::open): Pass in MediaSourcePrivateClient.
     52        (WebCore::MediaSourceGStreamer::MediaSourceGStreamer): Initialize m_mediaSource.
     53        (WebCore::MediaSourceGStreamer::durationChanged): Retrieve the duration from MediaSourcePrivateClient.
     54        (WebCore::MediaSourceGStreamer::markEndOfStream): Remove unnecssary ASSERT.
     55        (WebCore::MediaSourceGStreamer::unmarkEndOfStream): Ditto.
     56        (WebCore::MediaSourceGStreamer::setDuration): Deleted.
     57        * platform/graphics/gstreamer/MediaSourceGStreamer.h:
     58        * platform/mock/mediasource/MockMediaPlayerMediaSource.cpp:
     59        (WebCore::MockMediaPlayerMediaSource::load): Do not call setPrivateAndOpen().
     60        (WebCore::MockMediaPlayerMediaSource::buffered): Pass through to MockMediaSourcePrivate.
     61        (WebCore::MockMediaPlayerMediaSource::durationDouble): Ditto.
     62        (WebCore::MockMediaPlayerMediaSource::advanceCurrentTime): Ditto.
     63        * platform/mock/mediasource/MockMediaSourcePrivate.cpp:
     64        (WebCore::MockMediaSourcePrivate::create): Call setPrivateAndOpen().
     65        (WebCore::MockMediaSourcePrivate::MockMediaSourcePrivate): Set m_client.
     66        (WebCore::MockMediaSourcePrivate::duration): Pass through to MediaSourcePrivateClient.
     67        (WebCore::MockMediaSourcePrivate::buffered): Ditto.
     68        (WebCore::MockMediaSourcePrivate::durationChanged): Pass thorugh to MockMediaPlayerMediaSource.
     69        (WebCore::MockMediaSourcePrivate::setDuration): Deleted.
     70
     71        Route seekToTime through MediaSource, rather than through MediaSourcePrivate, so that
     72        the time can be compared against the buffered ranges, and trigger the delay of the seek
     73        operation if necessary. Add a seekTimer to MediaPlayerPrivateMediaSourceAVFObjC, as this
     74        guarantees the order of asynchronous operations, rather than callOnMainThread, which can
     75        cause async operations to occur out of order.
     76
     77        * Modules/mediasource/MediaSource.cpp:
     78        (WebCore::MediaSource::seekToTime): Bring up to spec.
     79        (WebCore::MediaSource::completeSeek): Ditto.
     80        (WebCore::MediaSource::monitorSourceBuffers): Call completeSeek() when appropriate.
     81        * Modules/mediasource/SourceBuffer.cpp:
     82        (WebCore::SourceBuffer::sourceBufferPrivateSeekToTime): Deleted.
     83        (WebCore::SourceBuffer::seekToTime): Renamed from sourceBufferPrivateSeekToTime().
     84        * platform/graphics/MediaSourcePrivate.h:
     85        * platform/graphics/MediaSourcePrivateClient.h:
     86        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
     87        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
     88        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::MediaPlayerPrivateMediaSourceAVFObjC): Add seekTimer. Only
     89            call timeChanged() if no longer seeking, thereby triggering a 'seeked' event.
     90        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::~MediaPlayerPrivateMediaSourceAVFObjC): Clear m_seekTimer.
     91        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekWithTolerance): Use m_seekTimer.
     92        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekTimerFired): Call seekInternal.
     93        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekInternal): Add logging.
     94        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::waitForSeekCompleted): Added.
     95        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekCompleted): Added; trigger 'seeked'.
     96        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::setReadyState): No longer attempt to finish seek when
     97            readyState changes here; this has been moved up to MediaSource.cpp.
     98        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h:
     99        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
     100        (WebCore::MediaSourcePrivateAVFObjC::waitForSeekCompleted): Pass through to MediaPlayerPrivateMediaSourceAVFObjC.
     101        (WebCore::MediaSourcePrivateAVFObjC::seekCompleted): Ditto.
     102        (WebCore::MediaSourcePrivateAVFObjC::seekToTime): Pass through to MediaSourcePrivateClient.
     103        (WebCore::MediaSourcePrivateAVFObjC::fastSeekTimeForMediaTime): Ditto.
     104        * platform/mock/mediasource/MockMediaPlayerMediaSource.cpp:
     105        (WebCore::MockMediaPlayerMediaSource::MockMediaPlayerMediaSource): Initialize m_seekCompleted.
     106        (WebCore::MockMediaPlayerMediaSource::seeking): Check for an uncompleted seek operation.
     107        (WebCore::MockMediaPlayerMediaSource::seekWithTolerance): Ditto.
     108        (WebCore::MockMediaPlayerMediaSource::waitForSeekCompleted): Added.
     109        (WebCore::MockMediaPlayerMediaSource::seekCompleted): Added; trigger 'seeked'.
     110        * platform/mock/mediasource/MockMediaPlayerMediaSource.h:
     111        * platform/mock/mediasource/MockMediaSourcePrivate.cpp:
     112        (WebCore::MockMediaSourcePrivate::waitForSeekCompleted): Pass through to MockMediaPlayerMediaSource.
     113        (WebCore::MockMediaSourcePrivate::seekCompleted): Ditto.
     114        * platform/mock/mediasource/MockMediaSourcePrivate.h:
     115
     116        Drive-by fixes.
     117
     118        * Modules/mediasource/MediaSource.cpp:
     119        (WebCore::MediaSource::streamEndedWithError): Re-order the steps in streamEndedWithError()
     120            to avoid the MediaSource being closed and re-opened by the resulting duration change
     121            operation.
     122        * Modules/mediasource/MediaSource.h:
     123        * Modules/mediasource/SourceBuffer.cpp:
     124        (WebCore::SourceBuffer::remove): Added logging.
     125        (WebCore::SourceBuffer::removeCodedFrames): Ditto.
     126        (WebCore::SourceBuffer::hasFutureTime): Swap an ASSERT for an early-return; it's possible
     127            for currentTime() to be outside of a buffered area.
     128        * Modules/mediasource/SourceBuffer.h:
     129        * html/HTMLMediaElement.cpp:
     130        (WebCore::HTMLMediaElement::parseAttribute): Do not issue an additional 'timeupdate' event
     131            after finishSeek() issues one of its own.
     132        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
     133        (WebCore::globalDataParserQueue): Allow parsing operations to happen concurrently on
     134            background queues.
     135
    11362014-07-12  Eric Carlson  <eric.carlson@apple.com>
    2137
  • trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp

    r171017 r171033  
    7777    : ActiveDOMObject(&context)
    7878    , m_mediaElement(0)
     79    , m_duration(std::numeric_limits<double>::quiet_NaN())
     80    , m_pendingSeekTime(MediaTime::invalidTime())
    7981    , m_readyState(closedKeyword())
    8082    , m_asyncEventQueue(*this)
     
    129131double MediaSource::duration() const
    130132{
    131     return isClosed() ? std::numeric_limits<float>::quiet_NaN() : m_private->duration().toDouble();
     133    return m_duration;
    132134}
    133135
     
    179181
    180182    return PlatformTimeRanges::create(intersectionRanges->ranges());
     183}
     184
     185void MediaSource::seekToTime(const MediaTime& time)
     186{
     187    // 2.4.3 Seeking
     188    // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#mediasource-seeking
     189
     190    m_pendingSeekTime = time;
     191
     192    // Run the following steps as part of the "Wait until the user agent has established whether or not the
     193    // media data for the new playback position is available, and, if it is, until it has decoded enough data
     194    // to play back that position" step of the seek algorithm:
     195    // 1. The media element looks for media segments containing the new playback position in each SourceBuffer
     196    // object in activeSourceBuffers.
     197    for (auto& sourceBuffer : *m_activeSourceBuffers) {
     198        // ↳ If one or more of the objects in activeSourceBuffers is missing media segments for the new
     199        // playback position
     200        if (!sourceBuffer->buffered()->ranges().contain(time)) {
     201            // 1.1 Set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
     202            m_private->setReadyState(MediaPlayer::HaveMetadata);
     203
     204            // 1.2 The media element waits until an appendBuffer() or an appendStream() call causes the coded
     205            // frame processing algorithm to set the HTMLMediaElement.readyState attribute to a value greater
     206            // than HAVE_METADATA.
     207            LOG(MediaSource, "MediaSource::seekToTime(%p) - waitForSeekCompleted()", this);
     208            m_private->waitForSeekCompleted();
     209            return;
     210        }
     211        // ↳ Otherwise
     212        // Continue
     213    }
     214
     215    completeSeek();
     216}
     217
     218void MediaSource::completeSeek()
     219{
     220    // 2.4.3 Seeking, ctd.
     221    // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#mediasource-seeking
     222
     223    ASSERT(m_pendingSeekTime.isValid());
     224
     225    // 2. The media element resets all decoders and initializes each one with data from the appropriate
     226    // initialization segment.
     227    // 3. The media element feeds coded frames from the active track buffers into the decoders starting
     228    // with the closest random access point before the new playback position.
     229    for (auto& sourceBuffer : *m_activeSourceBuffers)
     230        sourceBuffer->seekToTime(m_pendingSeekTime);
     231
     232    // 4. Resume the seek algorithm at the "Await a stable state" step.
     233    m_private->seekCompleted();
     234
     235    m_pendingSeekTime = MediaTime::invalidTime();
     236    monitorSourceBuffers();
    181237}
    182238
     
    218274        m_private->setReadyState(MediaPlayer::HaveEnoughData);
    219275
     276        if (m_pendingSeekTime.isValid())
     277            completeSeek();
     278
    220279        // 4. Abort these steps.
    221280        return;
     
    232291        m_private->setReadyState(MediaPlayer::HaveFutureData);
    233292
     293        if (m_pendingSeekTime.isValid())
     294            completeSeek();
     295
    234296        // 4. Abort these steps.
    235297        return;
     
    247309    // advance the media timeline.
    248310    m_private->setReadyState(MediaPlayer::HaveCurrentData);
    249    
     311
     312    if (m_pendingSeekTime.isValid())
     313        completeSeek();
     314
    250315    // 4. Abort these steps.
    251316}
     
    284349void MediaSource::setDurationInternal(double duration)
    285350{
    286     m_private->setDuration(MediaTime::createWithDouble(duration));
    287 }
    288 
     351    // Duration Change Algorithm
     352    // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#duration-change-algorithm
     353
     354    // 1. If the current value of duration is equal to new duration, then return.
     355    if (duration == m_duration)
     356        return;
     357
     358    // 2. Set old duration to the current value of duration.
     359    double oldDuration = m_duration;
     360
     361    // 3. Update duration to new duration.
     362    m_duration = duration;
     363
     364    // 4. If the new duration is less than old duration, then call remove(new duration, old duration)
     365    // on all objects in sourceBuffers.
     366    if (!isnan(oldDuration) && duration < oldDuration) {
     367        for (auto& sourceBuffer : *m_sourceBuffers)
     368            sourceBuffer->remove(duration, oldDuration, IGNORE_EXCEPTION);
     369    }
     370
     371    // 5. If a user agent is unable to partially render audio frames or text cues that start before and end after the
     372    // duration, then run the following steps:
     373    // 5.1 Update new duration to the highest end time reported by the buffered attribute across all SourceBuffer objects
     374    // in sourceBuffers.
     375    // 5.2 Update duration to new duration.
     376    // NOTE: Assume UA is able to partially render audio frames.
     377
     378    // 6. Update the media controller duration to new duration and run the HTMLMediaElement duration change algorithm.
     379    LOG(MediaSource, "MediaSource::setDurationInternal(%p) - duration(%g)", this, duration);
     380    m_private->durationChanged();
     381}
    289382
    290383void MediaSource::setReadyState(const AtomicString& state)
     
    298391        m_private.clear();
    299392        m_mediaElement = 0;
     393        m_duration = std::numeric_limits<double>::quiet_NaN();
    300394    }
    301395
     
    345439
    346440    // 2.4.7 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#end-of-stream-algorithm
    347     // 1. Change the readyState attribute value to "ended".
    348     // 2. Queue a task to fire a simple event named sourceended at the MediaSource.
    349     setReadyState(endedKeyword());
    350441
    351442    // 3.
    352443    if (error.isEmpty()) {
    353444        // ↳ If error is not set, is null, or is an empty string
    354         // 1. Run the duration change algorithm with new duration set to the highest end timestamp
    355         // across all SourceBuffer objects in sourceBuffers.
    356         MediaTime maxEndTimestamp;
    357         for (auto it = m_sourceBuffers->begin(), end = m_sourceBuffers->end(); it != end; ++it)
    358             maxEndTimestamp = std::max((*it)->highestPresentationEndTimestamp(), maxEndTimestamp);
    359         m_private->setDuration(maxEndTimestamp);
     445        // 1. Run the duration change algorithm with new duration set to the highest end time reported by
     446        // the buffered attribute across all SourceBuffer objects in sourceBuffers.
     447        double maxEndTime = 0;
     448        for (auto& sourceBuffer : *m_sourceBuffers) {
     449            if (auto length = sourceBuffer->buffered()->length())
     450                maxEndTime = std::max(sourceBuffer->buffered()->end(length - 1, IGNORE_EXCEPTION), maxEndTime);
     451        }
     452        setDurationInternal(maxEndTime);
    360453
    361454        // 2. Notify the media element that it now has all of the media data.
     
    397490        ec = INVALID_ACCESS_ERR;
    398491    }
     492
     493    // NOTE: Do steps 1 & 2 after step 3 to avoid the MediaSource's readyState being re-opened by a
     494    // remove() operation resulting from a duration change.
     495    // FIXME: Re-number or update this section once <https://www.w3.org/Bugs/Public/show_bug.cgi?id=26316> is resolved.
     496    // 1. Change the readyState attribute value to "ended".
     497    // 2. Queue a task to fire a simple event named sourceended at the MediaSource.
     498    setReadyState(endedKeyword());
    399499}
    400500
  • trunk/Source/WebCore/Modules/mediasource/MediaSource.h

    r170932 r171033  
    7373    virtual double duration() const override;
    7474    virtual std::unique_ptr<PlatformTimeRanges> buffered() const override;
     75    virtual void seekToTime(const MediaTime&) override;
    7576
    7677    bool attachToElement(HTMLMediaElement*);
     
    7879    bool isClosed() const;
    7980    void monitorSourceBuffers();
     81    void completeSeek();
    8082
    8183    void setDuration(double, ExceptionCode&);
     
    128130    RefPtr<SourceBufferList> m_activeSourceBuffers;
    129131    HTMLMediaElement* m_mediaElement;
     132    double m_duration;
     133    MediaTime m_pendingSeekTime;
    130134    AtomicString m_readyState;
    131135    GenericEventQueue m_asyncEventQueue;
  • trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp

    r171017 r171033  
    234234void SourceBuffer::remove(double start, double end, ExceptionCode& ec)
    235235{
     236    LOG(MediaSource, "SourceBuffer::remove(%p) - start(%s), end(%s)", this, toString(start).utf8().data(), toString(end).utf8().data());
    236237    // Section 3.2 remove() method steps.
    237238    // 1. If start is negative or greater than duration, then throw an InvalidAccessError exception and abort these steps.
     
    309310}
    310311
    311 void SourceBuffer::sourceBufferPrivateSeekToTime(SourceBufferPrivate*, const MediaTime& time)
    312 {
    313     LOG(MediaSource, "SourceBuffer::sourceBufferPrivateSeekToTime(%p) - time(%s)", this, toString(time).utf8().data());
     312void SourceBuffer::seekToTime(const MediaTime& time)
     313{
     314    LOG(MediaSource, "SourceBuffer::seekToTime(%p) - time(%s)", this, toString(time).utf8().data());
    314315
    315316    for (auto& trackBufferPair : m_trackBufferMap) {
     
    319320        reenqueueMediaForTime(trackBuffer, trackID, time);
    320321    }
    321 
    322     m_source->monitorSourceBuffers();
    323322}
    324323
     
    528527void SourceBuffer::removeCodedFrames(const MediaTime& start, const MediaTime& end)
    529528{
     529    LOG(MediaSource, "SourceBuffer::removeCodedFrames(%p) - start(%s), end(%s)", this, toString(start).utf8().data(), toString(end).utf8().data());
     530
    530531    // 3.5.9 Coded Frame Removal Algorithm
    531532    // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#sourcebuffer-coded-frame-removal
     
    14501451
    14511452    size_t found = ranges.find(nearest);
    1452     ASSERT(found != notFound);
     1453    if (found == notFound)
     1454        return false;
    14531455
    14541456    bool ignoredValid = false;
  • trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h

    r170976 r171033  
    7878    void removedFromMediaSource();
    7979    const MediaTime& highestPresentationEndTimestamp() const { return m_highestPresentationEndTimestamp; }
     80    void seekToTime(const MediaTime&);
    8081
    8182#if ENABLE(VIDEO_TRACK)
     
    117118    virtual bool sourceBufferPrivateHasVideo(const SourceBufferPrivate*) const override;
    118119    virtual void sourceBufferPrivateDidBecomeReadyForMoreSamples(SourceBufferPrivate*, AtomicString trackID) override;
    119     virtual void sourceBufferPrivateSeekToTime(SourceBufferPrivate*, const MediaTime&);
    120120    virtual MediaTime sourceBufferPrivateFastSeekTimeForMediaTime(SourceBufferPrivate*, const MediaTime&, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold);
    121121    virtual void sourceBufferPrivateAppendComplete(SourceBufferPrivate*, AppendResult) override;
  • trunk/Source/WebCore/html/HTMLMediaElement.cpp

    r170920 r171033  
    40924092    // it will only queue a 'timeupdate' event if we haven't already posted one at the current
    40934093    // movie time.
    4094     scheduleTimeupdateEvent(false);
     4094    else
     4095        scheduleTimeupdateEvent(false);
    40954096
    40964097    double now = currentTime();
  • trunk/Source/WebCore/platform/graphics/MediaSourcePrivate.h

    r169536 r171033  
    5252    enum AddStatus { Ok, NotSupported, ReachedIdLimit };
    5353    virtual AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&) = 0;
    54     virtual MediaTime duration() = 0;
    55     virtual void setDuration(const MediaTime&) = 0;
     54    virtual void durationChanged() = 0;
    5655    enum EndOfStreamStatus { EosNoError, EosNetworkError, EosDecodeError };
    5756    virtual void markEndOfStream(EndOfStreamStatus) = 0;
     
    6059    virtual MediaPlayer::ReadyState readyState() const = 0;
    6160    virtual void setReadyState(MediaPlayer::ReadyState) = 0;
     61
     62    virtual void waitForSeekCompleted() = 0;
     63    virtual void seekCompleted() = 0;
    6264};
    6365
  • trunk/Source/WebCore/platform/graphics/MediaSourcePrivateClient.h

    r165676 r171033  
    4343    virtual double duration() const = 0;
    4444    virtual std::unique_ptr<PlatformTimeRanges> buffered() const = 0;
     45    virtual void seekToTime(const MediaTime&) = 0;
    4546};
    4647
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h

    r170438 r171033  
    6666
    6767    void seekInternal();
     68    void waitForSeekCompleted();
     69    void seekCompleted();
    6870    void setLoadingProgresssed(bool flag) { m_loadingProgressed = flag; }
    6971    void setHasAvailableVideoFrame(bool flag) { m_hasAvailableVideoFrame = flag; }
     
    158160    void destroyLayer();
    159161    bool shouldBePlaying() const;
     162    void seekTimerFired(Timer<MediaPlayerPrivateMediaSourceAVFObjC>&);
    160163
    161164    // MediaPlayer Factory Methods
     
    183186    MediaPlayer* m_player;
    184187    WeakPtrFactory<MediaPlayerPrivateMediaSourceAVFObjC> m_weakPtrFactory;
    185     RefPtr<MediaSourcePrivateClient> m_mediaSource;
    186188    RefPtr<MediaSourcePrivateAVFObjC> m_mediaSourcePrivate;
    187189    RetainPtr<AVAsset> m_asset;
     
    191193    RetainPtr<id> m_timeJumpedObserver;
    192194    RetainPtr<id> m_durationObserver;
     195    Timer<MediaPlayerPrivateMediaSourceAVFObjC> m_seekTimer;
    193196    MediaPlayer::NetworkState m_networkState;
    194197    MediaPlayer::ReadyState m_readyState;
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm

    r170512 r171033  
    3030
    3131#import "CDMSession.h"
     32#import "Logging.h"
    3233#import "MediaSourcePrivateAVFObjC.h"
    3334#import "MediaSourcePrivateClient.h"
     
    135136    , m_weakPtrFactory(this)
    136137    , m_synchronizer(adoptNS([[getAVSampleBufferRenderSynchronizerClass() alloc] init]))
     138    , m_seekTimer(this, &MediaPlayerPrivateMediaSourceAVFObjC::seekTimerFired)
    137139    , m_networkState(MediaPlayer::Empty)
    138140    , m_readyState(MediaPlayer::HaveNothing)
     
    150152    // an arbitrarily large time value of once an hour:
    151153    __block auto weakThis = createWeakPtr();
    152     m_timeJumpedObserver = [m_synchronizer addPeriodicTimeObserverForInterval:toCMTime(MediaTime::createWithDouble(3600)) queue:dispatch_get_main_queue() usingBlock:^(CMTime){
     154    m_timeJumpedObserver = [m_synchronizer addPeriodicTimeObserverForInterval:toCMTime(MediaTime::createWithDouble(3600)) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
     155#if LOG_DISABLED
     156        UNUSED_PARAM(time);
     157#endif
    153158        // FIXME: Remove the below once <rdar://problem/15798050> is fixed.
    154159        if (!weakThis)
     
    156161
    157162        if (m_seeking && !m_pendingSeek) {
     163            LOG(MediaSource, "MediaPlayerPrivateMediaSourceAVFObjC::m_timeJumpedObserver(%p) - time(%s)", weakThis.get(), toString(toMediaTime(time)).utf8().data());
    158164            m_seeking = false;
     165
    159166            if (shouldBePlaying())
    160167                [m_synchronizer setRate:m_rate];
     168            if (!seeking())
     169                m_player->timeChanged();
    161170        }
    162 
    163         m_player->timeChanged();
    164171
    165172        if (m_pendingSeek)
     
    178185    if (m_durationObserver)
    179186        [m_synchronizer removeTimeObserver:m_durationObserver.get()];
     187
     188    m_seekTimer.stop();
    180189}
    181190
     
    287296}
    288297
    289 void MediaPlayerPrivateMediaSourceAVFObjC::load(const String& url, MediaSourcePrivateClient* source)
     298void MediaPlayerPrivateMediaSourceAVFObjC::load(const String& url, MediaSourcePrivateClient* client)
    290299{
    291300    UNUSED_PARAM(url);
    292301
    293     m_mediaSource = source;
    294     m_mediaSourcePrivate = MediaSourcePrivateAVFObjC::create(this);
    295 
    296     m_mediaSource->setPrivateAndOpen(*m_mediaSourcePrivate);
     302    m_mediaSourcePrivate = MediaSourcePrivateAVFObjC::create(this, client);
    297303}
    298304
     
    407413double MediaPlayerPrivateMediaSourceAVFObjC::durationDouble() const
    408414{
    409     return m_mediaSource ? m_mediaSource->duration() : 0;
     415    return m_mediaSourcePrivate ? m_mediaSourcePrivate->duration().toDouble() : 0;
    410416}
    411417
     
    432438void MediaPlayerPrivateMediaSourceAVFObjC::seekWithTolerance(double time, double negativeThreshold, double positiveThreshold)
    433439{
     440    LOG(MediaSource, "MediaPlayerPrivateMediaSourceAVFObjC::seekWithTolerance(%p) - time(%s), negativeThreshold(%s), positiveThreshold(%s)", this, toString(time).utf8().data(), toString(negativeThreshold).utf8().data(), toString(positiveThreshold).utf8().data());
    434441    m_seeking = true;
    435     m_seekCompleted = false;
    436442    auto weakThis = createWeakPtr();
    437443    m_pendingSeek = std::make_unique<PendingSeek>(MediaTime::createWithDouble(time), MediaTime::createWithDouble(negativeThreshold), MediaTime::createWithDouble(positiveThreshold));
    438444
    439     callOnMainThread([weakThis] {
    440         if (!weakThis)
    441             return;
    442         weakThis.get()->seekInternal();
    443     });
     445    if (m_seekTimer.isActive())
     446        m_seekTimer.stop();
     447    m_seekTimer.startOneShot(0);
     448}
     449
     450void MediaPlayerPrivateMediaSourceAVFObjC::seekTimerFired(Timer<MediaPlayerPrivateMediaSourceAVFObjC>&)
     451{
     452    seekInternal();
    444453}
    445454
     
    461470        seekTime = m_mediaSourcePrivate->fastSeekTimeForMediaTime(pendingSeek->targetTime, pendingSeek->positiveThreshold, pendingSeek->negativeThreshold);
    462471
     472    LOG(MediaSource, "MediaPlayerPrivateMediaSourceAVFObjC::seekInternal(%p) - seekTime(%s)", this, toString(seekTime).utf8().data());
     473
    463474    [m_synchronizer setRate:0 time:toCMTime(seekTime)];
    464475    m_mediaSourcePrivate->seekToTime(seekTime);
     476}
     477
     478void MediaPlayerPrivateMediaSourceAVFObjC::waitForSeekCompleted()
     479{
     480    if (!m_seeking)
     481        return;
     482    LOG(MediaSource, "MediaPlayerPrivateMediaSourceAVFObjC::waitForSeekCompleted(%p)", this);
     483    m_seekCompleted = false;
     484}
     485
     486void MediaPlayerPrivateMediaSourceAVFObjC::seekCompleted()
     487{
     488    if (m_seekCompleted)
     489        return;
     490    LOG(MediaSource, "MediaPlayerPrivateMediaSourceAVFObjC::seekCompleted(%p)", this);
     491    m_seekCompleted = true;
     492    if (!m_seeking)
     493        m_player->timeChanged();
    465494}
    466495
     
    504533std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateMediaSourceAVFObjC::buffered() const
    505534{
    506     return m_mediaSource ? m_mediaSource->buffered() : PlatformTimeRanges::create();
     535    return m_mediaSourcePrivate ? m_mediaSourcePrivate->buffered() : PlatformTimeRanges::create();
    507536}
    508537
     
    637666    NSArray* times = @[[NSValue valueWithCMTime:toCMTime(duration)]];
    638667    m_durationObserver = [m_synchronizer addBoundaryTimeObserverForTimes:times queue:dispatch_get_main_queue() usingBlock:[weakThis] {
    639         if (weakThis)
     668        if (weakThis) {
    640669            weakThis->pauseInternal();
     670            weakThis->m_player->timeChanged();
     671        }
    641672    }];
    642673
     
    676707
    677708    m_readyState = readyState;
    678 
    679     if (!m_seekCompleted && m_readyState >= MediaPlayer::HaveCurrentData) {
    680         m_seekCompleted = true;
    681         m_player->timeChanged();
    682     }
    683709
    684710    if (shouldBePlaying())
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h

    r170014 r171033  
    4646class CDMSession;
    4747class MediaPlayerPrivateMediaSourceAVFObjC;
     48class MediaSourcePrivateClient;
    4849class SourceBufferPrivateAVFObjC;
    4950class TimeRanges;
     
    5152class MediaSourcePrivateAVFObjC final : public MediaSourcePrivate {
    5253public:
    53     static RefPtr<MediaSourcePrivateAVFObjC> create(MediaPlayerPrivateMediaSourceAVFObjC*);
     54    static RefPtr<MediaSourcePrivateAVFObjC> create(MediaPlayerPrivateMediaSourceAVFObjC*, MediaSourcePrivateClient*);
    5455    virtual ~MediaSourcePrivateAVFObjC();
    5556
     
    5859
    5960    virtual AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&) override;
    60     virtual MediaTime duration() override;
    61     virtual void setDuration(const MediaTime&) override;
     61    virtual void durationChanged() override;
    6262    virtual void markEndOfStream(EndOfStreamStatus) override;
    6363    virtual void unmarkEndOfStream() override;
    6464    virtual MediaPlayer::ReadyState readyState() const override;
    6565    virtual void setReadyState(MediaPlayer::ReadyState) override;
     66    virtual void waitForSeekCompleted() override;
     67    virtual void seekCompleted() override;
     68
     69    MediaTime duration();
     70    std::unique_ptr<PlatformTimeRanges> buffered();
    6671
    6772    bool hasAudio() const;
    6873    bool hasVideo() const;
    6974
    70     void seekToTime(MediaTime);
    71     MediaTime fastSeekTimeForMediaTime(MediaTime, MediaTime negativeThreshold, MediaTime positiveThreshold);
     75    void seekToTime(const MediaTime&);
     76    MediaTime fastSeekTimeForMediaTime(const MediaTime&, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold);
    7277    IntSize naturalSize() const;
    7378
     
    7782
    7883private:
    79     MediaSourcePrivateAVFObjC(MediaPlayerPrivateMediaSourceAVFObjC*);
     84    MediaSourcePrivateAVFObjC(MediaPlayerPrivateMediaSourceAVFObjC*, MediaSourcePrivateClient*);
    8085
    8186    void sourceBufferPrivateDidChangeActiveState(SourceBufferPrivateAVFObjC*, bool active);
     
    9095
    9196    MediaPlayerPrivateMediaSourceAVFObjC* m_player;
    92     MediaTime m_duration;
     97    RefPtr<MediaSourcePrivateClient> m_client;
    9398    Vector<RefPtr<SourceBufferPrivateAVFObjC>> m_sourceBuffers;
    9499    Vector<SourceBufferPrivateAVFObjC*> m_activeSourceBuffers;
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm

    r170014 r171033  
    3333#import "ExceptionCodePlaceholder.h"
    3434#import "MediaPlayerPrivateMediaSourceAVFObjC.h"
     35#import "MediaSourcePrivateClient.h"
    3536#import "SourceBufferPrivateAVFObjC.h"
    3637#import "SoftLinking.h"
     
    4445#pragma mark MediaSourcePrivateAVFObjC
    4546
    46 RefPtr<MediaSourcePrivateAVFObjC> MediaSourcePrivateAVFObjC::create(MediaPlayerPrivateMediaSourceAVFObjC* parent)
    47 {
    48     return adoptRef(new MediaSourcePrivateAVFObjC(parent));
    49 }
    50 
    51 MediaSourcePrivateAVFObjC::MediaSourcePrivateAVFObjC(MediaPlayerPrivateMediaSourceAVFObjC* parent)
     47RefPtr<MediaSourcePrivateAVFObjC> MediaSourcePrivateAVFObjC::create(MediaPlayerPrivateMediaSourceAVFObjC* parent, MediaSourcePrivateClient* client)
     48{
     49    RefPtr<MediaSourcePrivateAVFObjC> mediaSourcePrivate = adoptRef(new MediaSourcePrivateAVFObjC(parent, client));
     50    client->setPrivateAndOpen(*mediaSourcePrivate);
     51    return mediaSourcePrivate;
     52}
     53
     54MediaSourcePrivateAVFObjC::MediaSourcePrivateAVFObjC(MediaPlayerPrivateMediaSourceAVFObjC* parent, MediaSourcePrivateClient* client)
    5255    : m_player(parent)
    53     , m_duration(MediaTime::invalidTime())
     56    , m_client(client)
    5457    , m_isEnded(false)
    5558{
     
    9295MediaTime MediaSourcePrivateAVFObjC::duration()
    9396{
    94     return m_duration;
    95 }
    96 
    97 void MediaSourcePrivateAVFObjC::setDuration(const MediaTime& duration)
    98 {
    99     if (duration == m_duration)
    100         return;
    101 
    102     m_duration = duration;
     97    return MediaTime::createWithDouble(m_client->duration());
     98}
     99
     100std::unique_ptr<PlatformTimeRanges> MediaSourcePrivateAVFObjC::buffered()
     101{
     102    return m_client->buffered();
     103}
     104
     105void MediaSourcePrivateAVFObjC::durationChanged()
     106{
    103107    m_player->durationChanged();
    104108}
     
    125129{
    126130    m_player->setReadyState(readyState);
     131}
     132
     133void MediaSourcePrivateAVFObjC::waitForSeekCompleted()
     134{
     135    m_player->waitForSeekCompleted();
     136}
     137
     138void MediaSourcePrivateAVFObjC::seekCompleted()
     139{
     140    m_player->seekCompleted();
    127141}
    128142
     
    179193}
    180194
    181 void MediaSourcePrivateAVFObjC::seekToTime(MediaTime time)
    182 {
    183     for (auto& buffer : m_activeSourceBuffers)
    184         buffer->seekToTime(time);
    185 }
    186 
    187 MediaTime MediaSourcePrivateAVFObjC::fastSeekTimeForMediaTime(MediaTime targetTime, MediaTime negativeThreshold, MediaTime positiveThreshold)
     195void MediaSourcePrivateAVFObjC::seekToTime(const MediaTime& time)
     196{
     197    m_client->seekToTime(time);
     198}
     199
     200MediaTime MediaSourcePrivateAVFObjC::fastSeekTimeForMediaTime(const MediaTime& targetTime, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold)
    188201{
    189202    MediaTime seekTime = targetTime;
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm

    r171017 r171033  
    123123- (void)setShouldProvideMediaData:(BOOL)shouldProvideMediaData forTrackID:(CMPersistentTrackID)trackID;
    124124- (BOOL)shouldProvideMediaDataForTrackID:(CMPersistentTrackID)trackID;
     125- (void)providePendingMediaData;
    125126- (void)processContentKeyResponseData:(NSData *)contentKeyResponseData forTrackID:(CMPersistentTrackID)trackID;
    126127- (void)processContentKeyResponseError:(NSError *)error forTrackID:(CMPersistentTrackID)trackID;
     
    683684    static dispatch_once_t onceToken;
    684685    dispatch_once(&onceToken, ^{
    685         globalQueue = dispatch_queue_create("SourceBufferPrivateAVFObjC data parser queue", DISPATCH_QUEUE_SERIAL);
     686        globalQueue = dispatch_queue_create("SourceBufferPrivateAVFObjC data parser queue", DISPATCH_QUEUE_CONCURRENT);
    686687    });
    687688    return globalQueue;
  • trunk/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.cpp

    r169539 r171033  
    4343void MediaSourceGStreamer::open(MediaSourcePrivateClient* mediaSource, WebKitMediaSrc* src)
    4444{
    45     mediaSource->setPrivateAndOpen(adoptRef(*new MediaSourceGStreamer(src)));
     45    ASSERT(mediaSource);
     46    mediaSource->setPrivateAndOpen(adoptRef(*new MediaSourceGStreamer(mediaSource, src)));
    4647}
    4748
    48 MediaSourceGStreamer::MediaSourceGStreamer(WebKitMediaSrc* src)
     49MediaSourceGStreamer::MediaSourceGStreamer(MediaSourcePrivateClient* mediaSource, WebKitMediaSrc* src)
    4950    : m_client(adoptRef(new MediaSourceClientGstreamer(src)))
     51    , m_mediaSource(mediaSource)
    5052    , m_readyState(MediaPlayer::HaveNothing)
    5153{
     54    ASSERT(m_client);
    5255}
    5356
     
    6265}
    6366
    64 void MediaSourceGStreamer::setDuration(const MediaTime& duration)
     67void MediaSourceGStreamer::durationChanged()
    6568{
    66     ASSERT(m_client);
    67     m_duration = duration;
    68     m_client->didReceiveDuration(duration.toDouble());
     69    m_client->didReceiveDuration(m_mediaSource->duration());
    6970}
    7071
    7172void MediaSourceGStreamer::markEndOfStream(EndOfStreamStatus)
    7273{
    73     ASSERT(m_client);
    7474    m_client->didFinishLoading(0);
    7575}
     
    7777void MediaSourceGStreamer::unmarkEndOfStream()
    7878{
    79     ASSERT(m_client);
    8079}
    8180
  • trunk/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.h

    r169537 r171033  
    4343    static void open(MediaSourcePrivateClient*, WebKitMediaSrc*);
    4444    ~MediaSourceGStreamer();
    45     AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&);
    46     MediaTime duration() { return m_duration; }
    47     void setDuration(const MediaTime&);
    48     void markEndOfStream(EndOfStreamStatus);
    49     void unmarkEndOfStream();
    50     MediaPlayer::ReadyState readyState() const { return m_readyState; }
    51     void setReadyState(MediaPlayer::ReadyState readyState) { m_readyState = readyState; }
    5245
    5346private:
     47    // MediaSourcePrivate
     48    virtual AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&) override;
     49    virtual void durationChanged() override;
     50    virtual void markEndOfStream(EndOfStreamStatus) override;
     51    virtual void unmarkEndOfStream() override;
     52    virtual MediaPlayer::ReadyState readyState() const override { return m_readyState; }
     53    virtual void setReadyState(MediaPlayer::ReadyState readyState) override { m_readyState = readyState; }
     54    virtual void waitForSeekCompleted() override { }
     55    virtual void seekCompleted() override { }
     56
    5457    RefPtr<MediaSourceClientGstreamer> m_client;
    55     MediaSourceGStreamer(WebKitMediaSrc*);
    56     MediaTime m_duration;
     58    MediaSourcePrivateClient* m_mediaSource;
     59    MediaSourceGStreamer(MediaSourcePrivateClient*, WebKitMediaSrc*);
    5760    MediaPlayer::ReadyState m_readyState;
    5861};
  • trunk/Source/WebCore/platform/mock/mediasource/MockMediaPlayerMediaSource.cpp

    r169568 r171033  
    8585    : m_player(player)
    8686    , m_currentTime(MediaTime::zeroTime())
    87     , m_duration(MediaTime::zeroTime())
    8887    , m_readyState(MediaPlayer::HaveNothing)
    8988    , m_networkState(MediaPlayer::Empty)
    9089    , m_playing(false)
     90    , m_seekCompleted(true)
    9191{
    9292}
     
    103103void MockMediaPlayerMediaSource::load(const String&, MediaSourcePrivateClient* source)
    104104{
    105     m_mediaSource = source;
    106     m_mediaSourcePrivate = MockMediaSourcePrivate::create(this);
    107     m_mediaSource->setPrivateAndOpen(*m_mediaSourcePrivate);
     105    m_mediaSourcePrivate = MockMediaSourcePrivate::create(this, source);
    108106}
    109107
     
    144142bool MockMediaPlayerMediaSource::seeking() const
    145143{
     144    return !m_seekCompleted;
     145}
     146
     147bool MockMediaPlayerMediaSource::paused() const
     148{
     149    return !m_playing;
     150}
     151
     152MediaPlayer::NetworkState MockMediaPlayerMediaSource::networkState() const
     153{
     154    return m_networkState;
     155}
     156
     157MediaPlayer::ReadyState MockMediaPlayerMediaSource::readyState() const
     158{
     159    return m_readyState;
     160}
     161
     162double MockMediaPlayerMediaSource::maxTimeSeekableDouble() const
     163{
     164    return m_duration.toDouble();
     165}
     166
     167std::unique_ptr<PlatformTimeRanges> MockMediaPlayerMediaSource::buffered() const
     168{
     169    if (m_mediaSourcePrivate)
     170        return m_mediaSourcePrivate->buffered();
     171
     172    return PlatformTimeRanges::create();
     173}
     174
     175bool MockMediaPlayerMediaSource::didLoadingProgress() const
     176{
    146177    return false;
    147178}
    148179
    149 bool MockMediaPlayerMediaSource::paused() const
    150 {
    151     return !m_playing;
    152 }
    153 
    154 MediaPlayer::NetworkState MockMediaPlayerMediaSource::networkState() const
    155 {
    156     return m_networkState;
    157 }
    158 
    159 MediaPlayer::ReadyState MockMediaPlayerMediaSource::readyState() const
    160 {
    161     return m_readyState;
    162 }
    163 
    164 double MockMediaPlayerMediaSource::maxTimeSeekableDouble() const
    165 {
    166     return m_duration.toDouble();
    167 }
    168 
    169 std::unique_ptr<PlatformTimeRanges> MockMediaPlayerMediaSource::buffered() const
    170 {
    171     if (m_mediaSource)
    172         return m_mediaSource->buffered();
    173 
    174     return PlatformTimeRanges::create();
    175 }
    176 
    177 bool MockMediaPlayerMediaSource::didLoadingProgress() const
    178 {
    179     return false;
    180 }
    181 
    182180void MockMediaPlayerMediaSource::setSize(const IntSize&)
    183181{
     
    195193double MockMediaPlayerMediaSource::durationDouble() const
    196194{
    197     return m_duration.toDouble();
     195    return m_mediaSourcePrivate ? m_mediaSourcePrivate->duration() : 0;
    198196}
    199197
     
    205203    } else
    206204        m_currentTime = m_mediaSourcePrivate->seekToTime(MediaTime::createWithDouble(time), MediaTime::createWithDouble(negativeTolerance), MediaTime::createWithDouble(positiveTolerance));
    207     m_player->timeChanged();
    208 
    209     if (m_playing)
    210         callOnMainThread(bind(&MockMediaPlayerMediaSource::advanceCurrentTime, this));
     205
     206    if (m_seekCompleted) {
     207        m_player->timeChanged();
     208
     209        if (m_playing)
     210            callOnMainThread(bind(&MockMediaPlayerMediaSource::advanceCurrentTime, this));
     211    }
    211212}
    212213
    213214void MockMediaPlayerMediaSource::advanceCurrentTime()
    214215{
    215     if (!m_mediaSource)
    216         return;
    217 
    218     auto buffered = m_mediaSource->buffered();
     216    if (!m_mediaSourcePrivate)
     217        return;
     218
     219    auto buffered = m_mediaSourcePrivate->buffered();
    219220    size_t pos = buffered->find(m_currentTime);
    220221    if (pos == notFound)
     
    253254}
    254255
     256void MockMediaPlayerMediaSource::waitForSeekCompleted()
     257{
     258    m_seekCompleted = false;
     259}
     260
     261void MockMediaPlayerMediaSource::seekCompleted()
     262{
     263    if (m_seekCompleted)
     264        return;
     265    m_seekCompleted = true;
     266
     267    m_player->timeChanged();
     268
     269    if (m_playing)
     270        callOnMainThread(bind(&MockMediaPlayerMediaSource::advanceCurrentTime, this));
     271}
     272
    255273unsigned long MockMediaPlayerMediaSource::totalVideoFrames()
    256274{
  • trunk/Source/WebCore/platform/mock/mediasource/MockMediaPlayerMediaSource.h

    r169536 r171033  
    5353    void setReadyState(MediaPlayer::ReadyState);
    5454    void setNetworkState(MediaPlayer::NetworkState);
     55    void waitForSeekCompleted();
     56    void seekCompleted();
    5557
    5658private:
     
    8486
    8587    MediaPlayer* m_player;
    86     RefPtr<MediaSourcePrivateClient> m_mediaSource;
    8788    RefPtr<MockMediaSourcePrivate> m_mediaSourcePrivate;
    8889
     
    9293    MediaPlayer::NetworkState m_networkState;
    9394    bool m_playing;
     95    bool m_seekCompleted;
    9496};
    9597
  • trunk/Source/WebCore/platform/mock/mediasource/MockMediaSourcePrivate.cpp

    r169536 r171033  
    3131#include "ContentType.h"
    3232#include "ExceptionCodePlaceholder.h"
     33#include "MediaSourcePrivateClient.h"
    3334#include "MockMediaPlayerMediaSource.h"
    3435#include "MockSourceBufferPrivate.h"
     
    3637namespace WebCore {
    3738
    38 RefPtr<MockMediaSourcePrivate> MockMediaSourcePrivate::create(MockMediaPlayerMediaSource* parent)
     39RefPtr<MockMediaSourcePrivate> MockMediaSourcePrivate::create(MockMediaPlayerMediaSource* parent, MediaSourcePrivateClient* client)
    3940{
    40     return adoptRef(new MockMediaSourcePrivate(parent));
     41    RefPtr<MockMediaSourcePrivate> mediaSourcePrivate = adoptRef(new MockMediaSourcePrivate(parent, client));
     42    client->setPrivateAndOpen(*mediaSourcePrivate);
     43    return mediaSourcePrivate;
    4144}
    4245
    43 MockMediaSourcePrivate::MockMediaSourcePrivate(MockMediaPlayerMediaSource* parent)
     46MockMediaSourcePrivate::MockMediaSourcePrivate(MockMediaPlayerMediaSource* parent, MediaSourcePrivateClient* client)
    4447    : m_player(parent)
    45     , m_duration(MediaTime::invalidTime())
     48    , m_client(client)
    4649    , m_isEnded(false)
    4750    , m_totalVideoFrames(0)
     
    8588}
    8689
    87 MediaTime MockMediaSourcePrivate::duration()
     90double MockMediaSourcePrivate::duration()
    8891{
    89     return m_duration;
     92    return m_client->duration();
    9093}
    9194
    92 void MockMediaSourcePrivate::setDuration(const MediaTime& duration)
     95std::unique_ptr<PlatformTimeRanges> MockMediaSourcePrivate::buffered()
    9396{
    94     if (duration == m_duration)
    95         return;
     97    return m_client->buffered();
     98}
    9699
    97     m_duration = duration;
    98     m_player->updateDuration(duration);
     100void MockMediaSourcePrivate::durationChanged()
     101{
     102    m_player->updateDuration(MediaTime::createWithDouble(duration()));
    99103}
    100104
     
    119123{
    120124    m_player->setReadyState(readyState);
     125}
     126
     127void MockMediaSourcePrivate::waitForSeekCompleted()
     128{
     129    m_player->waitForSeekCompleted();
     130}
     131
     132void MockMediaSourcePrivate::seekCompleted()
     133{
     134    m_player->seekCompleted();
    121135}
    122136
  • trunk/Source/WebCore/platform/mock/mediasource/MockMediaSourcePrivate.h

    r169536 r171033  
    4040class MockMediaSourcePrivate final : public MediaSourcePrivate {
    4141public:
    42     static RefPtr<MockMediaSourcePrivate> create(MockMediaPlayerMediaSource*);
     42    static RefPtr<MockMediaSourcePrivate> create(MockMediaPlayerMediaSource*, MediaSourcePrivateClient*);
    4343    virtual ~MockMediaSourcePrivate();
    4444
     
    4747    bool hasAudio() const;
    4848    bool hasVideo() const;
     49
     50    double duration();
     51    std::unique_ptr<PlatformTimeRanges> buffered();
    4952
    5053    MockMediaPlayerMediaSource* player() const { return m_player; }
     
    6467
    6568private:
    66     MockMediaSourcePrivate(MockMediaPlayerMediaSource*);
     69    MockMediaSourcePrivate(MockMediaPlayerMediaSource*, MediaSourcePrivateClient*);
    6770
    6871    // MediaSourcePrivate Overrides
    6972    virtual AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&) override;
    70     virtual MediaTime duration() override;
    71     virtual void setDuration(const MediaTime&) override;
     73    virtual void durationChanged() override;
    7274    virtual void markEndOfStream(EndOfStreamStatus) override;
    7375    virtual void unmarkEndOfStream() override;
    7476    virtual MediaPlayer::ReadyState readyState() const override;
    7577    virtual void setReadyState(MediaPlayer::ReadyState) override;
     78    virtual void waitForSeekCompleted() override;
     79    virtual void seekCompleted() override;
    7680
    7781    void sourceBufferPrivateDidChangeActiveState(MockSourceBufferPrivate*, bool active);
     
    8185
    8286    MockMediaPlayerMediaSource* m_player;
    83     MediaTime m_duration;
     87    RefPtr<MediaSourcePrivateClient> m_client;
    8488    Vector<RefPtr<MockSourceBufferPrivate>> m_sourceBuffers;
    8589    Vector<MockSourceBufferPrivate*> m_activeSourceBuffers;
Note: See TracChangeset for help on using the changeset viewer.