mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-27 14:58:46 +00:00
LibWeb: Implement TimeRanges and HTMLMediaElement.seekable()
This commit is contained in:
parent
3952ff4786
commit
aa243000f3
Notes:
github-actions[bot]
2025-02-18 17:46:30 +00:00
Author: https://github.com/Psychpsyo
Commit: aa243000f3
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3563
Reviewed-by: https://github.com/ADKaster ✅
5 changed files with 103 additions and 18 deletions
|
@ -185,6 +185,18 @@ GC::Ref<TimeRanges> HTMLMediaElement::played() const
|
||||||
return time_ranges;
|
return time_ranges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/media.html#dom-media-seekable
|
||||||
|
GC::Ref<TimeRanges> HTMLMediaElement::seekable() const
|
||||||
|
{
|
||||||
|
auto& realm = this->realm();
|
||||||
|
|
||||||
|
// The seekable attribute must return a new static normalized TimeRanges object that represents the ranges of the media resource, if any, that the
|
||||||
|
// user agent is able to seek to, at the time the attribute is evaluated.
|
||||||
|
auto time_ranges = realm.create<TimeRanges>(realm);
|
||||||
|
time_ranges->add_range(0, m_duration);
|
||||||
|
return time_ranges;
|
||||||
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/media.html#dom-navigator-canplaytype
|
// https://html.spec.whatwg.org/multipage/media.html#dom-navigator-canplaytype
|
||||||
Bindings::CanPlayTypeResult HTMLMediaElement::can_play_type(StringView type) const
|
Bindings::CanPlayTypeResult HTMLMediaElement::can_play_type(StringView type) const
|
||||||
{
|
{
|
||||||
|
@ -1566,11 +1578,46 @@ void HTMLMediaElement::seek_element(double playback_position, MediaSeekMode seek
|
||||||
if (playback_position < 0)
|
if (playback_position < 0)
|
||||||
playback_position = 0;
|
playback_position = 0;
|
||||||
|
|
||||||
// FIXME: 8. If the (possibly now changed) new playback position is not in one of the ranges given in the seekable attribute,
|
// 8. If the (possibly now changed) new playback position is not in one of the ranges given in the seekable attribute,
|
||||||
// then let it be the position in one of the ranges given in the seekable attribute that is the nearest to the new
|
auto time_ranges = seekable();
|
||||||
// playback position. If two positions both satisfy that constraint (i.e. the new playback position is exactly in the
|
if (!time_ranges->in_range(playback_position)) {
|
||||||
// middle between two ranges in the seekable attribute) then use the position that is closest to the current playback
|
// then let it be the position in one of the ranges given in the seekable attribute that is the nearest to the new
|
||||||
// position. If there are no ranges given in the seekable attribute then set the seeking IDL attribute to false and return.
|
// playback position.
|
||||||
|
|
||||||
|
// If there are no ranges given in the seekable attribute then set the seeking IDL attribute to false and return.
|
||||||
|
if (time_ranges->length() == 0) {
|
||||||
|
set_seeking(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double nearest_point;
|
||||||
|
Optional<double> other_nearest_point = {};
|
||||||
|
double distance = INFINITY;
|
||||||
|
for (size_t i = 0; i < time_ranges->length(); i++) {
|
||||||
|
for (double point : { MUST(time_ranges->start(i)), MUST(time_ranges->end(i)) }) {
|
||||||
|
auto point_distance = abs(playback_position - point);
|
||||||
|
if (point_distance < distance) {
|
||||||
|
nearest_point = point;
|
||||||
|
other_nearest_point = {};
|
||||||
|
distance = point_distance;
|
||||||
|
} else if (point_distance == distance) {
|
||||||
|
other_nearest_point = point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If two positions both satisfy that constraint (i.e. the new playback position is exactly in the middle between two ranges
|
||||||
|
// in the seekable attribute) then use the position that is closest to the current playback position.
|
||||||
|
if (other_nearest_point.has_value()) {
|
||||||
|
auto nearest_point_distance = abs(m_current_playback_position - nearest_point);
|
||||||
|
auto other_nearest_point_distance = abs(m_current_playback_position - other_nearest_point.value());
|
||||||
|
if (nearest_point_distance < other_nearest_point_distance) {
|
||||||
|
playback_position = nearest_point;
|
||||||
|
} else {
|
||||||
|
playback_position = other_nearest_point.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 9. If the approximate-for-speed flag is set, adjust the new playback position to a value that will allow for playback to resume
|
// 9. If the approximate-for-speed flag is set, adjust the new playback position to a value that will allow for playback to resume
|
||||||
// promptly. If new playback position before this step is before current playback position, then the adjusted new playback position
|
// promptly. If new playback position before this step is before current playback position, then the adjusted new playback position
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] GC::Ref<TimeRanges> buffered() const;
|
[[nodiscard]] GC::Ref<TimeRanges> buffered() const;
|
||||||
[[nodiscard]] GC::Ref<TimeRanges> played() const;
|
[[nodiscard]] GC::Ref<TimeRanges> played() const;
|
||||||
|
[[nodiscard]] GC::Ref<TimeRanges> seekable() const;
|
||||||
|
|
||||||
static inline constexpr auto supported_video_subtypes = Array {
|
static inline constexpr auto supported_video_subtypes = Array {
|
||||||
"webm"sv,
|
"webm"sv,
|
||||||
|
|
|
@ -63,7 +63,7 @@ interface HTMLMediaElement : HTMLElement {
|
||||||
attribute double playbackRate;
|
attribute double playbackRate;
|
||||||
[FIXME] attribute boolean preservesPitch;
|
[FIXME] attribute boolean preservesPitch;
|
||||||
readonly attribute TimeRanges played;
|
readonly attribute TimeRanges played;
|
||||||
[FIXME] readonly attribute TimeRanges seekable;
|
readonly attribute TimeRanges seekable;
|
||||||
readonly attribute boolean ended;
|
readonly attribute boolean ended;
|
||||||
[Reflect, CEReactions] attribute boolean autoplay;
|
[Reflect, CEReactions] attribute boolean autoplay;
|
||||||
[Reflect, CEReactions] attribute boolean loop;
|
[Reflect, CEReactions] attribute boolean loop;
|
||||||
|
|
|
@ -27,24 +27,44 @@ void TimeRanges::initialize(JS::Realm& realm)
|
||||||
// https://html.spec.whatwg.org/multipage/media.html#dom-timeranges-length
|
// https://html.spec.whatwg.org/multipage/media.html#dom-timeranges-length
|
||||||
size_t TimeRanges::length() const
|
size_t TimeRanges::length() const
|
||||||
{
|
{
|
||||||
// FIXME: The length IDL attribute must return the number of ranges represented by the object.
|
return m_ranges.size();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/media.html#dom-timeranges-start
|
// https://html.spec.whatwg.org/multipage/media.html#dom-timeranges-start
|
||||||
double TimeRanges::start(u32) const
|
WebIDL::ExceptionOr<double> TimeRanges::start(u32 index) const
|
||||||
{
|
{
|
||||||
// FIXME: The start(index) method must return the position of the start of the indexth range represented by the object,
|
// These methods must throw "IndexSizeError" DOMExceptions if called with an index argument greater than or equal to the number of ranges represented by the object.
|
||||||
// in seconds measured from the start of the timeline that the object covers.
|
if (index >= m_ranges.size())
|
||||||
return 0.0;
|
return WebIDL::IndexSizeError::create(realm(), "Index argument is greater than or equal to the number of ranges represented by this TimeRanges object"_string);
|
||||||
|
|
||||||
|
// The start(index) method must return the position of the start of the indexth range represented by the object,
|
||||||
|
// in seconds measured from the start of the timeline that the object covers.
|
||||||
|
return m_ranges[index].start;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/media.html#dom-timeranges-end
|
// https://html.spec.whatwg.org/multipage/media.html#dom-timeranges-end
|
||||||
double TimeRanges::end(u32) const
|
WebIDL::ExceptionOr<double> TimeRanges::end(u32 index) const
|
||||||
{
|
{
|
||||||
// FIXME: The end(index) method must return the position of the end of the indexth range represented by the object,
|
// These methods must throw "IndexSizeError" DOMExceptions if called with an index argument greater than or equal to the number of ranges represented by the object.
|
||||||
// in seconds measured from the start of the timeline that the object covers.
|
if (index >= m_ranges.size())
|
||||||
return 0.0;
|
return WebIDL::IndexSizeError::create(realm(), "Index argument is greater than or equal to the number of ranges represented by this TimeRanges object"_string);
|
||||||
|
|
||||||
|
// The end(index) method must return the position of the end of the indexth range represented by the object,
|
||||||
|
// in seconds measured from the start of the timeline that the object covers.
|
||||||
|
return m_ranges[index].end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeRanges::add_range(double start, double end)
|
||||||
|
{
|
||||||
|
m_ranges.append({ start, end });
|
||||||
|
}
|
||||||
|
bool TimeRanges::in_range(double point)
|
||||||
|
{
|
||||||
|
for (auto range : m_ranges) {
|
||||||
|
if (point >= range.start && point <= range.end)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,22 +8,39 @@
|
||||||
|
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
#include <LibWeb/Bindings/PlatformObject.h>
|
#include <LibWeb/Bindings/PlatformObject.h>
|
||||||
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||||
|
|
||||||
namespace Web::HTML {
|
namespace Web::HTML {
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/media.html#time-ranges
|
||||||
class TimeRanges final : public Bindings::PlatformObject {
|
class TimeRanges final : public Bindings::PlatformObject {
|
||||||
WEB_PLATFORM_OBJECT(TimeRanges, Bindings::PlatformObject);
|
WEB_PLATFORM_OBJECT(TimeRanges, Bindings::PlatformObject);
|
||||||
GC_DECLARE_ALLOCATOR(TimeRanges);
|
GC_DECLARE_ALLOCATOR(TimeRanges);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// https://html.spec.whatwg.org/multipage/media.html#dom-timeranges-length
|
||||||
size_t length() const;
|
size_t length() const;
|
||||||
double start(u32 index) const;
|
|
||||||
double end(u32 index) const;
|
// https://html.spec.whatwg.org/multipage/media.html#dom-timeranges-start
|
||||||
|
WebIDL::ExceptionOr<double> start(u32 index) const;
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/media.html#dom-timeranges-end
|
||||||
|
WebIDL::ExceptionOr<double> end(u32 index) const;
|
||||||
|
|
||||||
|
void add_range(double start, double end);
|
||||||
|
bool in_range(double);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit TimeRanges(JS::Realm&);
|
explicit TimeRanges(JS::Realm&);
|
||||||
|
|
||||||
virtual void initialize(JS::Realm&) override;
|
virtual void initialize(JS::Realm&) override;
|
||||||
|
|
||||||
|
struct Range {
|
||||||
|
double start;
|
||||||
|
double end;
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<Range> m_ranges;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue