mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 21:07:35 +00:00
LibWeb: Implement Window.scroll() and Window.scrollBy() JS methods
... and `Window.scrollTo()`, which is an alias for `scroll()`. There is still work that needs to be done here, regarding bringing the scroll position calculation in line with the spec. Currently we get the viewport rect from outside, and treat it as if it was the result of calculating steps 5-9 of the `scroll()` method. But it works. :^)
This commit is contained in:
parent
9588a377ec
commit
57371f7608
2 changed files with 154 additions and 0 deletions
|
@ -82,6 +82,10 @@ void WindowObject::initialize_global_object()
|
|||
define_native_accessor("scrollY", scroll_y_getter, {}, attr);
|
||||
define_native_accessor("pageYOffset", scroll_y_getter, {}, attr);
|
||||
|
||||
define_native_function("scroll", scroll, 2, attr);
|
||||
define_native_function("scrollTo", scroll, 2, attr);
|
||||
define_native_function("scrollBy", scroll_by, 2, attr);
|
||||
|
||||
// Legacy
|
||||
define_native_accessor("event", event_getter, {}, JS::Attribute::Enumerable);
|
||||
|
||||
|
@ -488,4 +492,152 @@ JS_DEFINE_NATIVE_GETTER(WindowObject::scroll_y_getter)
|
|||
return JS::Value(impl->page()->top_level_browsing_context().viewport_scroll_offset().y());
|
||||
}
|
||||
|
||||
enum class ScrollBehavior {
|
||||
Auto,
|
||||
Smooth
|
||||
};
|
||||
|
||||
// https://www.w3.org/TR/cssom-view/#perform-a-scroll
|
||||
static void perform_a_scroll(Page& page, double x, double y, ScrollBehavior)
|
||||
{
|
||||
// FIXME: Stop any existing smooth-scrolls
|
||||
// FIXME: Implement smooth-scroll
|
||||
page.client().page_did_request_scroll_to({ x, y });
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/cssom-view/#dom-window-scroll
|
||||
JS_DEFINE_NATIVE_FUNCTION(WindowObject::scroll)
|
||||
{
|
||||
auto* impl = impl_from(vm, global_object);
|
||||
if (!impl)
|
||||
return {};
|
||||
if (!impl->page())
|
||||
return {};
|
||||
auto& page = *impl->page();
|
||||
|
||||
auto viewport_rect = page.top_level_browsing_context().viewport_rect();
|
||||
auto x_value = JS::Value(viewport_rect.x());
|
||||
auto y_value = JS::Value(viewport_rect.y());
|
||||
String behavior_string = "auto";
|
||||
|
||||
if (vm.argument_count() == 1) {
|
||||
auto* options = vm.argument(0).to_object(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
auto left = options->get("left");
|
||||
if (vm.exception())
|
||||
return {};
|
||||
if (!left.is_undefined())
|
||||
x_value = left;
|
||||
|
||||
auto top = options->get("top");
|
||||
if (vm.exception())
|
||||
return {};
|
||||
if (!top.is_undefined())
|
||||
y_value = top;
|
||||
|
||||
auto behavior_string_value = options->get("behavior");
|
||||
if (vm.exception())
|
||||
return {};
|
||||
if (!behavior_string_value.is_undefined())
|
||||
behavior_string = behavior_string_value.to_string(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
if (behavior_string != "smooth" && behavior_string != "auto") {
|
||||
vm.throw_exception<JS::TypeError>(global_object, "Behavior is not one of 'smooth' or 'auto'");
|
||||
return {};
|
||||
}
|
||||
|
||||
} else if (vm.argument_count() >= 2) {
|
||||
// We ignore arguments 2+ in line with behavior of Chrome and Firefox
|
||||
x_value = vm.argument(0);
|
||||
y_value = vm.argument(1);
|
||||
}
|
||||
|
||||
ScrollBehavior behavior = (behavior_string == "smooth") ? ScrollBehavior::Smooth : ScrollBehavior::Auto;
|
||||
|
||||
double x = x_value.to_double(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
x = JS::Value(x).is_finite_number() ? x : 0.0;
|
||||
|
||||
double y = y_value.to_double(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
y = JS::Value(y).is_finite_number() ? y : 0.0;
|
||||
|
||||
// FIXME: Are we calculating the viewport in the way this function expects?
|
||||
// FIXME: Handle overflow-directions other than top-left to bottom-right
|
||||
|
||||
perform_a_scroll(page, x, y, behavior);
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/cssom-view/#dom-window-scrollby
|
||||
JS_DEFINE_NATIVE_FUNCTION(WindowObject::scroll_by)
|
||||
{
|
||||
auto* impl = impl_from(vm, global_object);
|
||||
if (!impl)
|
||||
return {};
|
||||
if (!impl->page())
|
||||
return {};
|
||||
auto& page = *impl->page();
|
||||
|
||||
JS::Object* options = nullptr;
|
||||
|
||||
if (vm.argument_count() == 0) {
|
||||
options = JS::Object::create(global_object, nullptr);
|
||||
} else if (vm.argument_count() == 1) {
|
||||
options = vm.argument(0).to_object(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
} else if (vm.argument_count() >= 2) {
|
||||
// We ignore arguments 2+ in line with behavior of Chrome and Firefox
|
||||
options = JS::Object::create(global_object, nullptr);
|
||||
options->set("left", vm.argument(0), ShouldThrowExceptions::No);
|
||||
options->set("top", vm.argument(1), ShouldThrowExceptions::No);
|
||||
options->set("behavior", JS::js_string(vm, "auto"), ShouldThrowExceptions::No);
|
||||
}
|
||||
|
||||
auto left_value = options->get("left");
|
||||
if (vm.exception())
|
||||
return {};
|
||||
auto left = left_value.to_double(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
auto top_value = options->get("top");
|
||||
if (vm.exception())
|
||||
return {};
|
||||
auto top = top_value.to_double(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
left = JS::Value(left).is_finite_number() ? left : 0.0;
|
||||
top = JS::Value(top).is_finite_number() ? top : 0.0;
|
||||
|
||||
auto current_scroll_position = page.top_level_browsing_context().viewport_scroll_offset();
|
||||
left = left + current_scroll_position.x();
|
||||
top = top + current_scroll_position.y();
|
||||
|
||||
auto behavior_string_value = options->get("behavior");
|
||||
if (vm.exception())
|
||||
return {};
|
||||
auto behavior_string = behavior_string_value.is_undefined() ? "auto" : behavior_string_value.to_string(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
if (behavior_string != "smooth" && behavior_string != "auto") {
|
||||
vm.throw_exception<JS::TypeError>(global_object, "Behavior is not one of 'smooth' or 'auto'");
|
||||
return {};
|
||||
}
|
||||
ScrollBehavior behavior = (behavior_string == "smooth") ? ScrollBehavior::Smooth : ScrollBehavior::Auto;
|
||||
|
||||
// FIXME: Spec wants us to call scroll(options) here.
|
||||
// The only difference is that would invoke the viewport calculations that scroll()
|
||||
// is not actually doing yet, so this is the same for now.
|
||||
perform_a_scroll(page, left, top, behavior);
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue