mirror of
https://github.com/servo/servo.git
synced 2025-09-30 00:29:14 +01:00
script: Add FocusOptions
argument to Element.focus
and implement FocusOptions.preventScroll
(#38495)
This is an implementation of the `prevent_scroll` feature in the focus transaction system. It allows to control whether focusing an element should prevent scrolling or not. Spec: https://html.spec.whatwg.org/multipage/interaction.html#dom-focusoptions-preventscroll Testing: Existing WPT tests Signed-off-by: abdelrahman1234567 <abdelrahman.hossameldin.awadalla@huawei.com>
This commit is contained in:
parent
2ac8665e03
commit
176e42d36d
14 changed files with 95 additions and 60 deletions
|
@ -87,6 +87,7 @@ use crate::dom::bindings::codegen::Bindings::ElementBinding::{
|
|||
use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElement_Binding::HTMLIFrameElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||
|
@ -264,6 +265,8 @@ struct FocusTransaction {
|
|||
element: Option<Dom<Element>>,
|
||||
/// See [`Document::has_focus`].
|
||||
has_focus: bool,
|
||||
/// Focus options for the transaction
|
||||
focus_options: FocusOptions,
|
||||
}
|
||||
|
||||
/// Information about a declarative refresh
|
||||
|
@ -1140,6 +1143,7 @@ impl Document {
|
|||
*self.focus_transaction.borrow_mut() = Some(FocusTransaction {
|
||||
element: self.focused.get().as_deref().map(Dom::from_ref),
|
||||
has_focus: self.has_focus.get(),
|
||||
focus_options: FocusOptions::default(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1169,15 +1173,27 @@ impl Document {
|
|||
}
|
||||
}
|
||||
|
||||
/// Request that the given element receive focus with default options.
|
||||
/// See [`Self::request_focus_with_options`] for the details.
|
||||
pub(crate) fn request_focus(
|
||||
&self,
|
||||
elem: Option<&Element>,
|
||||
focus_initiator: FocusInitiator,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
self.request_focus_with_options(elem, focus_initiator, FocusOptions::default(), can_gc);
|
||||
}
|
||||
|
||||
/// Request that the given element receive focus once the current
|
||||
/// transaction is complete. `None` specifies to focus the document.
|
||||
///
|
||||
/// If there's no ongoing transaction, this method automatically starts and
|
||||
/// commits an implicit transaction.
|
||||
pub(crate) fn request_focus(
|
||||
pub(crate) fn request_focus_with_options(
|
||||
&self,
|
||||
elem: Option<&Element>,
|
||||
focus_initiator: FocusInitiator,
|
||||
focus_options: FocusOptions,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
// If an element is specified, and it's non-focusable, ignore the
|
||||
|
@ -1197,6 +1213,7 @@ impl Document {
|
|||
let focus_transaction = focus_transaction.as_mut().unwrap();
|
||||
focus_transaction.element = elem.map(Dom::from_ref);
|
||||
focus_transaction.has_focus = true;
|
||||
focus_transaction.focus_options = focus_options;
|
||||
}
|
||||
|
||||
if implicit_transaction {
|
||||
|
@ -1236,7 +1253,7 @@ impl Document {
|
|||
/// Reassign the focus context to the element that last requested focus during this
|
||||
/// transaction, or the document if no elements requested it.
|
||||
pub(crate) fn commit_focus_transaction(&self, focus_initiator: FocusInitiator, can_gc: CanGc) {
|
||||
let (mut new_focused, new_focus_state) = {
|
||||
let (mut new_focused, new_focus_state, prevent_scroll) = {
|
||||
let focus_transaction = self.focus_transaction.borrow();
|
||||
let focus_transaction = focus_transaction
|
||||
.as_ref()
|
||||
|
@ -1247,6 +1264,7 @@ impl Document {
|
|||
.as_ref()
|
||||
.map(|e| DomRoot::from_ref(&**e)),
|
||||
focus_transaction.has_focus,
|
||||
focus_transaction.focus_options.preventScroll,
|
||||
)
|
||||
};
|
||||
*self.focus_transaction.borrow_mut() = None;
|
||||
|
@ -1363,16 +1381,19 @@ impl Document {
|
|||
}
|
||||
// Scroll operation to happen after element gets focus.
|
||||
// This is needed to ensure that the focused element is visible.
|
||||
elem.ScrollIntoView(BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(
|
||||
ScrollIntoViewOptions {
|
||||
parent: ScrollOptions {
|
||||
behavior: ScrollBehavior::Smooth,
|
||||
// Only scroll if preventScroll was not specified
|
||||
if !prevent_scroll {
|
||||
elem.ScrollIntoView(BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(
|
||||
ScrollIntoViewOptions {
|
||||
parent: ScrollOptions {
|
||||
behavior: ScrollBehavior::Smooth,
|
||||
},
|
||||
block: ScrollLogicalPosition::Center,
|
||||
inline: ScrollLogicalPosition::Center,
|
||||
container: ScrollIntoViewContainer::All,
|
||||
},
|
||||
block: ScrollLogicalPosition::Center,
|
||||
inline: ScrollLogicalPosition::Center,
|
||||
container: ScrollIntoViewContainer::All,
|
||||
},
|
||||
));
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::{
|
|||
};
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
|
@ -417,12 +418,19 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
|
|||
element.set_click_in_progress(false);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-focus
|
||||
fn Focus(&self, can_gc: CanGc) {
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-focus>
|
||||
fn Focus(&self, options: &FocusOptions, can_gc: CanGc) {
|
||||
// TODO: Mark the element as locked for focus and run the focusing steps.
|
||||
// https://html.spec.whatwg.org/multipage/#focusing-steps
|
||||
// <https://html.spec.whatwg.org/multipage/#focusing-steps>
|
||||
let document = self.owner_document();
|
||||
document.request_focus(Some(self.upcast()), FocusInitiator::Local, can_gc);
|
||||
document.request_focus_with_options(
|
||||
Some(self.upcast()),
|
||||
FocusInitiator::Local,
|
||||
FocusOptions {
|
||||
preventScroll: options.preventScroll,
|
||||
},
|
||||
can_gc,
|
||||
);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-blur
|
||||
|
|
|
@ -34,6 +34,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMeth
|
|||
use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
|
||||
use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
|
||||
|
@ -1101,7 +1102,9 @@ impl HTMLFormElement {
|
|||
}
|
||||
if first {
|
||||
if let Some(html_elem) = elem.downcast::<HTMLElement>() {
|
||||
html_elem.Focus(can_gc);
|
||||
// TODO: "Focusing steps" has a different meaning from the focus() method.
|
||||
// The actual focusing steps should be implemented
|
||||
html_elem.Focus(&FocusOptions::default(), can_gc);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,12 @@ use script_bindings::str::DOMString;
|
|||
use stylo_dom::ElementState;
|
||||
|
||||
use crate::dom::attr::Attr;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::SVGElementBinding::SVGElementMethods;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::document::{Document, FocusInitiator};
|
||||
use crate::dom::element::{AttributeMutation, Element};
|
||||
use crate::dom::node::{Node, NodeTraits};
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
|
@ -91,7 +92,7 @@ impl VirtualMethods for SVGElement {
|
|||
}
|
||||
|
||||
impl SVGElementMethods<crate::DomTypeHolder> for SVGElement {
|
||||
// https://html.spec.whatwg.org/multipage/#the-style-attribute
|
||||
/// <https://html.spec.whatwg.org/multipage/#the-style-attribute>
|
||||
fn Style(&self) -> DomRoot<CSSStyleDeclaration> {
|
||||
self.style_decl.or_init(|| {
|
||||
let global = self.owner_window();
|
||||
|
@ -105,28 +106,41 @@ impl SVGElementMethods<crate::DomTypeHolder> for SVGElement {
|
|||
})
|
||||
}
|
||||
|
||||
// <https://html.spec.whatwg.org/multipage/#globaleventhandlers>
|
||||
// https://html.spec.whatwg.org/multipage/#globaleventhandlers
|
||||
global_event_handlers!();
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce>
|
||||
fn Nonce(&self) -> DOMString {
|
||||
self.as_element().nonce_value().into()
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce>
|
||||
fn SetNonce(&self, value: DOMString) {
|
||||
self.as_element()
|
||||
.update_nonce_internal_slot(value.to_string())
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-fe-autofocus
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-fe-autofocus>
|
||||
fn Autofocus(&self) -> bool {
|
||||
self.element.has_attribute(&local_name!("autofocus"))
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-fe-autofocus
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-fe-autofocus>
|
||||
fn SetAutofocus(&self, autofocus: bool, can_gc: CanGc) {
|
||||
self.element
|
||||
.set_bool_attribute(&local_name!("autofocus"), autofocus, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-focus>
|
||||
fn Focus(&self, options: &FocusOptions) {
|
||||
let document = self.element.owner_document();
|
||||
document.request_focus_with_options(
|
||||
Some(&self.element),
|
||||
FocusInitiator::Local,
|
||||
FocusOptions {
|
||||
preventScroll: options.preventScroll,
|
||||
},
|
||||
CanGc::note(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
|
@ -75,7 +76,9 @@ pub(crate) trait Validatable {
|
|||
validation_message_for_flags(&self.validity_state(), flags)
|
||||
);
|
||||
if let Some(html_elem) = self.as_element().downcast::<HTMLElement>() {
|
||||
html_elem.Focus(can_gc);
|
||||
// TODO: "Focusing steps" has a different meaning from the focus() method.
|
||||
// The actual focusing steps should be implemented
|
||||
html_elem.Focus(&FocusOptions::default(), can_gc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue