Bug 546995 - Implement autofocus attribute. r=sicking, sr=smaug
authorMounir Lamouri <mounir.lamouri@gmail.com>
Wed, 19 May 2010 19:52:17 +0200
changeset 42460 13ffec2c6673
parent 42459 d7fc936e9d85
child 42461 e0a9a9ea63f1
push id13355
push userdgottwald@mozilla.com
push dateWed, 19 May 2010 17:55:06 +0000
treeherdermozilla-central@e0a9a9ea63f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking, smaug
bugs546995
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 546995 - Implement autofocus attribute. r=sicking, sr=smaug
content/base/src/nsGkAtomList.h
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLButtonElement.cpp
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLSelectElement.cpp
content/html/content/src/nsHTMLSelectElement.h
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/content/test/Makefile.in
content/html/content/test/file_bug546995.html
content/html/content/test/test_bug546995-1.html
content/html/content/test/test_bug546995-2.html
content/html/content/test/test_bug546995-3.html
content/html/content/test/test_bug546995-4.html
content/html/content/test/test_bug546995-5.html
dom/interfaces/html/nsIDOMNSHTMLButtonElement.idl
dom/interfaces/html/nsIDOMNSHTMLInputElement.idl
dom/interfaces/html/nsIDOMNSHTMLSelectElement.idl
dom/interfaces/html/nsIDOMNSHTMLTextAreaElement.idl
dom/tests/browser/Makefile.in
dom/tests/browser/browser_autofocus_background.js
modules/libpref/src/init/all.js
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -118,16 +118,17 @@ GK_ATOM(attribute, "attribute")
 GK_ATOM(attributeSet, "attribute-set")
 GK_ATOM(aural, "aural")
 GK_ATOM(_auto, "auto")
 #ifdef MOZ_MEDIA
 GK_ATOM(autobuffer, "autobuffer")
 #endif
 GK_ATOM(autocheck, "autocheck")
 GK_ATOM(autocomplete, "autocomplete")
+GK_ATOM(autofocus, "autofocus")
 #ifdef MOZ_MEDIA
 GK_ATOM(autoplay, "autoplay")
 #endif
 GK_ATOM(autorepeatbutton, "autorepeatbutton")
 GK_ATOM(axis, "axis")
 GK_ATOM(b, "b")
 GK_ATOM(background, "background")
 GK_ATOM(base, "base")
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -106,16 +106,18 @@
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
 #include "nsEventDispatcher.h"
 #include "nsLayoutUtils.h"
 #include "nsContentCreatorFunctions.h"
 #include "mozAutoDocUpdate.h"
 #include "nsHtml5Module.h"
 
+#include "nsThreadUtils.h"
+
 class nsINodeInfo;
 class nsIDOMNodeList;
 class nsRuleWalker;
 
 // XXX todo: add in missing out-of-memory checks
 
 //----------------------------------------------------------------------
 
@@ -164,16 +166,57 @@ nsGenericHTMLElement::Init(nsINodeInfo *
 {
   GEUS_ElementCreated(aNodeInfo);
 
   return nsGenericHTMLElementBase::Init(aNodeInfo);
 }
 
 #endif
 
+/**
+ * nsAutoFocusEvent is used to dispatch a focus event when a
+ * nsGenericHTMLFormElement is binded to the tree with the autofocus attribute
+ * enabled.
+ */
+class nsAutoFocusEvent : public nsRunnable
+{
+public:
+  nsAutoFocusEvent(nsGenericHTMLFormElement* aElement) : mElement(aElement) {}
+
+  NS_IMETHOD Run() {
+    nsFocusManager* fm = nsFocusManager::GetFocusManager();
+    if (!fm) {
+      return NS_ERROR_NULL_POINTER;
+    }
+
+    nsIDocument* document = mElement->GetOwnerDoc();
+    if (!document) {
+      return NS_OK;
+    }
+
+    // Do not autofocus if an sub-window is focused.
+    nsPIDOMWindow* window = document->GetWindow();
+    if (window && window->GetFocusedNode()) {
+      return NS_OK;
+    }
+
+    // If something is focused in the same document, ignore autofocus.
+    if (!fm->GetFocusedContent() ||
+        fm->GetFocusedContent()->GetOwnerDoc() != document) {
+      return mElement->Focus();
+    }
+
+    return NS_OK;
+  }
+private:
+  // NOTE: nsGenericHTMLFormElement is saved as a nsGenericHTMLElement
+  // because AddRef/Release are ambiguous with nsGenericHTMLFormElement
+  // and Focus() is declared (and defined) in nsGenericHTMLElement class.
+  nsRefPtr<nsGenericHTMLElement> mElement;
+};
 
 class nsGenericHTMLElementTearoff : public nsIDOMNSHTMLElement,
                                     public nsIDOMElementCSSInlineStyle
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   nsGenericHTMLElementTearoff(nsGenericHTMLElement *aElement)
     : mElement(aElement)
@@ -2395,16 +2438,30 @@ nsGenericHTMLFormElement::BindToTree(nsI
                                      nsIContent* aParent,
                                      nsIContent* aBindingParent,
                                      PRBool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
+
+  // An autofocus event has to be launched if the autofocus attribute is
+  // specified and the element accept the autofocus attribute. In addition,
+  // the document should not be already loaded and the "browser.autofocus"
+  // preference should be 'true'.
+  if (AcceptAutofocus() && HasAttr(kNameSpaceID_None, nsGkAtoms::autofocus) &&
+      aDocument &&
+      aDocument->GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE &&
+      nsContentUtils::GetBoolPref("browser.autofocus", PR_TRUE)) {
+    nsCOMPtr<nsIRunnable> event = new nsAutoFocusEvent(this);
+    rv = NS_DispatchToCurrentThread(event);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   if (!aParent) {
     return NS_OK;
   }
 
   PRBool hadForm = (mForm != nsnull);
   
   if (!mForm) {
     // We now have a parent, so we may have picked up an ancestor form.  Search
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -807,16 +807,24 @@ public:
 protected:
   virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                                  const nsAString* aValue, PRBool aNotify);
 
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                                 const nsAString* aValue, PRBool aNotify);
 
   /**
+   * Returns if the element should react on autofocus attribute.
+   */
+  virtual PRBool AcceptAutofocus() const
+  {
+    return PR_FALSE;
+  }
+
+  /**
    * Returns true if the control can be disabled
    */
   PRBool CanBeDisabled() const;
 
   void UpdateEditableFormControlState();
 
   // The focusability state of this form control.  eUnfocusable means that it
   // shouldn't be focused at all, eInactiveWindow means it's in an inactive
--- a/content/html/content/src/nsHTMLButtonElement.cpp
+++ b/content/html/content/src/nsHTMLButtonElement.cpp
@@ -101,16 +101,18 @@ public:
 
   // nsIDOMNSHTMLButtonElement
   // Can't just use the macro, since it shares GetType with
   // nsIDOMHTMLButtonElement
   NS_IMETHOD Blur();
   NS_IMETHOD Focus();
   NS_IMETHOD Click();
   NS_IMETHOD SetType(const nsAString& aType);
+  NS_IMETHOD GetAutofocus(PRBool* aAutofocus);
+  NS_IMETHOD SetAutofocus(PRBool aAutofocus);
 
   // overriden nsIFormControl methods
   NS_IMETHOD_(PRInt32) GetType() const { return mType; }
   NS_IMETHOD Reset();
   NS_IMETHOD SubmitNamesValues(nsFormSubmission* aFormSubmission,
                                nsIContent* aSubmitElement);
   NS_IMETHOD SaveState();
   PRBool RestoreState(nsPresState* aState);
@@ -134,16 +136,21 @@ public:
                                 nsAttrValue& aResult);
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
   virtual void DoneCreatingElement();
 
 protected:
+  virtual PRBool AcceptAutofocus() const
+  {
+    return PR_TRUE;
+  }
+
   PRInt8 mType;
   PRPackedBool mHandlingClick;
   PRPackedBool mDisabledChanged;
   PRPackedBool mInInternalActivate;
 
 private:
   // The analogue of defaultValue in the DOM for input and textarea
   nsresult SetDefaultValue(const nsAString& aDefaultValue);
@@ -197,16 +204,17 @@ NS_IMPL_ELEMENT_CLONE(nsHTMLButtonElemen
 
 NS_IMETHODIMP
 nsHTMLButtonElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
   return nsGenericHTMLFormElement::GetForm(aForm);
 }
 
 NS_IMPL_STRING_ATTR(nsHTMLButtonElement, AccessKey, accesskey)
+NS_IMPL_BOOL_ATTR(nsHTMLButtonElement, Autofocus, autofocus)
 NS_IMPL_BOOL_ATTR(nsHTMLButtonElement, Disabled, disabled)
 NS_IMPL_STRING_ATTR(nsHTMLButtonElement, Name, name)
 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLButtonElement, TabIndex, tabindex, 0)
 NS_IMPL_STRING_ATTR(nsHTMLButtonElement, Value, value)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLButtonElement, Type, type,
                                 kButtonDefaultType->tag)
 
 NS_IMETHODIMP
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -390,16 +390,21 @@ protected:
 
   void SelectAll(nsPresContext* aPresContext);
   PRBool IsImage() const
   {
     return AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                        nsGkAtoms::image, eIgnoreCase);
   }
 
+  virtual PRBool AcceptAutofocus() const
+  {
+    return PR_TRUE;
+  }
+
   /**
    * Fire the onChange event
    */
   void FireOnChange();
 
   /**
    * Visit a the group of radio buttons this radio belongs to
    * @param aVisitor the visitor to visit with
@@ -782,16 +787,17 @@ nsHTMLInputElement::GetForm(nsIDOMHTMLFo
 }
 
 //NS_IMPL_STRING_ATTR(nsHTMLInputElement, DefaultValue, value)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, DefaultChecked, checked)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Accept, accept)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, AccessKey, accesskey)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Align, align)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Alt, alt)
+NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Autofocus, autofocus)
 //NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Checked, checked)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Disabled, disabled)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Multiple, multiple)
 NS_IMPL_NON_NEGATIVE_INT_ATTR(nsHTMLInputElement, MaxLength, maxlength)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Name, name)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, ReadOnly, readonly)
 NS_IMPL_URI_ATTR(nsHTMLInputElement, Src, src)
 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLInputElement, TabIndex, tabindex, 0)
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -1197,16 +1197,17 @@ nsHTMLSelectElement::SetValue(const nsAS
       }
     }
   }
 
   return rv;
 }
 
 
+NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Autofocus, autofocus)
 NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Disabled, disabled)
 NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Multiple, multiple)
 NS_IMPL_STRING_ATTR(nsHTMLSelectElement, Name, name)
 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLSelectElement, Size, size, 0)
 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLSelectElement, TabIndex, tabindex, 0)
 
 NS_IMETHODIMP
 nsHTMLSelectElement::Blur()
--- a/content/html/content/src/nsHTMLSelectElement.h
+++ b/content/html/content/src/nsHTMLSelectElement.h
@@ -463,16 +463,21 @@ protected:
    * Rebuilds the options array from scratch as a fallback in error cases.
    */
   void RebuildOptionsArray();
 
 #ifdef DEBUG
   void VerifyOptionsArray();
 #endif
 
+  virtual PRBool AcceptAutofocus() const
+  {
+    return PR_TRUE;
+  }
+
   /** The options[] array */
   nsRefPtr<nsHTMLOptionCollection> mOptions;
   /** false if the parser is in the middle of adding children. */
   PRPackedBool    mIsDoneAddingChildren;
   /** true if our disabled state has changed from the default **/
   PRPackedBool    mDisabledChanged;
   /** true if child nodes are being added or removed.
    *  Used by nsSafeOptionListMutation.
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -195,16 +195,21 @@ protected:
    */
   void GetValueInternal(nsAString& aValue, PRBool aIgnoreWrap);
 
   nsresult SetValueInternal(const nsAString& aValue,
                             nsITextControlFrame* aFrame,
                             PRBool aUserInput);
   nsresult GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
 
+  virtual PRBool AcceptAutofocus() const
+  {
+    return PR_TRUE;
+  }
+
   /**
    * Common method to call from the various mutation observer methods.
    * aContent is a content node that's either the one that changed or its
    * parent; we should only respond to the change if aContent is non-anonymous.
    */
   void ContentChanged(nsIContent* aContent);
 
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom *aName,
@@ -357,16 +362,17 @@ nsHTMLTextAreaElement::IsHTMLFocusable(P
   }
 
   // disabled textareas are not focusable
   *aIsFocusable = !HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
   return PR_FALSE;
 }
 
 NS_IMPL_STRING_ATTR(nsHTMLTextAreaElement, AccessKey, accesskey)
+NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, Autofocus, autofocus)
 NS_IMPL_INT_ATTR(nsHTMLTextAreaElement, Cols, cols)
 NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, Disabled, disabled)
 NS_IMPL_NON_NEGATIVE_INT_ATTR(nsHTMLTextAreaElement, MaxLength, maxlength)
 NS_IMPL_STRING_ATTR(nsHTMLTextAreaElement, Name, name)
 NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, ReadOnly, readonly)
 NS_IMPL_INT_ATTR(nsHTMLTextAreaElement, Rows, rows)
 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLTextAreaElement, TabIndex, tabindex, 0)
 NS_IMPL_STRING_ATTR(nsHTMLTextAreaElement, Wrap, wrap)
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -164,12 +164,18 @@ include $(topsrcdir)/config/rules.mk
 		test_bug556645.html \
 		test_bug559284.html \
 		test_bug551670.html \
 		test_bug346485.html \
 		test_bug555567.html \
 		test_bug557620.html \
 		test_bug565538.html \
 		test_bug456229.html \
+		test_bug546995-1.html \
+		test_bug546995-2.html \
+		test_bug546995-3.html \
+		test_bug546995-4.html \
+		test_bug546995-5.html \
+		file_bug546995.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_bug546995.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<body onload="load();">
+<div id="content">
+  <script>
+    var c = document.getElementById('content');
+    var element = parent.gElement;
+
+    var e = document.createElement(element);
+    e.autofocus = false;
+    c.appendChild(e)
+
+    e = document.createElement(element);
+    e.autofocus = true;
+    e.id = 'a';
+    c.appendChild(e)
+
+    e = document.createElement(element);
+    e.autofocus = true;
+    c.appendChild(e)
+  </script>
+</div>
+
+<script>
+function load() {
+  // Our parent should have a look at us in 1 sec so focus events can be thrown.
+  setTimeout("parent.gGen.next()", 1000);
+}
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug546995-1.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=546995
+-->
+<head>
+  <title>Test for Bug 546995</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=546995">Mozilla Bug 546995</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <input id='i'>
+  <textarea id='t'></textarea>
+  <select id='s'></select>
+  <button id='b'></button>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 546995 **/
+
+/* This test in only testing IDL reflection, another one is testing the behavior */
+
+function checkAutofocusIDLAttribute(element)
+{
+  ok('autofocus' in element, "Element has the autofocus IDL attribute");
+  ok(!element.autofocus, "autofocus default value is false");
+  element.setAttribute('autofocus', 'autofocus');
+  ok(element.autofocus, "autofocus should be enabled");
+  element.removeAttribute('autofocus');
+  ok(!element.autofocus, "autofocus should be disabled");
+}
+
+// TODO: keygen should be added when correctly implemented, see bug 101019.
+checkAutofocusIDLAttribute(document.getElementById('i'));
+checkAutofocusIDLAttribute(document.getElementById('t'));
+checkAutofocusIDLAttribute(document.getElementById('s'));
+checkAutofocusIDLAttribute(document.getElementById('b'));
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug546995-2.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=546995
+-->
+<head>
+  <title>Test for Bug 546995</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=546995">Mozilla Bug 546995</a>
+<p id="display"></p>
+<iframe id="iframe"></iframe>
+<div id='content'>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+/** Test for Bug 546995 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var gGen = runTests();
+addLoadEvent(function() { gGen.next(); });
+
+var gElement = "";
+
+function runTests()
+{
+  var iframe = document.getElementById("iframe");
+  var iframeCw = iframe.contentWindow;
+
+  // TODO: keygen should be added when correctly implemented, see bug 101019.
+  var elements = ["input", "textarea", "select", "button"];
+
+  for each(element in elements) {
+    gElement = element;
+    iframeCw.location = "file_bug546995.html";
+    yield;
+    is(iframe.contentDocument.activeElement,
+       iframe.contentDocument.getElementById('a'),
+       "The first inserted element with autofocus should be focused");
+  }
+
+  // Now we want to focus the body element.
+  document.activeElement.blur();
+  is(document.activeElement, document.body,
+     "Body should be the active element");
+
+  // Adding elements with autofocus.
+  for each(element in elements) {
+    var e = document.createElement(element);
+    e.autofocus = true;
+    document.getElementById('content').appendChild(e);
+  }
+
+  // The element should still be focused. Waiting a second to be sure.
+  setTimeout(function() {
+               ok(document.activeElement, document.body,
+                  "After loading, elements can't be autofocused");
+               SimpleTest.finish();
+             }, 1000);
+  yield;
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug546995-3.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=546995
+-->
+<head>
+  <title>Test for Bug 546995</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<!-- When the page is loaded, we wait 2 seconds before launching the tests
+     because the focus events may not be finished when the page is loaded so
+     we have to wait or launch the tests when a focus events is thrown.
+     However, if autofocus is ignored, there will be no focus event so we can't
+     rely on that. -->
+<body onload="window.setTimeout(runTests, 2000);">
+<script type="application/javascript">
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+            .getService(Components.interfaces.nsIPrefBranch);
+  var gAutofocusPref = prefs.getBoolPref("browser.autofocus");
+  prefs.setBoolPref("browser.autofocus", false);
+
+  var gFocusHandled = false;
+</script>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=546995">Mozilla Bug 546995</a>
+<p id="display"></p>
+<div id="content">
+  <input autofocus onfocus='gFocusHandled=true;'>
+  <textarea autofocus onfocus='gFocusHandled=true;'></textarea>
+  <select autofocus onfocus='gFocusHandled=true;'></select>
+  <button autofocus onfocus='gFocusHandled=true;'></button>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 546995 **/
+
+// TODO: keygen should be added when correctly implemented, see bug 101019.
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests()
+{
+  // We do not check |document.activeElement| to be sure the input is never
+  // ever focused.
+  ok(!gFocusHandled,
+     "When browser.autofocus=false, autofocus attribute should be ignored");
+
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  prefs.setBoolPref("browser.autofocus", gAutofocusPref);
+
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug546995-4.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=546995
+-->
+<head>
+  <title>Test for Bug 546995</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<!-- When the page is loaded, we wait 2 seconds before launching the tests
+     because the focus events may not be finished when the page is loaded so
+     we have to wait or launch the tests when a focus events is thrown.
+     However, if autofocus is ignored, there will be no focus event so we can't
+     rely on that. -->
+<body onload="setTimeout(runTests, 2000);">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=546995">Mozilla Bug 546995</a>
+<p id="display"></p>
+<iframe id="iframe"></iframe>
+<script type="application/javascript">
+  var iframe = document.getElementById('iframe');
+  iframe.focus();
+  is(document.activeElement, iframe, "The iframe should have the focus");
+</script>
+<div id='content'>
+  <input id='i' autofocus>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+/** Test for Bug 546995 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests()
+{
+  isnot(document.activeElement, document.getElementById('i'),
+        "The focus should be able to leave a sub-document");
+  is(document.activeElement, iframe, "The iframe should have the focus");
+
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug546995-5.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=546995
+-->
+<head>
+  <title>Test for Bug 546995</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<!-- When the page is loaded, we wait 2 seconds before launching the tests
+     because the focus events may not be finished when the page is loaded so
+     we have to wait or launch the tests when a focus events is thrown.
+     However, if autofocus is ignored, there will be no focus event so we can't
+     rely on that. -->
+<body onload="setTimeout(runTests, 2000);">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=546995">Mozilla Bug 546995</a>
+<p id="display"></p>
+<script type="application/javascript">
+  document.body.focus();
+  is(document.activeElement, document.body,
+     "The document body should have te focus");
+</script>
+<div id='content'>
+  <input id='i' autofocus>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+/** Test for Bug 546995 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests()
+{
+  is(document.activeElement, document.getElementById('i'),
+     "The input element should be focused");
+
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/interfaces/html/nsIDOMNSHTMLButtonElement.idl
+++ b/dom/interfaces/html/nsIDOMNSHTMLButtonElement.idl
@@ -34,16 +34,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
-[scriptable, uuid(c914d7a4-63b3-4d40-943f-91a3c7ab0d4d)]
+[scriptable, uuid(41494073-ac06-4fbe-9d5e-f79c2586385b)]
 interface nsIDOMNSHTMLButtonElement : nsISupports
 {
+  attribute boolean         autofocus;
   void                      blur();
   void                      focus();
   void                      click();
   attribute DOMString       type;
 };
--- a/dom/interfaces/html/nsIDOMNSHTMLInputElement.idl
+++ b/dom/interfaces/html/nsIDOMNSHTMLInputElement.idl
@@ -37,19 +37,20 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
 interface nsIControllers;
 interface nsIDOMFileList;
 
-[scriptable, uuid(bc4acab7-1ad4-40ce-8b5f-c95bdd7bf8ff)]
+[scriptable, uuid(aa764da9-9046-4565-9e34-abd3c4da158e)]
 interface nsIDOMNSHTMLInputElement : nsISupports
 {
+           attribute boolean          autofocus;
 	readonly attribute nsIControllers   controllers;
 	
 	readonly attribute long             textLength;
 	
            attribute long             selectionStart;
            attribute long             selectionEnd;
 
 	readonly attribute nsIDOMFileList   files;
--- a/dom/interfaces/html/nsIDOMNSHTMLSelectElement.idl
+++ b/dom/interfaces/html/nsIDOMNSHTMLSelectElement.idl
@@ -34,14 +34,15 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
-[scriptable, uuid(a6cf9105-15b3-11d2-932e-00805f8add32)]
+[scriptable, uuid(3212788d-fa20-44c4-b945-bd70bdc23ddf)]
 interface nsIDOMNSHTMLSelectElement : nsISupports
 {
+  attribute boolean         autofocus;
   nsIDOMNode                item(in unsigned long index);
   nsIDOMNode                namedItem(in DOMString name);
 };
--- a/dom/interfaces/html/nsIDOMNSHTMLTextAreaElement.idl
+++ b/dom/interfaces/html/nsIDOMNSHTMLTextAreaElement.idl
@@ -36,19 +36,20 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
 interface nsIControllers;
 
-[scriptable, uuid(dd9f7df2-c06c-4c2d-b33a-90124e5ddea1)]
+[scriptable, uuid(ca626eac-924b-49e5-9170-39e69e11000a)]
 interface nsIDOMNSHTMLTextAreaElement : nsISupports
 {
+           attribute boolean          autofocus;
   readonly attribute nsIControllers   controllers;
 
   readonly attribute long    textLength;
   attribute long             selectionStart;
   attribute long             selectionEnd;
   attribute long             maxLength;
   attribute DOMString        placeholder;
 
--- a/dom/tests/browser/Makefile.in
+++ b/dom/tests/browser/Makefile.in
@@ -41,11 +41,13 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= dom/tests/browser
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
 		browser_focus_steal_from_chrome.js \
+		browser_autofocus_background.js \
+		$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_autofocus_background.js
@@ -0,0 +1,64 @@
+function test() {
+  waitForExplicitFinish();
+
+  let fm = Components.classes["@mozilla.org/focus-manager;1"]
+                     .getService(Components.interfaces.nsIFocusManager);
+
+  let tabs = [ gBrowser.selectedTab, gBrowser.addTab(), gBrowser.addTab() ];
+
+  // The first tab has an autofocused element.
+  // The second tab is exactly like the first one without the autofocus.
+  let testingList = [
+    { uri: "data:text/html,<!DOCTYPE html><html><body><input autofocus id='target'></body></html>",
+      tagName: "INPUT"},
+    { uri: "data:text/html,<!DOCTYPE html><html><body><input id='target'></body></html>",
+      tagName: "BODY"}
+  ];
+
+  function runTest() {
+    // Set the focus to the first tab.
+    tabs[0].linkedBrowser.focus();
+
+    // Load the first tab on background.
+    tabs[1].linkedBrowser.addEventListener("load", onLoadBackgroundFirstTab, true);
+    tabs[1].linkedBrowser.loadURI(testingList[0].uri);
+  }
+
+  function onLoadBackgroundFirstTab() {
+    tabs[1].linkedBrowser.removeEventListener("load", onLoadBackgroundFirstTab, true);
+
+    // Now load the second tab on background.
+    tabs[2].linkedBrowser.addEventListener("load", onLoadBackgroundSecondTab, true);
+    tabs[2].linkedBrowser.loadURI(testingList[1].uri);
+  }
+
+  function onLoadBackgroundSecondTab() {
+    tabs[2].linkedBrowser.removeEventListener("load", onLoadBackgroundSecondTab, true);
+
+    // Wait a second to be sure all focus events are done before launching tests.
+    setTimeout(doTest, 1000);
+  }
+
+  function doTest() {
+    for (var i=0; i<testingList.length; ++i) {
+      // Get active element in the tab.
+      var e = tabs[i+1].linkedBrowser.contentDocument.activeElement;
+
+      is(e.tagName, testingList[i].tagName,
+         "The background tab's focused element should be " +
+         testingList[i].tagName);
+      isnot(fm.focusedElement, e,
+            "The background tab's focused element should not be the focus " +
+            "manager focused element");
+    }
+
+    // Cleaning up.
+    gBrowser.addTab();
+    for (let i = 0; i < tabs.length; i++) {
+      gBrowser.removeTab(tabs[i]);
+    }
+    finish();
+  }
+
+  runTest();
+}
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -112,16 +112,19 @@ pref("browser.display.auto_quality_min_f
 pref("browser.anchor_color",                "#0000EE");
 pref("browser.active_color",                "#EE0000");
 pref("browser.visited_color",               "#551A8B");
 pref("browser.underline_anchors",           true);
 pref("browser.blink_allowed",               true);
 pref("browser.enable_automatic_image_resizing", false);
 pref("browser.enable_click_image_resizing", true);
 
+// See http://dev.w3.org/html5/spec/forms.html#attr-fe-autofocus
+pref("browser.autofocus", true);
+
 // See http://whatwg.org/specs/web-apps/current-work/#ping
 pref("browser.send_pings", false);
 pref("browser.send_pings.max_per_link", 1);           // limit the number of pings that are sent per link click
 pref("browser.send_pings.require_same_host", false);  // only send pings to the same host if this is true
 
 pref("browser.display.use_focus_colors",    false);
 pref("browser.display.focus_background_color", "#117722");
 pref("browser.display.focus_text_color",     "#ffffff");