diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 364022644d..a381f0a5a2 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -308,12 +308,18 @@ Element::TabIndex() } void -Element::Focus(mozilla::ErrorResult& aError) +Element::Focus(const FocusOptions& aOptions, ErrorResult& aError) { nsCOMPtr domElement = do_QueryInterface(this); nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + // Also other browsers seem to have the hack to not re-focus (and flush) when + // the element is already focused. + // Until https://github.com/whatwg/html/issues/4512 is clarified, we'll + // maintain interoperatibility by not re-focusing, independent of aOptions. + // I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll: + // false })` won't re-focus. if (fm && domElement) { - aError = fm->SetFocus(domElement, 0); + aError = fm->SetFocus(domElement, nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions)); } } diff --git a/dom/base/Element.h b/dom/base/Element.h index 14c53ae465..2f1d449281 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -216,7 +216,7 @@ public: /** * Make focus on this element. */ - virtual void Focus(mozilla::ErrorResult& aError); + virtual void Focus(const FocusOptions& aOptions, ErrorResult& aError); /** * Show blur and clear focus. diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index fa321ac300..f8d265e905 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -48,6 +48,7 @@ #include "mozilla/ContentEvents.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/ElementBinding.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/HTMLInputElement.h" #include "mozilla/dom/HTMLSlotElement.h" @@ -3005,6 +3006,11 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindowOuter* aWindow, return NS_OK; } +uint32_t nsFocusManager::FocusOptionsToFocusManagerFlags( + const mozilla::dom::FocusOptions& aOptions) { + return aOptions.mPreventScroll ? nsIFocusManager::FLAG_NOSCROLL : 0; +} + static bool IsHostOrSlot(nsIContent* aContent) { diff --git a/dom/base/nsFocusManager.h b/dom/base/nsFocusManager.h index 8fe919a155..b05df8abf1 100644 --- a/dom/base/nsFocusManager.h +++ b/dom/base/nsFocusManager.h @@ -26,6 +26,7 @@ class nsIMessageBroadcaster; namespace mozilla { namespace dom { +struct FocusOptions; class TabParent; } } @@ -110,6 +111,9 @@ public: static nsIContent* GetFocusedDescendant(nsPIDOMWindowOuter* aWindow, bool aDeep, nsPIDOMWindowOuter** aFocusedWindow); + static uint32_t FocusOptionsToFocusManagerFlags( + const mozilla::dom::FocusOptions& aOptions); + /** * Returns the content node that focus will be redirected to if aContent was * focused. This is used for the special case of certain XUL elements such diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 56b7c36da2..0e20834fe8 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -3590,7 +3590,7 @@ HTMLInputElement::Blur(ErrorResult& aError) } void -HTMLInputElement::Focus(ErrorResult& aError) +HTMLInputElement::Focus(const FocusOptions& aOptions, ErrorResult& aError) { if (mType == NS_FORM_INPUT_NUMBER) { // Focus our anonymous text control, if we have one. @@ -3600,7 +3600,7 @@ HTMLInputElement::Focus(ErrorResult& aError) RefPtr textControl = numberControlFrame->GetAnonTextControl(); if (textControl) { - textControl->Focus(aError); + textControl->Focus(aOptions, aError); return; } } @@ -3616,7 +3616,7 @@ HTMLInputElement::Focus(ErrorResult& aError) } if (mType != NS_FORM_INPUT_FILE) { - nsGenericHTMLElement::Focus(aError); + nsGenericHTMLElement::Focus(aOptions, aError); return; } diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 462d29b524..9c73d10b1c 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -134,7 +134,8 @@ public: virtual int32_t TabIndexDefault() override; using nsGenericHTMLElement::Focus; virtual void Blur(ErrorResult& aError) override; - virtual void Focus(ErrorResult& aError) override; + virtual void Focus(const FocusOptions& aOptions, + ErrorResult& aError) override; // nsINode #if !defined(ANDROID) && !defined(XP_MACOSX) diff --git a/dom/html/HTMLLabelElement.cpp b/dom/html/HTMLLabelElement.cpp index 00df4b5d94..46858e4ce1 100644 --- a/dom/html/HTMLLabelElement.cpp +++ b/dom/html/HTMLLabelElement.cpp @@ -93,14 +93,15 @@ HTMLLabelElement::GetForm() const } void -HTMLLabelElement::Focus(ErrorResult& aError) +HTMLLabelElement::Focus(const FocusOptions& aOptions, + ErrorResult& aError) { // retarget the focus method at the for content nsIFocusManager* fm = nsFocusManager::GetFocusManager(); if (fm) { nsCOMPtr elem = do_QueryObject(GetLabeledElement()); if (elem) - fm->SetFocus(elem, 0); + fm->SetFocus(elem, nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions)); } } diff --git a/dom/html/HTMLLabelElement.h b/dom/html/HTMLLabelElement.h index 5771a001f7..29a71b0837 100644 --- a/dom/html/HTMLLabelElement.h +++ b/dom/html/HTMLLabelElement.h @@ -56,7 +56,8 @@ public: } using nsGenericHTMLElement::Focus; - virtual void Focus(mozilla::ErrorResult& aError) override; + virtual void Focus(const FocusOptions& aOptions, + ErrorResult& aError) override; virtual bool IsDisabled() const override { return false; } diff --git a/dom/html/HTMLLegendElement.cpp b/dom/html/HTMLLegendElement.cpp index 87df3ba3e7..73038e38b1 100644 --- a/dom/html/HTMLLegendElement.cpp +++ b/dom/html/HTMLLegendElement.cpp @@ -86,7 +86,8 @@ HTMLLegendElement::UnbindFromTree(bool aDeep, bool aNullParent) } void -HTMLLegendElement::Focus(ErrorResult& aError) +HTMLLegendElement::Focus(const FocusOptions& aOptions, + ErrorResult& aError) { nsIFrame* frame = GetPrimaryFrame(); if (!frame) { @@ -95,7 +96,7 @@ HTMLLegendElement::Focus(ErrorResult& aError) int32_t tabIndex; if (frame->IsFocusable(&tabIndex, false)) { - nsGenericHTMLElement::Focus(aError); + nsGenericHTMLElement::Focus(aOptions, aError); return; } @@ -108,7 +109,8 @@ HTMLLegendElement::Focus(ErrorResult& aError) nsCOMPtr result; aError = fm->MoveFocus(nullptr, this, nsIFocusManager::MOVEFOCUS_FORWARD, - nsIFocusManager::FLAG_NOPARENTFRAME, + nsIFocusManager::FLAG_NOPARENTFRAME | + nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions), getter_AddRefs(result)); } @@ -116,9 +118,10 @@ bool HTMLLegendElement::PerformAccesskey(bool aKeyCausesActivation, bool aIsTrustedEvent) { - // just use the same behaviour as the focus method + FocusOptions options; ErrorResult rv; - Focus(rv); + + Focus(options, rv); return NS_SUCCEEDED(rv.StealNSResult()); } diff --git a/dom/html/HTMLLegendElement.h b/dom/html/HTMLLegendElement.h index 6ce3140ec9..79b585edae 100644 --- a/dom/html/HTMLLegendElement.h +++ b/dom/html/HTMLLegendElement.h @@ -24,7 +24,8 @@ public: NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLLegendElement, legend) using nsGenericHTMLElement::Focus; - virtual void Focus(ErrorResult& aError) override; + virtual void Focus(const FocusOptions& aOptions, + ErrorResult& aError) override; virtual bool PerformAccesskey(bool aKeyCausesActivation, bool aIsTrustedEvent) override; diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 997decd7a0..ba9db1febd 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -150,8 +150,9 @@ public: // If something is focused in the same document, ignore autofocus. if (!fm->GetFocusedContent() || fm->GetFocusedContent()->OwnerDoc() != document) { - mozilla::ErrorResult rv; - mElement->Focus(rv); + FocusOptions options; + ErrorResult rv; + mElement->Focus(options, rv); return rv.StealNSResult(); } diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 5807c1746d..cd1bf59069 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -434,8 +434,9 @@ public: return rv.StealNSResult(); } NS_IMETHOD Focus() final override { + mozilla::dom::FocusOptions options; mozilla::ErrorResult rv; - Focus(rv); + Focus(options, rv); return rv.StealNSResult(); } NS_IMETHOD GetDraggable(bool* aDraggable) final override { diff --git a/dom/mathml/MathMLElement.cpp b/dom/mathml/MathMLElement.cpp index e453741450..17eb7b5bfd 100644 --- a/dom/mathml/MathMLElement.cpp +++ b/dom/mathml/MathMLElement.cpp @@ -435,8 +435,7 @@ MathMLElement::ParseNumericValue(const nsString& aString, number.Append(c); } - if (/*StaticPrefs::mathml_legacy_number_syntax_disabled() &&*/ gotDot && - str[i - 1] == '.') { + if (gotDot && str[i - 1] == '.') { if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) { ReportLengthParseError(aString, aDocument); } diff --git a/dom/webidl/Element.webidl b/dom/webidl/Element.webidl index ab62440f6f..4e239e5fe7 100644 --- a/dom/webidl/Element.webidl +++ b/dom/webidl/Element.webidl @@ -169,6 +169,23 @@ interface mixin ElementCSSInlineStyle { readonly attribute CSSStyleDeclaration style; }; +// https://html.spec.whatwg.org/#focus-management-apis +dictionary FocusOptions { + boolean preventScroll = false; +}; + +interface mixin HTMLOrForeignElement { + [SameObject] readonly attribute DOMStringMap dataset; + // See bug 1389421 + // attribute DOMString nonce; // intentionally no [CEReactions] + + // See bug 1575154 + // [CEReactions] attribute boolean autofocus; + [CEReactions, SetterThrows, Pure] attribute long tabIndex; + [Throws] void focus(optional FocusOptions options); + [Throws] void blur(); +}; + // http://dev.w3.org/csswg/cssom-view/ enum ScrollLogicalPosition { "start", "center", "end", "nearest" }; dictionary ScrollIntoViewOptions : ScrollOptions { diff --git a/dom/webidl/HTMLElement.webidl b/dom/webidl/HTMLElement.webidl index ee98607388..31046fabdb 100644 --- a/dom/webidl/HTMLElement.webidl +++ b/dom/webidl/HTMLElement.webidl @@ -22,8 +22,6 @@ interface HTMLElement : Element { // attribute boolean translate; [CEReactions, SetterThrows, Pure] attribute DOMString dir; - [Constant] - readonly attribute DOMStringMap dataset; [CEReactions, GetterThrows, Pure] attribute [TreatNullAs=EmptyString] DOMString innerText; @@ -32,12 +30,6 @@ interface HTMLElement : Element { [CEReactions, SetterThrows, Pure] attribute boolean hidden; void click(); - [CEReactions, SetterThrows, Pure] - attribute long tabIndex; - [Throws] - void focus(); - [Throws] - void blur(); [CEReactions, SetterThrows, Pure] attribute DOMString accessKey; [Pure] @@ -92,6 +84,7 @@ interface mixin TouchEventHandlers { }; HTMLElement includes GlobalEventHandlers; +HTMLElement includes HTMLOrForeignElement; HTMLElement includes DocumentAndElementEventHandlers; HTMLElement includes ElementCSSInlineStyle; HTMLElement includes TouchEventHandlers; diff --git a/dom/webidl/MathMLElement.webidl b/dom/webidl/MathMLElement.webidl index c54896c4e7..e370eda2b0 100644 --- a/dom/webidl/MathMLElement.webidl +++ b/dom/webidl/MathMLElement.webidl @@ -13,7 +13,7 @@ [Exposed=Window] interface MathMLElement : Element { }; MathMLElement includes GlobalEventHandlers; -//MathMLElement includes HTMLOrForeignElement; +MathMLElement includes HTMLOrForeignElement; MathMLElement includes DocumentAndElementEventHandlers; MathMLElement includes ElementCSSInlineStyle; MathMLElement includes TouchEventHandlers; \ No newline at end of file diff --git a/dom/webidl/SVGElement.webidl b/dom/webidl/SVGElement.webidl index 866df8095f..592685a27b 100644 --- a/dom/webidl/SVGElement.webidl +++ b/dom/webidl/SVGElement.webidl @@ -15,18 +15,13 @@ interface SVGElement : Element { [Constant] readonly attribute SVGAnimatedString className; - [SameObject] readonly attribute DOMStringMap dataset; readonly attribute SVGSVGElement? ownerSVGElement; readonly attribute SVGElement? viewportElement; - - [SetterThrows, Pure] - attribute long tabIndex; - [Throws] void focus(); - [Throws] void blur(); }; SVGElement includes GlobalEventHandlers; +SVGElement includes HTMLOrForeignElement; SVGElement includes DocumentAndElementEventHandlers; SVGElement includes ElementCSSInlineStyle; SVGElement includes TouchEventHandlers; diff --git a/dom/webidl/XULElement.webidl b/dom/webidl/XULElement.webidl index ab2300df53..9e43532c76 100644 --- a/dom/webidl/XULElement.webidl +++ b/dom/webidl/XULElement.webidl @@ -96,10 +96,6 @@ interface XULElement : Element { [Throws] readonly attribute BoxObject? boxObject; - [Throws] - void focus(); - [Throws] - void blur(); [Throws] void click(); void doCommand(); @@ -134,6 +130,7 @@ interface mixin MozFrameLoaderOwner { XULElement includes GlobalEventHandlers; XULElement includes ElementCSSInlineStyle; +XULElement includes HTMLOrForeignElement; XULElement includes TouchEventHandlers; XULElement includes MozFrameLoaderOwner; XULElement includes OnErrorEventHandlerForNodes; diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index b2f225cedf..12229d4e4a 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -1720,7 +1720,8 @@ NS_IMETHODIMP nsXULElement::Focus() { ErrorResult rv; - Focus(rv); + FocusOptions options; + Focus(options, rv); return rv.StealNSResult(); } diff --git a/layout/forms/nsNumberControlFrame.cpp b/layout/forms/nsNumberControlFrame.cpp index a1dfd3c083..3f4cde6fd0 100644 --- a/layout/forms/nsNumberControlFrame.cpp +++ b/layout/forms/nsNumberControlFrame.cpp @@ -308,7 +308,11 @@ public: NS_IMETHOD Run() override { if (mNumber->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS)) { - HTMLInputElement::FromContent(mTextField)->Focus(); + // This job shouldn't be triggered by a WebIDL interface, hence the + // default options can be used. + FocusOptions options; + ErrorResult rv; + HTMLInputElement::FromContent(mTextField)->Focus(options, rv); } return NS_OK; @@ -595,7 +599,11 @@ nsNumberControlFrame::HandleFocusEvent(WidgetEvent* aEvent) if (aEvent->mOriginalTarget != mTextField) { // Move focus to our text field RefPtr textField = HTMLInputElement::FromContent(mTextField); - textField->Focus(); + // Use default FocusOptions, because this method isn't supposed to be called + // from a WebIDL interface. + FocusOptions options; + ErrorResult rv; + textField->Focus(options, rv); } }