Bug 1640280 - Extend autofocus to SVGGraphicsElement r=emilio
☠☠ backed out by 30a6807bb0fe ☠ ☠
authorlongsonr <longsonr@gmail.com>
Sun, 24 May 2020 21:04:14 +0000
changeset 531815 1334652d0ec42759562bd39be74e165971037889
parent 531814 30fedc6e38f8b13a65c27a4a2f56c472a312ab2b
child 531816 c9f80397bbecbaa3b026d20fe8fc58c9e44fbed2
push id37446
push userabutkovits@mozilla.com
push dateMon, 25 May 2020 09:34:40 +0000
treeherdermozilla-central@9155018d4978 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1640280, 662496
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1640280 - Extend autofocus to SVGGraphicsElement r=emilio Note we're still limited to markup only because of bug 662496 so we don't pass the web platform tests for this feature. Chrome has implemented support per https://www.chromestatus.com/feature/5654905853313024 In SVG only SVGGraphicsElements are focusable. Differential Revision: https://phabricator.services.mozilla.com/D76546
dom/base/BindContext.cpp
dom/base/BindContext.h
dom/base/moz.build
dom/html/nsGenericHTMLElement.cpp
dom/svg/SVGGraphicsElement.cpp
dom/svg/SVGGraphicsElement.h
dom/webidl/SVGGraphicsElement.webidl
layout/reftests/svg/autofocus-01-ref.svg
layout/reftests/svg/autofocus-01a.svg
layout/reftests/svg/autofocus-01b.svg
layout/reftests/svg/autofocus-02a.svg
layout/reftests/svg/autofocus-02b.svg
layout/reftests/svg/reftest.list
new file mode 100644
--- /dev/null
+++ b/dom/base/BindContext.cpp
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/BindContext.h"
+
+#include "mozilla/StaticPrefs_browser.h"
+
+namespace mozilla {
+namespace dom {
+
+bool BindContext::AllowsAutoFocus() const {
+  if (!StaticPrefs::browser_autofocus()) {
+    return false;
+  }
+  if (!InUncomposedDoc()) {
+    return false;
+  }
+  if (mDoc.IsBeingUsedAsImage()) {
+    return false;
+  }
+  return IsSameOriginAsTop();
+}
+
+bool BindContext::IsSameOriginAsTop() const {
+  BrowsingContext* browsingContext = mDoc.GetBrowsingContext();
+  if (!browsingContext) {
+    return false;
+  }
+
+  nsPIDOMWindowOuter* topWindow = browsingContext->Top()->GetDOMWindow();
+  if (!topWindow) {
+    // If we don't have a DOMWindow, We are not in same origin.
+    return false;
+  }
+
+  Document* topLevelDocument = topWindow->GetExtantDoc();
+  if (!topLevelDocument) {
+    return false;
+  }
+
+  return NS_SUCCEEDED(nsContentUtils::CheckSameOrigin(topLevelDocument, &mDoc));
+}
+
+}  // namespace dom
+}  // namespace mozilla
--- a/dom/base/BindContext.h
+++ b/dom/base/BindContext.h
@@ -42,16 +42,23 @@ struct MOZ_STACK_CLASS BindContext final
 
   Document* GetUncomposedDoc() const {
     return mInUncomposedDoc ? &mDoc : nullptr;
   }
 
   // Whether our subtree root is changing as a result of this operation.
   bool SubtreeRootChanges() const { return mSubtreeRootChanges; }
 
+  // Autofocus is allowed only if the is in the same origin as the top level
+  // document.
+  // https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute:same-origin
+  // In addition, the document should not be already loaded and the
+  // "browser.autofocus" preference should be 'true'.
+  bool AllowsAutoFocus() const;
+
   // This constructor should be used for regular appends to content.
   explicit BindContext(nsINode& aParent)
       : mDoc(*aParent.OwnerDoc()),
         mInComposedDoc(aParent.IsInComposedDoc()),
         mInUncomposedDoc(aParent.IsInUncomposedDoc()),
         mSubtreeRootChanges(true) {}
 
   // When re-binding a shadow host into a tree, we re-bind all the shadow tree
@@ -77,16 +84,20 @@ struct MOZ_STACK_CLASS BindContext final
     MOZ_ASSERT(mInComposedDoc, "Binding NAC in a disconnected subtree?");
   }
 
  private:
   static bool IsLikelyUndisplayed(const nsINode& aParent) {
     return aParent.IsAnyOfHTMLElements(nsGkAtoms::style, nsGkAtoms::script);
   }
 
+  // Returns true iff the document is in the same origin as the top level
+  // document.
+  bool IsSameOriginAsTop() const;
+
   Document& mDoc;
 
   const bool mInComposedDoc;
   const bool mInUncomposedDoc;
 
   // Whether the bind operation will change the subtree root of the content
   // we're binding.
   const bool mSubtreeRootChanges;
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -265,16 +265,17 @@ if CONFIG['FUZZING']:
     ]
 
 UNIFIED_SOURCES += [
     'AbstractRange.cpp',
     'AnonymousContent.cpp',
     'Attr.cpp',
     'AttrArray.cpp',
     'BarProps.cpp',
+    'BindContext.cpp',
     'BodyConsumer.cpp',
     'BodyStream.cpp',
     'BodyUtil.cpp',
     'BorrowedAttrInfo.cpp',
     'CharacterData.cpp',
     'ChildIterator.cpp',
     'ChromeMessageBroadcaster.cpp',
     'ChromeMessageSender.cpp',
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -11,17 +11,16 @@
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/MappedDeclarations.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/TextEditor.h"
-#include "mozilla/StaticPrefs_browser.h"
 #include "mozilla/StaticPrefs_layout.h"
 
 #include "nscore.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsCOMPtr.h"
 #include "nsAtom.h"
 #include "nsQueryObject.h"
@@ -1654,54 +1653,23 @@ nsIContent::IMEState nsGenericHTMLFormEl
   IMEState state;
   nsresult rv = textEditor->GetPreferredIMEState(&state);
   if (NS_FAILED(rv)) {
     return nsGenericHTMLElement::GetDesiredIMEState();
   }
   return state;
 }
 
-static bool IsSameOriginAsTop(const BindContext& aContext,
-                              const nsGenericHTMLFormElement* aElement) {
-  MOZ_ASSERT(aElement);
-
-  BrowsingContext* browsingContext = aContext.OwnerDoc().GetBrowsingContext();
-  if (!browsingContext) {
-    return false;
-  }
-
-  nsPIDOMWindowOuter* topWindow = browsingContext->Top()->GetDOMWindow();
-  if (!topWindow) {
-    // If we don't have a DOMWindow, We are not in same origin.
-    return false;
-  }
-
-  Document* topLevelDocument = topWindow->GetExtantDoc();
-  if (!topLevelDocument) {
-    return false;
-  }
-
-  return NS_SUCCEEDED(
-      nsContentUtils::CheckSameOrigin(topLevelDocument, aElement));
-}
-
 nsresult nsGenericHTMLFormElement::BindToTree(BindContext& aContext,
                                               nsINode& aParent) {
   nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // An autofocus event has to be launched if the autofocus attribute is
-  // specified and the element accepts the autofocus attribute and only if the
-  // target document is in the same origin as the top level document.
-  // https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute:same-origin
-  // In addition, the document should not be already loaded and the
-  // "browser.autofocus" preference should be 'true'.
-  if (IsAutofocusable() && HasAttr(kNameSpaceID_None, nsGkAtoms::autofocus) &&
-      StaticPrefs::browser_autofocus() && IsInUncomposedDoc() &&
-      IsSameOriginAsTop(aContext, this)) {
+  if (IsAutofocusable() && HasAttr(nsGkAtoms::autofocus) &&
+      aContext.AllowsAutoFocus()) {
     aContext.OwnerDoc().SetAutoFocusElement(this);
   }
 
   // If @form is set, the element *has* to be in a composed document, otherwise
   // it wouldn't be possible to find an element with the corresponding id.
   // If @form isn't set, the element *has* to have a parent, otherwise it
   // wouldn't be possible to find a form ancestor.
   // We should not call UpdateFormOwner if none of these conditions are
--- a/dom/svg/SVGGraphicsElement.cpp
+++ b/dom/svg/SVGGraphicsElement.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/SVGGraphicsElement.h"
 
+#include "mozilla/dom/BindContext.h"
+
 namespace mozilla {
 namespace dom {
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(SVGGraphicsElement, SVGGraphicsElementBase)
 NS_IMPL_RELEASE_INHERITED(SVGGraphicsElement, SVGGraphicsElementBase)
@@ -57,10 +59,23 @@ bool SVGGraphicsElement::IsSVGFocusable(
 
 bool SVGGraphicsElement::IsFocusableInternal(int32_t* aTabIndex,
                                              bool aWithMouse) {
   bool isFocusable = false;
   IsSVGFocusable(&isFocusable, aTabIndex);
   return isFocusable;
 }
 
+nsresult SVGGraphicsElement::BindToTree(BindContext& aContext,
+                                        nsINode& aParent) {
+  nsresult rv = SVGGraphicsElementBase::BindToTree(aContext, aParent);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (IsFocusable() && HasAttr(nsGkAtoms::autofocus) &&
+      aContext.AllowsAutoFocus()) {
+    aContext.OwnerDoc().SetAutoFocusElement(this);
+  }
+
+  return NS_OK;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/SVGGraphicsElement.h
+++ b/dom/svg/SVGGraphicsElement.h
@@ -20,17 +20,24 @@ class SVGGraphicsElement : public SVGGra
   explicit SVGGraphicsElement(
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
   ~SVGGraphicsElement() = default;
 
  public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
 
+  // WebIDL
+  bool Autofocus() const { return GetBoolAttr(nsGkAtoms::autofocus); }
+  void SetAutofocus(bool aAutofocus) {
+    SetBoolAttr(nsGkAtoms::autofocus, aAutofocus);
+  }
+
   bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override;
+  nsresult BindToTree(BindContext&, nsINode& aParent) override;
   SVGElement* AsSVGElement() final { return this; }
 
  protected:
   // returns true if focusability has been definitively determined otherwise
   // false
   bool IsSVGFocusable(bool* aIsFocusable, int32_t* aTabIndex);
 
   template <typename T>
--- a/dom/webidl/SVGGraphicsElement.webidl
+++ b/dom/webidl/SVGGraphicsElement.webidl
@@ -14,16 +14,19 @@ dictionary SVGBoundingBoxOptions {
   boolean fill = true;
   boolean stroke = false;
   boolean markers = false;
   boolean clipped = false;
 };
 
 [Exposed=Window]
 interface SVGGraphicsElement : SVGElement {
+  [Pure]
+  attribute boolean autofocus;
+
   readonly attribute SVGAnimatedTransformList transform;
 
   readonly attribute SVGElement? nearestViewportElement;
   readonly attribute SVGElement? farthestViewportElement;
 
   [NewObject]
   SVGRect getBBox(optional SVGBoundingBoxOptions aOptions = {});
   // Not implemented
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/autofocus-01-ref.svg
@@ -0,0 +1,5 @@
+<svg width="400" height="110" xmlns="http://www.w3.org/2000/svg" onload="document.querySelector('a').focus();">
+  <a href="#">
+    <rect x="20" y="20" width="300" height="100" fill="lime"/>
+  </a>
+</svg> 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/autofocus-01a.svg
@@ -0,0 +1,3 @@
+<svg width="400" height="110" xmlns="http://www.w3.org/2000/svg">
+  <rect x="20" y="20" width="300" height="100" fill="lime" tabindex="0" autofocus="true"/>
+</svg> 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/autofocus-01b.svg
@@ -0,0 +1,5 @@
+<svg width="400" height="110" xmlns="http://www.w3.org/2000/svg">
+  <a href="#" autofocus="true">
+    <rect x="20" y="20" width="300" height="100" fill="lime"/>
+  </a>
+</svg> 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/autofocus-02a.svg
@@ -0,0 +1,5 @@
+<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
+  <rect width="100%" height="100%" fill="lime"/>
+  <!-- need tabindex for autofocus to work -->
+  <rect x="20" y="20" width="300" height="100" fill="lime" autofocus="true"/>
+</svg> 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/autofocus-02b.svg
@@ -0,0 +1,7 @@
+<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
+  <rect width="100%" height="100%" fill="lime"/>
+  <!-- need href for autofocus to work -->
+  <a autofocus="true">
+    <rect x="20" y="20" width="300" height="100" fill="lime"/>
+  </a>
+</svg> 
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -20,16 +20,21 @@ include text/reftest.list
 include load-only/reftest.list
 
 # Mozilla only tests (i.e. those containing XUL/XBL/etc.)
 include moz-only/reftest.list
 
 # svg-integration tests (using svg effects in e.g. HTML)
 include svg-integration/reftest.list
 
+fails-if(Android) needs-focus == autofocus-01a.svg autofocus-01-ref.svg # bug 1640512
+needs-focus == autofocus-01b.svg autofocus-01-ref.svg
+needs-focus == autofocus-02a.svg pass.svg
+needs-focus == autofocus-02b.svg pass.svg
+
 == background-svg-without-height.html background-ref.html
 == background-svg-without-height-width.html background-ref.html
 == background-svg-without-width.html background-ref.html
 
 == baseline-middle-01.svg pass.svg
 
 fails-if(Android&&webrender&&device) == blend-color-burn.svg blend-color-burn-ref.svg
 fails-if(Android&&webrender&&device) == blend-color-dodge.svg blend-color-dodge-ref.svg