mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-09 09:39:39 +00:00
LibWeb: Resolve FIXME in media select resource algorithm
Fixes at least three WPT test that were previously timing out: - html/semantics/embedded-content/media-elements/error-codes/error.html - html/semantics/embedded-content/media-elements/location-of-the-media-resource/currentSrc.html - html/semantics/embedded-content/the-video-element/video_crash_empty_src.html
This commit is contained in:
parent
bfc79a403d
commit
e1d2582680
Notes:
github-actions[bot]
2025-06-16 11:29:21 +00:00
Author: https://github.com/prajjwal000 🔰
Commit: e1d2582680
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4956
Reviewed-by: https://github.com/AtkinsSJ
Reviewed-by: https://github.com/shannonbooth ✅
8 changed files with 334 additions and 115 deletions
|
@ -40,6 +40,7 @@
|
||||||
#include <LibWeb/MimeSniff/MimeType.h>
|
#include <LibWeb/MimeSniff/MimeType.h>
|
||||||
#include <LibWeb/Page/Page.h>
|
#include <LibWeb/Page/Page.h>
|
||||||
#include <LibWeb/Painting/Paintable.h>
|
#include <LibWeb/Painting/Paintable.h>
|
||||||
|
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||||
#include <LibWeb/WebIDL/Promise.h>
|
#include <LibWeb/WebIDL/Promise.h>
|
||||||
|
|
||||||
namespace Web::HTML {
|
namespace Web::HTML {
|
||||||
|
@ -813,138 +814,140 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::select_resource()
|
||||||
// 3. Set the media element's delaying-the-load-event flag to true (this delays the load event).
|
// 3. Set the media element's delaying-the-load-event flag to true (this delays the load event).
|
||||||
m_delaying_the_load_event.emplace(document());
|
m_delaying_the_load_event.emplace(document());
|
||||||
|
|
||||||
// FIXME: 4. Await a stable state, allowing the task that invoked this algorithm to continue. The synchronous section consists of all the remaining
|
// 4. Await a stable state, allowing the task that invoked this algorithm to continue. The synchronous section consists of all the remaining
|
||||||
// steps of this algorithm until the algorithm says the synchronous section has ended. (Steps in synchronous sections are marked with ⌛.)
|
// steps of this algorithm until the algorithm says the synchronous section has ended. (Steps in synchronous sections are marked with ⌛.)
|
||||||
|
|
||||||
// FIXME: 5. ⌛ If the media element's blocked-on-parser flag is false, then populate the list of pending text tracks.
|
queue_a_microtask(&document(), GC::create_function(realm.heap(), [this, &realm]() {
|
||||||
|
// FIXME: 5. ⌛ If the media element's blocked-on-parser flag is false, then populate the list of pending text tracks.
|
||||||
|
|
||||||
Optional<SelectMode> mode;
|
Optional<SelectMode> mode;
|
||||||
GC::Ptr<HTMLSourceElement> candidate;
|
GC::Ptr<HTMLSourceElement> candidate;
|
||||||
|
|
||||||
// 6. FIXME: ⌛ If the media element has an assigned media provider object, then let mode be object.
|
// 6. FIXME: ⌛ If the media element has an assigned media provider object, then let mode be object.
|
||||||
|
|
||||||
// ⌛ Otherwise, if the media element has no assigned media provider object but has a src attribute, then let mode be attribute.
|
// ⌛ Otherwise, if the media element has no assigned media provider object but has a src attribute, then let mode be attribute.
|
||||||
if (has_attribute(HTML::AttributeNames::src)) {
|
if (has_attribute(HTML::AttributeNames::src)) {
|
||||||
mode = SelectMode::Attribute;
|
mode = SelectMode::Attribute;
|
||||||
}
|
}
|
||||||
// ⌛ Otherwise, if the media element does not have an assigned media provider object and does not have a src attribute, but does have
|
// ⌛ Otherwise, if the media element does not have an assigned media provider object and does not have a src attribute, but does have
|
||||||
// a source element child, then let mode be children and let candidate be the first such source element child in tree order.
|
// a source element child, then let mode be children and let candidate be the first such source element child in tree order.
|
||||||
else if (auto* source_element = first_child_of_type<HTMLSourceElement>()) {
|
else if (auto* source_element = first_child_of_type<HTMLSourceElement>()) {
|
||||||
mode = SelectMode::Children;
|
mode = SelectMode::Children;
|
||||||
candidate = source_element;
|
candidate = source_element;
|
||||||
}
|
}
|
||||||
// ⌛ Otherwise the media element has no assigned media provider object and has neither a src attribute nor a source element child:
|
// ⌛ Otherwise the media element has no assigned media provider object and has neither a src attribute nor a source element child:
|
||||||
else {
|
else {
|
||||||
// 1. ⌛ Set the networkState to NETWORK_EMPTY.
|
// 1. ⌛ Set the networkState to NETWORK_EMPTY.
|
||||||
m_network_state = NetworkState::Empty;
|
m_network_state = NetworkState::Empty;
|
||||||
|
|
||||||
// 2. ⌛ Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
|
// 2. ⌛ Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
|
||||||
m_delaying_the_load_event.clear();
|
m_delaying_the_load_event.clear();
|
||||||
|
|
||||||
// 3. End the synchronous section and return.
|
// 3. End the synchronous section and return.
|
||||||
return {};
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// 7. ⌛ Set the media element's networkState to NETWORK_LOADING.
|
|
||||||
m_network_state = NetworkState::Loading;
|
|
||||||
|
|
||||||
// 8. ⌛ Queue a media element task given the media element to fire an event named loadstart at the media element.
|
|
||||||
queue_a_media_element_task([this] {
|
|
||||||
dispatch_event(DOM::Event::create(this->realm(), HTML::EventNames::loadstart));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 9. Run the appropriate steps from the following list:
|
|
||||||
switch (*mode) {
|
|
||||||
// -> If mode is object
|
|
||||||
case SelectMode::Object:
|
|
||||||
// FIXME: 1. ⌛ Set the currentSrc attribute to the empty string.
|
|
||||||
// FIXME: 2. End the synchronous section, continuing the remaining steps in parallel.
|
|
||||||
// FIXME: 3. Run the resource fetch algorithm with the assigned media provider object. If that algorithm returns without aborting this one,
|
|
||||||
// then theload failed.
|
|
||||||
// FIXME: 4. Failed with media provider: Reaching this step indicates that the media resource failed to load. Take pending play promises and queue
|
|
||||||
// a media element task given the media element to run the dedicated media source failure steps with the result.
|
|
||||||
// FIXME: 5. Wait for the task queued by the previous step to have executed.
|
|
||||||
|
|
||||||
// 6. Return. The element won't attempt to load another resource until this algorithm is triggered again.
|
|
||||||
return {};
|
|
||||||
|
|
||||||
// -> If mode is attribute
|
|
||||||
case SelectMode::Attribute: {
|
|
||||||
auto failed_with_attribute = [this](auto error_message) {
|
|
||||||
IGNORE_USE_IN_ESCAPING_LAMBDA bool ran_media_element_task = false;
|
|
||||||
|
|
||||||
// 6. Failed with attribute: Reaching this step indicates that the media resource failed to load or that the given URL could not be parsed. Take
|
|
||||||
// pending play promises and queue a media element task given the media element to run the dedicated media source failure steps with the result.
|
|
||||||
queue_a_media_element_task([this, &ran_media_element_task, error_message = move(error_message)]() mutable {
|
|
||||||
auto promises = take_pending_play_promises();
|
|
||||||
handle_media_source_failure(promises, move(error_message)).release_value_but_fixme_should_propagate_errors();
|
|
||||||
|
|
||||||
ran_media_element_task = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 7. Wait for the task queued by the previous step to have executed.
|
|
||||||
HTML::main_thread_event_loop().spin_until(GC::create_function(heap(), [&]() { return ran_media_element_task; }));
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1. ⌛ If the src attribute's value is the empty string, then end the synchronous section, and jump down to the failed with attribute step below.
|
|
||||||
auto source = get_attribute_value(HTML::AttributeNames::src);
|
|
||||||
if (source.is_empty()) {
|
|
||||||
failed_with_attribute("The 'src' attribute is empty"_string);
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. ⌛ Let urlString and urlRecord be the resulting URL string and the resulting URL record, respectively, that would have resulted from parsing
|
// 7. ⌛ Set the media element's networkState to NETWORK_LOADING.
|
||||||
// the URL specified by the src attribute's value relative to the media element's node document when the src attribute was last changed.
|
m_network_state = NetworkState::Loading;
|
||||||
auto url_record = document().parse_url(source);
|
|
||||||
|
|
||||||
// 3. ⌛ If urlString was obtained successfully, set the currentSrc attribute to urlString.
|
// 8. ⌛ Queue a media element task given the media element to fire an event named loadstart at the media element.
|
||||||
if (url_record.has_value())
|
queue_a_media_element_task([this] {
|
||||||
m_current_src = url_record->to_string();
|
dispatch_event(DOM::Event::create(this->realm(), HTML::EventNames::loadstart));
|
||||||
|
});
|
||||||
|
|
||||||
// 4. End the synchronous section, continuing the remaining steps in parallel.
|
// 9. Run the appropriate steps from the following list:
|
||||||
|
switch (*mode) {
|
||||||
|
// -> If mode is object
|
||||||
|
case SelectMode::Object:
|
||||||
|
// FIXME: 1. ⌛ Set the currentSrc attribute to the empty string.
|
||||||
|
// FIXME: 2. End the synchronous section, continuing the remaining steps in parallel.
|
||||||
|
// FIXME: 3. Run the resource fetch algorithm with the assigned media provider object. If that algorithm returns without aborting this one,
|
||||||
|
// then theload failed.
|
||||||
|
// FIXME: 4. Failed with media provider: Reaching this step indicates that the media resource failed to load. Take pending play promises and queue
|
||||||
|
// a media element task given the media element to run the dedicated media source failure steps with the result.
|
||||||
|
// FIXME: 5. Wait for the task queued by the previous step to have executed.
|
||||||
|
|
||||||
// 5. If urlRecord was obtained successfully, run the resource fetch algorithm with urlRecord. If that algorithm returns without aborting this one,
|
// 6. Return. The element won't attempt to load another resource until this algorithm is triggered again.
|
||||||
// then the load failed.
|
return;
|
||||||
if (url_record.has_value()) {
|
|
||||||
TRY(fetch_resource(*url_record, move(failed_with_attribute)));
|
// -> If mode is attribute
|
||||||
return {};
|
case SelectMode::Attribute: {
|
||||||
|
auto failed_with_attribute = [this](auto error_message) {
|
||||||
|
IGNORE_USE_IN_ESCAPING_LAMBDA bool ran_media_element_task = false;
|
||||||
|
|
||||||
|
// 6. Failed with attribute: Reaching this step indicates that the media resource failed to load or that the given URL could not be parsed. Take
|
||||||
|
// pending play promises and queue a media element task given the media element to run the dedicated media source failure steps with the result.
|
||||||
|
queue_a_media_element_task([this, &ran_media_element_task, error_message = move(error_message)]() mutable {
|
||||||
|
auto promises = take_pending_play_promises();
|
||||||
|
handle_media_source_failure(promises, move(error_message)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
|
||||||
|
ran_media_element_task = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 7. Wait for the task queued by the previous step to have executed.
|
||||||
|
HTML::main_thread_event_loop().spin_until(GC::create_function(heap(), [&]() { return ran_media_element_task; }));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1. ⌛ If the src attribute's value is the empty string, then end the synchronous section, and jump down to the failed with attribute step below.
|
||||||
|
auto source = get_attribute_value(HTML::AttributeNames::src);
|
||||||
|
if (source.is_empty()) {
|
||||||
|
failed_with_attribute("The 'src' attribute is empty"_string);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. ⌛ Let urlString and urlRecord be the resulting URL string and the resulting URL record, respectively, that would have resulted from parsing
|
||||||
|
// the URL specified by the src attribute's value relative to the media element's node document when the src attribute was last changed.
|
||||||
|
auto url_record = document().parse_url(source);
|
||||||
|
|
||||||
|
// 3. ⌛ If urlString was obtained successfully, set the currentSrc attribute to urlString.
|
||||||
|
if (url_record.has_value())
|
||||||
|
m_current_src = url_record->to_string();
|
||||||
|
|
||||||
|
// 4. End the synchronous section, continuing the remaining steps in parallel.
|
||||||
|
|
||||||
|
// 5. If urlRecord was obtained successfully, run the resource fetch algorithm with urlRecord. If that algorithm returns without aborting this one,
|
||||||
|
// then the load failed.
|
||||||
|
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [this, url_record = move(url_record), failed_with_attribute = move(failed_with_attribute)]() mutable {
|
||||||
|
if (url_record.has_value()) {
|
||||||
|
fetch_resource(*url_record, move(failed_with_attribute)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 8. Return. The element won't attempt to load another resource until this algorithm is triggered again.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
failed_with_attribute("Failed to parse 'src' attribute as a URL"_string);
|
// -> Otherwise (mode is children)
|
||||||
|
case SelectMode::Children:
|
||||||
|
VERIFY(candidate);
|
||||||
|
|
||||||
// 8. Return. The element won't attempt to load another resource until this algorithm is triggered again.
|
// 1. ⌛ Let pointer be a position defined by two adjacent nodes in the media element's child list, treating the start of the list (before the
|
||||||
return {};
|
// first child in the list, if any) and end of the list (after the last child in the list, if any) as nodes in their own right. One node is
|
||||||
}
|
// the node before pointer, and the other node is the node after pointer. Initially, let pointer be the position between the candidate node
|
||||||
|
// and the next node, if there are any, or the end of the list, if it is the last node.
|
||||||
|
//
|
||||||
|
// As nodes are inserted and removed into the media element, pointer must be updated as follows:
|
||||||
|
//
|
||||||
|
// If a new node is inserted between the two nodes that define pointer
|
||||||
|
// Let pointer be the point between the node before pointer and the new node. In other words, insertions at pointer go after pointer.
|
||||||
|
// If the node before pointer is removed
|
||||||
|
// Let pointer be the point between the node after pointer and the node before the node after pointer. In other words, pointer doesn't
|
||||||
|
// move relative to the remaining nodes.
|
||||||
|
// If the node after pointer is removed
|
||||||
|
// Let pointer be the point between the node before pointer and the node after the node before pointer. Just as with the previous case,
|
||||||
|
// pointer doesn't move relative to the remaining nodes.
|
||||||
|
// Other changes don't affect pointer.
|
||||||
|
|
||||||
// -> Otherwise (mode is children)
|
// NOTE: We do not bother with maintaining this pointer. We inspect the DOM tree on the fly, rather than dealing
|
||||||
case SelectMode::Children:
|
// with the headache of auto-updating this pointer as the DOM changes.
|
||||||
VERIFY(candidate);
|
|
||||||
|
|
||||||
// 1. ⌛ Let pointer be a position defined by two adjacent nodes in the media element's child list, treating the start of the list (before the
|
m_source_element_selector = realm.create<SourceElementSelector>(*this, *candidate);
|
||||||
// first child in the list, if any) and end of the list (after the last child in the list, if any) as nodes in their own right. One node is
|
m_source_element_selector->process_candidate().release_value_but_fixme_should_propagate_errors();
|
||||||
// the node before pointer, and the other node is the node after pointer. Initially, let pointer be the position between the candidate node
|
|
||||||
// and the next node, if there are any, or the end of the list, if it is the last node.
|
|
||||||
//
|
|
||||||
// As nodes are inserted and removed into the media element, pointer must be updated as follows:
|
|
||||||
//
|
|
||||||
// If a new node is inserted between the two nodes that define pointer
|
|
||||||
// Let pointer be the point between the node before pointer and the new node. In other words, insertions at pointer go after pointer.
|
|
||||||
// If the node before pointer is removed
|
|
||||||
// Let pointer be the point between the node after pointer and the node before the node after pointer. In other words, pointer doesn't
|
|
||||||
// move relative to the remaining nodes.
|
|
||||||
// If the node after pointer is removed
|
|
||||||
// Let pointer be the point between the node before pointer and the node after the node before pointer. Just as with the previous case,
|
|
||||||
// pointer doesn't move relative to the remaining nodes.
|
|
||||||
// Other changes don't affect pointer.
|
|
||||||
|
|
||||||
// NOTE: We do not bother with maintaining this pointer. We inspect the DOM tree on the fly, rather than dealing
|
break;
|
||||||
// with the headache of auto-updating this pointer as the DOM changes.
|
}
|
||||||
|
}));
|
||||||
m_source_element_selector = realm.create<SourceElementSelector>(*this, *candidate);
|
|
||||||
TRY(m_source_element_selector->process_candidate());
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 6 tests
|
||||||
|
|
||||||
|
4 Pass
|
||||||
|
2 Fail
|
||||||
|
Pass audio.error initial value
|
||||||
|
Fail audio.error after successful load
|
||||||
|
Pass audio.error after setting src to the empty string
|
||||||
|
Pass video.error initial value
|
||||||
|
Fail video.error after successful load
|
||||||
|
Pass video.error after setting src to the empty string
|
|
@ -0,0 +1,23 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 18 tests
|
||||||
|
|
||||||
|
18 Pass
|
||||||
|
Pass audio.currentSrc initial value
|
||||||
|
Pass audio.currentSrc after setting src attribute ""
|
||||||
|
Pass audio.currentSrc after adding source element with src attribute ""
|
||||||
|
Pass audio.currentSrc after setting src attribute "."
|
||||||
|
Pass audio.currentSrc after adding source element with src attribute "."
|
||||||
|
Pass audio.currentSrc after setting src attribute " "
|
||||||
|
Pass audio.currentSrc after adding source element with src attribute " "
|
||||||
|
Pass audio.currentSrc after setting src attribute "data:,"
|
||||||
|
Pass audio.currentSrc after adding source element with src attribute "data:,"
|
||||||
|
Pass video.currentSrc initial value
|
||||||
|
Pass video.currentSrc after setting src attribute ""
|
||||||
|
Pass video.currentSrc after adding source element with src attribute ""
|
||||||
|
Pass video.currentSrc after setting src attribute "."
|
||||||
|
Pass video.currentSrc after adding source element with src attribute "."
|
||||||
|
Pass video.currentSrc after setting src attribute " "
|
||||||
|
Pass video.currentSrc after adding source element with src attribute " "
|
||||||
|
Pass video.currentSrc after setting src attribute "data:,"
|
||||||
|
Pass video.currentSrc after adding source element with src attribute "data:,"
|
|
@ -0,0 +1,7 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 2 tests
|
||||||
|
|
||||||
|
2 Pass
|
||||||
|
Pass src="about:blank" does not crash.
|
||||||
|
Pass src="" does not crash.
|
57
Tests/LibWeb/Text/input/wpt-import/common/media.js
Normal file
57
Tests/LibWeb/Text/input/wpt-import/common/media.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* Returns the URL of a supported video source based on the user agent
|
||||||
|
* @param {string} base - media URL without file extension
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getVideoURI(base)
|
||||||
|
{
|
||||||
|
var extension = '.mp4';
|
||||||
|
|
||||||
|
var videotag = document.createElement("video");
|
||||||
|
|
||||||
|
if ( videotag.canPlayType )
|
||||||
|
{
|
||||||
|
if (videotag.canPlayType('video/webm; codecs="vp9, opus"') )
|
||||||
|
{
|
||||||
|
extension = '.webm';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base + extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL of a supported audio source based on the user agent
|
||||||
|
* @param {string} base - media URL without file extension
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getAudioURI(base)
|
||||||
|
{
|
||||||
|
var extension = '.mp3';
|
||||||
|
|
||||||
|
var audiotag = document.createElement("audio");
|
||||||
|
|
||||||
|
if ( audiotag.canPlayType &&
|
||||||
|
audiotag.canPlayType('audio/ogg') )
|
||||||
|
{
|
||||||
|
extension = '.oga';
|
||||||
|
}
|
||||||
|
|
||||||
|
return base + extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the MIME type for a media URL based on the file extension.
|
||||||
|
* @param {string} url
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getMediaContentType(url) {
|
||||||
|
var extension = new URL(url, location).pathname.split(".").pop();
|
||||||
|
var map = {
|
||||||
|
"mp4" : "video/mp4",
|
||||||
|
"webm": "video/webm",
|
||||||
|
"mp3" : "audio/mp3",
|
||||||
|
"oga" : "application/ogg",
|
||||||
|
};
|
||||||
|
return map[extension];
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<!doctype html>
|
||||||
|
<title>error</title>
|
||||||
|
<script src="../../../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../../../resources/testharnessreport.js"></script>
|
||||||
|
<script src="../../../../../common/media.js"></script>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
function error_test(tagName, src) {
|
||||||
|
test(function() {
|
||||||
|
assert_equals(document.createElement(tagName).error, null);
|
||||||
|
}, tagName + '.error initial value');
|
||||||
|
|
||||||
|
async_test(function(t) {
|
||||||
|
var e = document.createElement(tagName);
|
||||||
|
e.src = src;
|
||||||
|
e.onerror = t.unreached_func();
|
||||||
|
e.onloadeddata = t.step_func(function() {
|
||||||
|
assert_equals(e.error, null);
|
||||||
|
t.done();
|
||||||
|
});
|
||||||
|
}, tagName + '.error after successful load');
|
||||||
|
|
||||||
|
// TODO: MEDIA_ERR_ABORTED, MEDIA_ERR_NETWORK, MEDIA_ERR_DECODE
|
||||||
|
|
||||||
|
async_test(function(t) {
|
||||||
|
var e = document.createElement(tagName);
|
||||||
|
e.src = '';
|
||||||
|
e.onerror = t.step_func(function() {
|
||||||
|
assert_true(e.error instanceof MediaError);
|
||||||
|
assert_equals(e.error.code, 4);
|
||||||
|
assert_equals(e.error.code, e.error.MEDIA_ERR_SRC_NOT_SUPPORTED);
|
||||||
|
assert_equals(typeof e.error.message, 'string', 'error.message type');
|
||||||
|
t.done();
|
||||||
|
});
|
||||||
|
}, tagName + '.error after setting src to the empty string');
|
||||||
|
}
|
||||||
|
|
||||||
|
error_test('audio', getAudioURI('/media/sound_5'));
|
||||||
|
error_test('video', getVideoURI('/media/movie_5'));
|
||||||
|
</script>
|
|
@ -0,0 +1,48 @@
|
||||||
|
<!doctype html>
|
||||||
|
<title>currentSrc</title>
|
||||||
|
<script src="../../../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../../../resources/testharnessreport.js"></script>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
['audio', 'video'].forEach(function(tagName) {
|
||||||
|
test(function() {
|
||||||
|
assert_equals(document.createElement(tagName).currentSrc, '');
|
||||||
|
}, tagName + '.currentSrc initial value');
|
||||||
|
|
||||||
|
['', '.', ' ', 'data:,'].forEach(function(src) {
|
||||||
|
async_test(function(t) {
|
||||||
|
var e = document.createElement(tagName);
|
||||||
|
e.src = src;
|
||||||
|
assert_equals(e.currentSrc, '');
|
||||||
|
e.addEventListener('loadstart', function () {
|
||||||
|
t.step_timeout(function () {
|
||||||
|
if (src == '') {
|
||||||
|
assert_equals(e.currentSrc, '');
|
||||||
|
} else {
|
||||||
|
assert_equals(e.currentSrc, e.src);
|
||||||
|
}
|
||||||
|
t.done();
|
||||||
|
}, 0);
|
||||||
|
})
|
||||||
|
}, tagName + '.currentSrc after setting src attribute "' + src + '"');
|
||||||
|
|
||||||
|
async_test(function(t) {
|
||||||
|
var e = document.createElement(tagName);
|
||||||
|
var s = document.createElement('source');
|
||||||
|
s.src = src;
|
||||||
|
e.appendChild(s);
|
||||||
|
assert_equals(e.currentSrc, '');
|
||||||
|
e.addEventListener('loadstart', function() {
|
||||||
|
t.step_timeout(function () {
|
||||||
|
if (src == '') {
|
||||||
|
assert_equals(e.currentSrc, '');
|
||||||
|
} else {
|
||||||
|
assert_equals(e.currentSrc, s.src);
|
||||||
|
}
|
||||||
|
t.done();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
}, tagName + '.currentSrc after adding source element with src attribute "' + src + '"');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>HTML5 Media Elements: An empty src should not crash the player.</title>
|
||||||
|
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
|
||||||
|
<link rel="author" title="Alicia Boya García" href="mailto:aboya@igalia.com"/>
|
||||||
|
<script src="../../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../../resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
function makeCrashTest(src) {
|
||||||
|
async_test((test) => {
|
||||||
|
const video = document.createElement("video");
|
||||||
|
video.src = src;
|
||||||
|
video.controls = true;
|
||||||
|
video.addEventListener("error", () => {
|
||||||
|
document.body.removeChild(video);
|
||||||
|
test.done();
|
||||||
|
});
|
||||||
|
document.body.appendChild(video);
|
||||||
|
}, `src="${src}" does not crash.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
makeCrashTest("about:blank");
|
||||||
|
makeCrashTest("");
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue