Bug 1640280 - Extend autofocus to SVGGraphicsElement draft
authorlongsonr <longsonr@gmail.com>
Sun, 24 May 2020 06:30:27 +0000
changeset 2913092 cb5d6eb78859bf1e135c787e3f8cf242fac752d1
parent 2912807 9a2f741cef6ae4ae568a17fd3ffe52599f0a574f
child 2913093 9d3f63838843646494bf1e7fad1ad108da74b954
push id542232
push userreviewbot
push dateSun, 24 May 2020 06:30:58 +0000
treeherdertry@9d3f63838843 [default view] [failures only]
bugs1640280
milestone78.0a1
Bug 1640280 - Extend autofocus to SVGGraphicsElement Differential Diff: PHID-DIFF-ov4jmfyr4bo34htjhr6e
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,3 @@
+<svg width="400" height="110" xmlns="http://www.w3.org/2000/svg" onload="document.querySelector('rect').focus();">
+  <rect x="20" y="20" width="300" height="100" fill="lime" tabindex="0"/>
+</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
 
+== autofocus-01a.svg autofocus-01-ref.svg
+fails-if(Android) == autofocus-01b.svg autofocus-01-ref.svg
+== autofocus-02a.svg pass.svg
+== 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