Last Comment Bug 546995 - Implement autofocus attribute
: Implement autofocus attribute
Status: RESOLVED FIXED
: dev-doc-complete, html5
Product: Core
Classification: Components
Component: Layout: Form Controls (show other bugs)
: unspecified
: All All
: -- enhancement with 2 votes (vote)
: mozilla1.9.3a5
Assigned To: Mounir Lamouri (:mounir)
:
: Jet Villegas (:jet)
Mentors:
http://dev.w3.org/html5/spec/forms.ht...
Depends on: 1209802 1238388 563669 566983 567098 569399 597887 601030 601031
Blocks: html5forms 566348
  Show dependency treegraph
 
Reported: 2010-02-18 09:02 PST by Mounir Lamouri (:mounir)
Modified: 2016-01-10 12:55 PST (History)
15 users (show)
dao+bmo: in‑testsuite+
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---


Attachments
Test v0.1 (5.24 KB, patch)
2010-03-26 04:32 PDT, Mounir Lamouri (:mounir)
no flags Details | Diff | Splinter Review
Patch v0.1 (14.86 KB, patch)
2010-03-26 04:32 PDT, Mounir Lamouri (:mounir)
no flags Details | Diff | Splinter Review
Patch v0.1.1 (15.64 KB, patch)
2010-03-26 11:02 PDT, Mounir Lamouri (:mounir)
no flags Details | Diff | Splinter Review
Tests v2 (4.93 KB, patch)
2010-04-02 12:56 PDT, Mounir Lamouri (:mounir)
jonas: review-
Details | Diff | Splinter Review
Patch v2 (15.31 KB, patch)
2010-04-02 12:58 PDT, Mounir Lamouri (:mounir)
no flags Details | Diff | Splinter Review
Tests v3 (7.90 KB, patch)
2010-04-17 15:59 PDT, Mounir Lamouri (:mounir)
jonas: review-
Details | Diff | Splinter Review
Patch v3 (19.53 KB, patch)
2010-04-17 16:02 PDT, Mounir Lamouri (:mounir)
jonas: review-
Details | Diff | Splinter Review
Tests v4 (7.90 KB, patch)
2010-04-24 14:48 PDT, Mounir Lamouri (:mounir)
jonas: review+
Details | Diff | Splinter Review
Patch v4 (19.51 KB, patch)
2010-04-24 14:49 PDT, Mounir Lamouri (:mounir)
no flags Details | Diff | Splinter Review
Tests v4.1 (7.95 KB, patch)
2010-04-27 07:04 PDT, Mounir Lamouri (:mounir)
no flags Details | Diff | Splinter Review
Patch v4.1 (17.18 KB, patch)
2010-04-27 07:05 PDT, Mounir Lamouri (:mounir)
jonas: review+
bugs: superreview-
Details | Diff | Splinter Review
Tests v5 (14.45 KB, patch)
2010-05-05 15:57 PDT, Mounir Lamouri (:mounir)
jonas: review+
Details | Diff | Splinter Review
Patch v5 (17.45 KB, patch)
2010-05-05 16:00 PDT, Mounir Lamouri (:mounir)
bugs: superreview+
Details | Diff | Splinter Review
Tests v5.1 (14.28 KB, patch)
2010-05-17 07:02 PDT, Mounir Lamouri (:mounir)
no flags Details | Diff | Splinter Review
Patch v5.1 (17.20 KB, patch)
2010-05-17 07:03 PDT, Mounir Lamouri (:mounir)
no flags Details | Diff | Splinter Review

Description Mounir Lamouri (:mounir) 2010-02-18 09:02:54 PST
User-Agent:       Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.6) Gecko/20100114 Gentoo Firefox/3.5.6
Build Identifier: 

We should implement autofocus attribute from HTML5 Forms specification.
This attribute aims to have an element focused when the page is loaded.
This attribute applies to input, button, select, textearea and keygen elements at the moment.

For more information, look at $URL

Reproducible: Always
Comment 1 Mounir Lamouri (:mounir) 2010-03-26 04:32:28 PDT
Created attachment 435139 [details] [diff] [review]
Test v0.1
Comment 2 Mounir Lamouri (:mounir) 2010-03-26 04:32:59 PDT
Created attachment 435140 [details] [diff] [review]
Patch v0.1
Comment 3 Mounir Lamouri (:mounir) 2010-03-26 04:48:17 PDT
There is a check to not autofocus if something is already focused in the document (this is optional in the specs). However, if the user is only reading the document, the page may scroll to the focused element which could be annoying for some users. I think it could be appreciated to add an option in about:config to disable the autofocus feature. I will open a follow-up if you agree.
Comment 4 :Ehsan Akhgari 2010-03-26 09:44:42 PDT
Comment on attachment 435140 [details] [diff] [review]
Patch v0.1

Please change the interface ID for all the interfaces which you're modifying.
Comment 5 :Ehsan Akhgari 2010-03-26 09:53:27 PDT
Also, you should handle the keygen element.
Comment 6 Mounir Lamouri (:mounir) 2010-03-26 10:17:08 PDT
Thanks for you feedbacks Ehsan. I will fix the IID (I always forget to update them!).
For the keygen element, as I've never see any interface for it, I do not even understand why it is available. I will check deeply but at the moment, I can't add the autofocus attribute.
Comment 7 Mounir Lamouri (:mounir) 2010-03-26 11:02:50 PDT
Created attachment 435221 [details] [diff] [review]
Patch v0.1.1

This patch is updating UUID's.
Comment 8 :Ehsan Akhgari 2010-03-26 12:47:37 PDT
(In reply to comment #6)
> Thanks for you feedbacks Ehsan. I will fix the IID (I always forget to update
> them!).
> For the keygen element, as I've never see any interface for it, I do not even
> understand why it is available. I will check deeply but at the moment, I can't
> add the autofocus attribute.

I _think_ that the parser creates a span content node for keygen elements, though I'm not sure.  Maybe Henri knows?

Anyway, the HTML5 spec clearly states that autofocus should also work in keygen elements, so this patch at its current status is incomplete.
Comment 9 Henri Sivonen (:hsivonen) 2010-03-29 00:41:47 PDT
Gecko has a bogus <keygen> implementation. There isn't a proper element node class or interface for it. Instead, the old parser treats <keygen> as a parser macro that expands to a <select> element with a special marker attribute and generated children.

Contrary to the HTML5 spec, the HTML5 parser currently implements a parser macro, too, by cloning the exact keygen behavior of the old parser (including bugs). The plan is to have a real keygen element eventually (bug 101019).

Keygen is available, because its functionality is necessary for certificate enrollment workflows (unless the browser implements Microsoft's enrollment API and also VBScript, etc.).
Comment 10 Mounir Lamouri (:mounir) 2010-04-02 04:01:58 PDT
Some interesting test cases: http://lachy.id.au/dev/markup/tests/html5/autofocus/

This patch is passing most of them.
The reason of the failures are:
- a test is doing element.autofocus=true; and is testing if element.getAttribute('autofocus') is equal to 'autofocus' which is not the case. AFAIK, a bool attribute is true as long as the attribute is defined so...
- some tests are assuming where there are two elements with the autofocus attribute, the last one should be focused. The specs say only one element should have the autofocus attribute in a document so that mean, in my understanding, we can do whatever we want in the case there are more than one element. As I decided to not autofocus an element when an element already have the focus in the document for the reasons mentioned in comment 3, tests assuming that are not passing.
Comment 11 Mounir Lamouri (:mounir) 2010-04-02 04:04:21 PDT
By the way, I think we could create a follow-up for the keygen element because I do not think having a working-as-expected keygen element is going to be trivial.
Comment 12 Anne (:annevk) 2010-04-02 09:23:59 PDT
FYI, if you set a boolean IDL attribute to true its corresponding boolean content attribute value is to be set to the attribute name, so that test is correct. Furthermore, that the last element with autofocus specified is to be focused follows from the requirements in the specification as well. You are looking at author conformance requirements but they have no impact on what implementations are required to do.
Comment 13 Mounir Lamouri (:mounir) 2010-04-02 09:58:42 PDT
For the boolean IDL attribute / content attribute relation, can you provide a link ? By the way, it looks like last opera version is now filing the content attribute with 'true'. (AFAIK, those tests come from Opera)

For the second point, in my opinion, two sentences are more or less opposite:
"There must not be more than one element in the document with the autofocus attribute specified." and "Whenever an element with the autofocus attribute specified is inserted into a document, the user agent should queue a task that checks to see if the element is focusable, and if so, runs the focusing steps for that element."
The first one makes me think the UA can behave as it wants when there is more than one autofocus attribute specified and the second let me think the last one should be focused. As I am not experienced enough if W3C specifications I may have misunderstand that. Anne, if you can tell me more about how those sentence should be understood, it would be great.
Comment 14 Anne (:annevk) 2010-04-02 10:29:27 PDT
http://www.whatwg.org/html#reflecting-content-attributes-in-idl-attributes (I guess Opera may have a regression, I didn't check.)

For the second point. Of those two statement the first places a requirement on authors. By virtue of it being a requirement on authors it does not apply to implementors and implementors should not try to extract meaning out of it. The second requirement clearly places a requirement on implementors which is what we should follow.

E.g. consider that it is forbidden for authors to have <bogus> in their documents. That does not imply <bogus> will not end up in the document if it is included. It in fact implies nothing at all about what implementors have to do.
Comment 15 Aryeh Gregor (:ayg) (next working March 28-April 26) 2010-04-02 10:31:19 PDT
(mid-air collision)

(In reply to comment #13)
> For the boolean IDL attribute / content attribute relation, can you provide a
> link ? By the way, it looks like last opera version is now filing the content
> attribute with 'true'. (AFAIK, those tests come from Opera)

"If a reflecting IDL attribute is a boolean attribute, then on getting the IDL attribute must return true if the content attribute is set, and false if it is absent. On setting, the content attribute must be removed if the IDL attribute is set to false, and must be set to have the same value as its name if the IDL attribute is set to true."
http://www.whatwg.org/specs/web-apps/current-work/multipage/urls.html#reflecting-content-attributes-in-idl-attributes

> For the second point, in my opinion, two sentences are more or less opposite:
> "There must not be more than one element in the document with the autofocus
> attribute specified." and "Whenever an element with the autofocus attribute
> specified is inserted into a document, the user agent should queue a task that
> checks to see if the element is focusable, and if so, runs the focusing steps
> for that element."
> The first one makes me think the UA can behave as it wants when there is more
> than one autofocus attribute specified and the second let me think the last one
> should be focused. As I am not experienced enough if W3C specifications I may
> have misunderstand that. Anne, if you can tell me more about how those sentence
> should be understood, it would be great.

HTML5 is written differently from some other specs.  Errors are always handled in a well-defined manner, because experience has shown that users serve pages with errors no matter what, and browsers have to reverse-engineer each other's error-handling behavior to ensure interoperability in practice if it's not specced.  You might want to refer to the section on conformance requirements:

"Note: There is no implied relationship between document conformance requirements and implementation conformance requirements. User agents are not free to handle non-conformant documents as they please; the processing model described in this specification applies to implementations regardless of the conformity of the input documents."
http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#conformance-requirements
Comment 16 Mounir Lamouri (:mounir) 2010-04-02 10:41:57 PDT
I've open bug 556805 about the boolean IDL/content attribute issue.
For the other issue, I will provide a new patch fixing that.

Thank you Anne and Aryeh for your help !
Comment 17 Mounir Lamouri (:mounir) 2010-04-02 12:56:06 PDT
Created attachment 436747 [details] [diff] [review]
Tests v2
Comment 18 Mounir Lamouri (:mounir) 2010-04-02 12:58:33 PDT
Created attachment 436748 [details] [diff] [review]
Patch v2

This patch pass all tests from the source in comment 10 except the one linked to bug 556805.

I see two follow-ups:
 - an option in about:config to disable autofocus
 - do not autofocus when the user is already doing something (like typing/focus on something else, ...)
Comment 19 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-14 21:37:07 PDT
I think I'd prefer to not wait for a followup to add the pref. It should be simple to implement a pref if you use nsContentUtils::AddBoolPrefVarCache.

As for the second issue. Maybe you could at least check if another element in the document currently has focus and only honor the attribute if that is not the case.

Also, this patch only allow forms controls to be autofocused. The spec says that any focusable element should honor it.

Finally, would it make sense to only honor the attribute while the document is being loaded? It seems likely to me that people will move around form controls in the DOM and thus accidentally re-insert an element with autofocus set. Though maybe this won't be much of a problem if we implement the idea in the second paragraph above.
Comment 20 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-14 21:38:33 PDT
Comment on attachment 436747 [details] [diff] [review]
Tests v2

Unsetting review request based on previous comment.

Btw, is there a reason not to combine the test and code changes into a single patch?
Comment 21 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-14 21:39:01 PDT
Comment on attachment 436748 [details] [diff] [review]
Patch v2

Oh, and feel free to rerequest if you disagree with all my comments :)
Comment 22 Mounir Lamouri (:mounir) 2010-04-15 03:47:19 PDT
(In reply to comment #19)
> I think I'd prefer to not wait for a followup to add the pref. It should be
> simple to implement a pref if you use nsContentUtils::AddBoolPrefVarCache.

Ok.

> As for the second issue. Maybe you could at least check if another element in
> the document currently has focus and only honor the attribute if that is not
> the case.

My first patch was doing that but it is conflicting with one part of the spec saying when an element with autofocus is inserted into the document, it has to be focused. If two elements have 'autofocus', the second one should be focused.
The only solution I see is to add an information to the focus manager so I can check if the focused element has been focused with autofocus or by user input.
I don't think that's a good idea to autofocus an element when another element has been previously autofocused.

> Also, this patch only allow forms controls to be autofocused. The spec says
> that any focusable element should honor it.

$URL is about "Autofocusing a form control" I don't see where it says it should apply to all focusable element.

> Finally, would it make sense to only honor the attribute while the document is
> being loaded? It seems likely to me that people will move around form controls
> in the DOM and thus accidentally re-insert an element with autofocus set.
> Though maybe this won't be much of a problem if we implement the idea in the
> second paragraph above.

I don't think we can prevent that except by preventing autofocus when it sounds undesirable. When an element already has the focus with something else than autofocus sounds the only reasonable way to check that.

Maybe two things we could ask to be added to the specs:
- only autofocus the first element with the autofocus attribute so we can't have the focus moving through the page with autofocus. For the webpage author, having more than one element with autofocus is forbidden (so that's not changing anything for them) and for the user it may be more convenient ;
- do not autofocus after the page is loaded. It sounds out of the use of autofocus and .focus js function should be used instead.

What is your opinion ?

(In reply to comment #20)
> (From update of attachment 436747 [details] [diff] [review])
> Unsetting review request based on previous comment.
> 
> Btw, is there a reason not to combine the test and code changes into a single
> patch?

No reason. I only do that for readability. I'm writing tests then code but I can attach them in the same patch if you think it's more convenient.
Comment 23 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-15 12:49:32 PDT
> > As for the second issue. Maybe you could at least check if another element
> > in the document currently has focus and only honor the attribute if that is
> > not the case.
> 
> My first patch was doing that but it is conflicting with one part of the spec
> saying when an element with autofocus is inserted into the document, it has to
> be focused. If two elements have 'autofocus', the second one should be
> focused. The only solution I see is to add an information to the focus
> manager so I can check if the focused element has been focused with autofocus
> or by user input.

Yeah, that seems like a very bad thing. Plus you still wouldn't catch the case when one form control has been focused using an autofocus attribute and then the user starts typing in that control. While the user is typing another control is inserted in the DOM (say, because we parse more of the page). At this point it would be really annoying if the new form control steals focus in the middle of the user typing.

I think the most sensible solution here is to ask the spec to be changed such that the first autofocus'ed element is the one that receives focus. I really can't see any other good solution?

Can you email whatwg/htmlwg, or do you want me to?

> > Also, this patch only allow forms controls to be autofocused. The spec says
> > that any focusable element should honor it.
> 
> $URL is about "Autofocusing a form control" I don't see where it says it
> should apply to all focusable element.

I'm not sure that the title can be considered as part of the normative requirements. The normative requirements say:

"Whenever an element with the autofocus attribute specified is inserted into a document whose browsing context did not have the sandboxed automatic features browsing context flag set when the Document was created, the user agent should queue a task that checks to see if the element is focusable, and if so, runs the focusing steps for that element."

So no restrictions there on that this only applies to form controls. However since the autofocus attribute is only specified on form controls I think the intent was that this should only happen for form controls. So I think you're correct here.

However it would be great to get this clarified in the spec. For example by saying that this only happens on elements where the autofocus attribute is defined.

> > Finally, would it make sense to only honor the attribute while the document
> > is being loaded? It seems likely to me that people will move around form
> > controls in the DOM and thus accidentally re-insert an element with
> > autofocus set. Though maybe this won't be much of a problem if we implement
> > the idea in the second paragraph above.
> 
> I don't think we can prevent that except by preventing autofocus when it
> sounds undesirable. When an element already has the focus with something else
> than autofocus sounds the only reasonable way to check that.


> Maybe two things we could ask to be added to the specs:
> - only autofocus the first element with the autofocus attribute so we can't
> have the focus moving through the page with autofocus. For the webpage author,
> having more than one element with autofocus is forbidden (so that's not
> changing anything for them) and for the user it may be more convenient ;
> - do not autofocus after the page is loaded. It sounds out of the use of
> autofocus and .focus js function should be used instead.
> 
> What is your opinion ?

Agreed on both accounts :)

It's debatable if autofocus should be "disabled" after the 'load' event has fired, or after the 'DOMContentLoaded' event has fired. I'm fine either way. And either is trivial to implement by looking at the documents readyState (nsIDocument::GetReadyStateEnum)

> (In reply to comment #20)
> > (From update of attachment 436747 [details] [diff] [review] [details])
> > Unsetting review request based on previous comment.
> > 
> > Btw, is there a reason not to combine the test and code changes into a single
> > patch?
> 
> No reason. I only do that for readability. I'm writing tests then code but I
> can attach them in the same patch if you think it's more convenient.

Ah, makes sense. Since they're written separately I'll treat them separately. In fact, I'll go review the tests right away :)
Comment 24 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-15 13:12:09 PDT
Comment on attachment 436747 [details] [diff] [review]
Tests v2

>+++ b/content/html/content/test/test_bug546995-2.html	Fri Apr 02 21:52:05 2010 +0200
>+function runTests()
>+{
>+  is(gFocusHandler, 1, "Input element should have been focused");
>+
>+  addElementWithAutofocus('button', true);
>+  setTimeout("addElementWithAutofocus('select')", 100);
>+  setTimeout("addElementWithAutofocus('textarea')", 200);
>+
>+  setTimeout("is(gFocusHandler, 4, 'All elements should have been focused')",
>+             300);

Unfortunately this is no good. Waiting for X milliseconds to see that the focus event has fired won't work reliably enough. Our mochitest servers are notorious for randomly taking forever to perform a specific action, causing tests like this to randomly go orange.

Instead what you should do is to wait until the focus event fires, and only then run the next test.

(I've found it really neat to use generators to run asynchronous tests. For an example, see here:
http://mxr.mozilla.org/mozilla-central/source/content/base/test/test_bug544642.html?force=1
Just make sure to set the script type to "application/javascript;version=1.8". Definitely feel free to not use this pattern though.)

>+  // If we try to autofocus when an element is already focused,
>+  // the inserted element should be focused.
>+  setTimeout("addElementWithAutofocus('input')", 400);
>+  setTimeout(
>+    "is(gFocusHandler, 5, 'The inserted element should have been focused')",
>+    500);

If we are able to go through with the spec changes, then this test would need to be changed obviously.

>+  // If we try to autofocus an element which does not allow focus,
>+  // nothing should happen.
>+  setTimeout("addNotFocusableElementWithAutofocus()", 600);
>+  setTimeout("is(gFocusHandler, 5, 'No element should have been focused')", 700);

Unfortunately this is tricky. Here you obviously can't wait for the focus event to fire, as it shouldn't fire. I can't really think of any better solution than essentially doing what you are doing here. Wait X milliseconds and then check if the event fired. The upshot is that if we start failing this test, we'll correctly fail most of the time, and only occasionally erroneously pass the test.

Let me know if that makes sense? :)

Minusing based on the timeout for when the focus is expected to fire as that will cause random oranges.
Comment 25 Mounir Lamouri (:mounir) 2010-04-15 15:52:45 PDT
The propositions have been sent to the whatwg mailing list.

For the tests and patch, I will update them after getting some return to see how that's moving. I will probably increase the timeout value for the no-focus tests to minimize chances to get an erroneous pass.
Comment 26 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-15 16:10:56 PDT
Sounds great! Thanks for your work!
Comment 28 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-17 08:51:24 PDT
For what it's worth, it seemed like there was enough agreement on the whatwg list that I think we can go ahead and implement the proposal. This way we also gather implementation experience with our proposed behavior. I don't think we need to wait for an official response from the editor.

Worst case we can always change the code if it ultimately gets rejected.
Comment 29 Mounir Lamouri (:mounir) 2010-04-17 15:59:09 PDT
Created attachment 439731 [details] [diff] [review]
Tests v3

This is not testing the behavior for all available elements because it would need a test file for each element and that will probably too much verbose.
If you think every elements should be tested, let me know.
Comment 30 Mounir Lamouri (:mounir) 2010-04-17 16:02:30 PDT
Created attachment 439733 [details] [diff] [review]
Patch v3

This patch is including the proposed changes to the specifications.
I'm using a virtual |AcceptAutofocus| method which has to be re-defined when an element accepts autofocus. That's a personal taste but I've see most similar functions (like |CanBeDisabled|) are not virtual.
If you think I should use the |CanBeDisabled| approach, I can.
Comment 31 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-20 12:14:02 PDT
Comment on attachment 439731 [details] [diff] [review]
Tests v3

>diff -r d5a2312e96a0 content/html/content/test/test_bug546995-2.html
>--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
>+++ b/content/html/content/test/test_bug546995-2.html	Sun Apr 18 00:57:05 2010 +0200
>@@ -0,0 +1,68 @@
>+<!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 onload="window.setTimeout(runTests, 0);">

This is still relying on ordering of various events where the order is essentially undefined. You're basically hoping that by the time that this timeout has run we've fired all the needed focus events for the various elements.

Instead make the focus event handler for the element that should receive focus start a timeout that is long enough to test that no other focus events fire.

>+function onFocusHandler()
>+{
>+  focus4 = true;
>+}
...
>+  var e = document.createElement('input');
>+  e.autofocus = true;
>+  e.onfocus = onFocusHandler;
>+  c.appendChild(e);

Would probably be easier to follow to simply do:

e.onfocus = function() {
  focus4 = true;
};

and get rid of the 'onFocusHandler' function.

>+  setTimeout("ok(!focus4, \"After loading, elements can't be autofocused\")",
>+             500);
>+
>+  setTimeout(SimpleTest.finish, 600);

This is also a bad idea. You're relying on that the first timer fires before the second one. Simply put the call to SimpleTest.finish() into the first timeout.

A lot of these comments apply to other tests in this patch too.

Hope this makes sense? Feel free to ping me on irc if you have questions btw. I know writing reliable tests in the face of our unreliable mochitest infrastructure is a pain.
Comment 32 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-20 12:23:19 PDT
Comment on attachment 439733 [details] [diff] [review]
Patch v3

>+class nsAutoFocusEvent : public nsRunnable {
>+public:
>+  nsAutoFocusEvent(nsGenericHTMLFormElement* aElement) : mElement(aElement) {}
>+
>+  NS_IMETHOD Run() {
>+    nsFocusManager* fm = nsFocusManager::GetFocusManager();
>+    if (!fm) {
>+      return NS_ERROR_NULL_POINTER;
>+    }
>+
>+    if (fm->sAutofocusEnabled) {
>+      return NS_OK;
>+    }
>+
>+    nsIDocument* document = mElement->GetDocument();
>+
>+    // Do not autofocus an element if the document is loaded.
>+    if (document->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE) {
>+      return NS_OK;
>+    }

It would probably be better to put the readystate test before we schedule the runnable. As it is, you're running the risk that we'll finish parsing the document between scheduling the runnable and it actually running.

>+    // If something is focused in the same document, ignore autofocus.
>+    if (!fm->GetFocusedContent() ||
>+        fm->GetFocusedContent()->GetDocument() != document) {
>+      return mElement->Focus();
>+    }

Keep this test here though. This is looking good.

However GetDocument() is deprecated. Use GetOwnerDoc(). See documentation in nsINode.h. Same thing with the call to mElement->GetDocument() above.

>@@ -2397,26 +2439,34 @@ nsGenericHTMLFrameElement::IsHTMLFocusab
>   *aIsFocusable = isFocusable;
>   if (!isFocusable && aTabIndex) {
>     *aTabIndex = -1;
>   }
> 
>   return PR_FALSE;
> }
> 
>+
> nsresult

Nit: This change seems unnecessary.

Looking good otherwise! Minusing since I'd like to see the above things fixed though.

Also I would like to check with Neil Deakin if the changes to the focus manager are ok, or if he would prefer to keep that code in nsGenericHTMLElement or some such.
Comment 33 Neil Deakin 2010-04-20 12:52:34 PDT
> Also I would like to check with Neil Deakin if the changes to the focus manager
> are ok, or if he would prefer to keep that code in nsGenericHTMLElement or some
> such.

Adding to nsGenericHTMLElement or similar would seem better, although I'm fine with it being in nsFocusManager if there's no suitable initialization point.


> +  ok(!focus1, "First input element should not have been focused");
> +  ok(focus2, "Second input element should be focused");
> +  ok(!focus3, "First textarea should not have been focused");

It doesn't seem like 'autofocus' relies on the element actually being frontmost or firing events. After loading, document.activeElement should be set to 'focus2' here I would think, which avoids having to wait for the events. All that needs to happen is to ensure that runTests runs after the autofocus handling.

+  for (var i=0; i<c.childNodes.length; i++)
+  {
+    if (c.childNodes[i] instanceof HTMLElement) {
+      c.childNodes[i].blur();
+    }
+  }

document.activeElement.blur() will accomplish this.
Comment 34 Mounir Lamouri (:mounir) 2010-04-20 16:28:26 PDT
(In reply to comment #33)
> > Also I would like to check with Neil Deakin if the changes to the focus manager
> > are ok, or if he would prefer to keep that code in nsGenericHTMLElement or some
> > such.
> 
> Adding to nsGenericHTMLElement or similar would seem better, although I'm fine
> with it being in nsFocusManager if there's no suitable initialization point.

Actually, I do not see a good initialization point in nsGenericHTMLElement. We /may/ found one but the initialization of a singleton is probably a good one. In addition, I think if someone is looking for the init point of the parameter, the focus manager will come in mind before nsGenericHTMLElement.

> > +  ok(!focus1, "First input element should not have been focused");
> > +  ok(focus2, "Second input element should be focused");
> > +  ok(!focus3, "First textarea should not have been focused");
> 
> It doesn't seem like 'autofocus' relies on the element actually being frontmost
> or firing events. After loading, document.activeElement should be set to
> 'focus2' here I would think, which avoids having to wait for the events. All
> that needs to happen is to ensure that runTests runs after the autofocus
> handling.
> 
> +  for (var i=0; i<c.childNodes.length; i++)
> +  {
> +    if (c.childNodes[i] instanceof HTMLElement) {
> +      c.childNodes[i].blur();
> +    }
> +  }
> 
> document.activeElement.blur() will accomplish this.

Thanks for this hint ;)

I will update tests and patches tomorrow.
Comment 35 Mounir Lamouri (:mounir) 2010-04-24 14:48:37 PDT
Created attachment 441308 [details] [diff] [review]
Tests v4
Comment 36 Mounir Lamouri (:mounir) 2010-04-24 14:49:58 PDT
Created attachment 441309 [details] [diff] [review]
Patch v4
Comment 37 Mounir Lamouri (:mounir) 2010-04-24 14:51:13 PDT
Comment on attachment 441309 [details] [diff] [review]
Patch v4

Neil, could you review this patch to confirm the change in |nsFocusManager| is okay ?
Comment 38 Neil Deakin 2010-04-26 13:55:03 PDT
(In reply to comment #34)
> Actually, I do not see a good initialization point in nsGenericHTMLElement. We
> /may/ found one but the initialization of a singleton is probably a good one.
> In addition, I think if someone is looking for the init point of the parameter,
> the focus manager will come in mind before nsGenericHTMLElement.

Since it's only applies to html elements and is only used in the html element code it makes more sense to just be a static in nsGenericHTMLElement.

Why do you even need to cache the preference value at all? This patch is getting the value at startup, but it would make more sense to cache it only when used, since this wouldn't be a heavily used feature.
Comment 39 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-26 14:44:07 PDT
I guess I don't care super much if we cache the pref value or not.
Comment 40 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-04-26 17:43:24 PDT
Comment on attachment 441308 [details] [diff] [review]
Tests v4

>diff -r 6b15adef4222 content/html/content/test/test_bug546995-2.html
...
>+/** 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 = Array("input", "textarea", "select", "button");

Nit: You could also do:

var elements = ["input", "textarea", ... ];

>+  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();
>+  ok(document.activeElement instanceof HTMLBodyElement, "foo");

The "foo" description isn't very helpful ;)

You could also do:
is (document.activeElement, document.body, ...);

>+  // 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("ok(document.activeElement instanceof HTMLBodyElement,\
>+             \"After loading, elements can't be autofocused\");\
>+             SimpleTest.finish();", 1000);
>+  yield;

It'd be more readable to do:

  setTimeout(function() {
    ok(document.activeElement instanceof HTMLBodyElement,
       "After loading, elements can't be autofocused");
    SimpleTest.finish();
  }, 1000);
  yield;

Or even:

  setTimeout(function() { gGen.next(); }, 1000);
  yield;

  ok(document.activeElement instanceof HTMLBodyElement,
     "After loading, elements can't be autofocused");
  SimpleTest.finish();
  yield;
Comment 41 Mounir Lamouri (:mounir) 2010-04-27 07:04:29 PDT
Created attachment 441792 [details] [diff] [review]
Tests v4.1
Comment 42 Mounir Lamouri (:mounir) 2010-04-27 07:05:54 PDT
Created attachment 441793 [details] [diff] [review]
Patch v4.1

I've removed all the code from |nsFocusManager| and I'm no longer caching the pref value.
Thank you for your comments Neil.
Comment 43 Olli Pettay [:smaug] 2010-05-03 08:10:47 PDT
Comment on attachment 441793 [details] [diff] [review]
Patch v4.1

>+class nsAutoFocusEvent : public nsRunnable {
{ should be in the next line.

>+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;
>+    }
>+
>+    // If something is focused in the same document, ignore autofocus.
>+    if (!fm->GetFocusedContent() ||
>+        fm->GetFocusedContent()->GetOwnerDoc() != document) {
>+      return mElement->Focus();
>+    }
So how does this all work when the page containing autofocus
is loaded in a background tab? I believe when bringing that
tab to front, focus should be set to the element
which has autofocus.


>@@ -2367,16 +2403,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);
>+  }
>+




>+++ b/content/html/content/src/nsHTMLButtonElement.cpp	Tue Apr 27 15:58:50 2010 +0200
>@@ -106,16 +106,18 @@ public:
...
> protected:
>+  PRBool AcceptAutofocus() const
>+  {
>+    return PR_TRUE;
>+  }

So in nsGenericHTMLElement.cpp this is public and virtual.
Same problem also elsewhere.

sr-, waiting for answer to the background tab problem.
Comment 44 Olli Pettay [:smaug] 2010-05-03 09:05:55 PDT
So, please test how/if autofocus works in background tab.

And another thing; how should autofocus work if
some subdocument is focused before autofocus runs?

I mean, what if the page has <iframe> which has <input> and use focuses that.
Comment 45 :Ehsan Akhgari 2010-05-03 09:25:45 PDT
(In reply to comment #44)
> So, please test how/if autofocus works in background tab.

I think we should also have an automated test for that behavior.
Comment 46 Olli Pettay [:smaug] 2010-05-03 09:37:45 PDT
(In reply to comment #44)
> I mean, what if the page has <iframe> which has <input> and use focuses that.
s/use/user/

And yes, automated tests would be great for this all.
Comment 47 Mounir Lamouri (:mounir) 2010-05-05 03:50:30 PDT
I did the change to prevent focusing a parent window when the focus is in a sub-window and the tests are working.
However, I just can't write a mochitest to test if autofocus is working correctly when it is used in a backgrounded window. Indeed, I can't focus the caller window after window.open(). The best I can do is to have the caller window back in foreground but the focus is still in the popup. I tried |popupWindow.focus()| and |focusManager.focusedWindow(window)|... is there something I should try ? The only solution I see is to write a browser (or chrome ?) test.
Comment 48 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-05-05 11:28:24 PDT
Yes, a Chrome test is needed to test that. See here for how to do it:
https://developer.mozilla.org/en/Chrome_tests
Comment 49 Mounir Lamouri (:mounir) 2010-05-05 15:57:48 PDT
Created attachment 443745 [details] [diff] [review]
Tests v5

I've added the two tests for the focus in the iframe and the browser chrome test for the autofocus in the background.
Comment 50 Mounir Lamouri (:mounir) 2010-05-05 16:00:52 PDT
Created attachment 443748 [details] [diff] [review]
Patch v5

This is making the new tests pass.

Btw, I realized something like that:
<input autofocus onfocus="blur();">
/* lot of stuff */
<input autofocus>
would probably make the focus moving into the document with autofocus during the page load. I'm not sure we should make this impossible because it would be a lot of work for something not so important but we will have to make sure the specification changes we've requested are not too restrictive.
Comment 51 Mounir Lamouri (:mounir) 2010-05-17 05:36:35 PDT
Tests: r=sicking
Patch: r=sicking sr=smaug
Comment 52 Mounir Lamouri (:mounir) 2010-05-17 07:02:27 PDT
Created attachment 445703 [details] [diff] [review]
Tests v5.1

r=sicking

Updated patch against the HEAD.
Comment 53 Mounir Lamouri (:mounir) 2010-05-17 07:03:28 PDT
Created attachment 445704 [details] [diff] [review]
Patch v5.1

r=sicking sr=smaug

Updated patch against the HEAD.
Comment 54 Dão Gottwald [:dao] 2010-05-19 11:25:30 PDT
http://hg.mozilla.org/mozilla-central/rev/13ffec2c6673
Comment 55 :Ehsan Akhgari 2010-05-19 15:42:42 PDT
I really really don't like these tests waiting for 2 seconds for no good reason.  What causes us to have to wait for 2 seconds in these tests?
Comment 56 Mounir Lamouri (:mounir) 2010-05-19 17:36:15 PDT
(In reply to comment #55)
> I really really don't like these tests waiting for 2 seconds for no good
> reason.  What causes us to have to wait for 2 seconds in these tests?

Because when @autofocus is found, a focus is added to the event queue so we can't assume the focus event will be done when the page is loaded. It could be a bit later.
However, I assume you want me to had a handler for the focus event and just let the test timeout if the focus event is never sent/catch, right ?
Comment 57 Jonas Sicking (:sicking) No longer reading bugmail consistently 2010-05-19 17:43:28 PDT
Timing out if an event that should fire doesn't fire is quite ok. I thought that was what I asked for a few times.

However I thought we were also testing that an asynchronous event did *not* fire. That I don't know how to do other than simply waiting for a bit and then checking that it still hasn't fired.
Comment 58 Neil Deakin 2010-05-19 17:49:23 PDT
(In reply to comment #57)
> However I thought we were also testing that an asynchronous event did *not*
> fire. That I don't know how to do other than simply waiting for a bit and then
> checking that it still hasn't fired.

This could be done though with a very small timeout (perhaps even 0 or by using executeSoon), and iterating a few times until a few seconds have elapsed and then failing. In the usual case where the test works, this won't cause much delay. You don't need to wait for an event, you could just compare document.activeElement.
Comment 59 Mounir Lamouri (:mounir) 2010-05-19 18:09:08 PDT
(In reply to comment #57)
> Timing out if an event that should fire doesn't fire is quite ok. I thought
> that was what I asked for a few times.
> 
> However I thought we were also testing that an asynchronous event did *not*
> fire. That I don't know how to do other than simply waiting for a bit and then
> checking that it still hasn't fired.

Indeed. Sorry, it's sometimes hard to get back to some old code.

(In reply to comment #58)
> (In reply to comment #57)
> > However I thought we were also testing that an asynchronous event did *not*
> > fire. That I don't know how to do other than simply waiting for a bit and then
> > checking that it still hasn't fired.
> 
> This could be done though with a very small timeout (perhaps even 0 or by using
> executeSoon), and iterating a few times until a few seconds have elapsed and
> then failing. In the usual case where the test works, this won't cause much
> delay. You don't need to wait for an event, you could just compare
> document.activeElement.

Don't we want the exact opposite ? For example, test-4 is checking that the <input> is not focused. If we are iterating to check it is not focused, the usual case will have us do all the iterations.

By the way, I think the setTimout of test-5 could be removed and the browser test could be improved. I will open follow-ups for that.
Comment 60 Neil Deakin 2010-05-19 18:18:37 PDT
(In reply to comment #59)
> Don't we want the exact opposite ? For example, test-4 is checking that the
> <input> is not focused. If we are iterating to check it is not focused, the
> usual case will have us do all the iterations.

Hmm. I guess so then. I didn't see there was special logic when an iframe was focused. Or is that just when anything happens to already be focused?
Comment 61 Mounir Lamouri (:mounir) 2010-05-19 18:23:00 PDT
(In reply to comment #60)
> (In reply to comment #59)
> > Don't we want the exact opposite ? For example, test-4 is checking that the
> > <input> is not focused. If we are iterating to check it is not focused, the
> > usual case will have us do all the iterations.
> 
> Hmm. I guess so then. I didn't see there was special logic when an iframe was
> focused. Or is that just when anything happens to already be focused?

Indeed and the same type of behavior is checked in test-3.
Comment 62 Mounir Lamouri (:mounir) 2010-05-20 06:01:50 PDT
I've open bug 567098 to remove some |setTimeout|.
Comment 63 :Ehsan Akhgari 2010-05-20 12:04:18 PDT
So I think that for the cases where you need to make sure that an async event has executed, you can do it with one (or in some cases, two) executeSoon calls.  And like jst said before, timing out is a valid failure mode for tests, you don't need to write the tests in a way that they always fail explicitly.
Comment 64 neil@parkwaycc.co.uk 2010-05-31 03:22:05 PDT
As well as background tabs/windows, what about autofocus in sidebar documents?
Comment 65 Eric Shepherd [:sheppy] 2010-06-15 13:24:44 PDT
This is documented on the appropriate elements, and mentioned here:

https://developer.mozilla.org/en/HTML/HTML5/Forms_in_HTML5

Note You need to log in before you can comment on or make changes to this bug.