Bug 546995 - Implement autofocus attribute. r=sicking, sr=smaug

--HG--
extra : rebase_source : b0f233cae75aa9182d6587b6d66d4cbebea1126c
This commit is contained in:
Mounir Lamouri 2010-05-19 19:52:17 +02:00
Родитель 5f1e23b8db
Коммит 36397b56bd
22 изменённых файлов: 480 добавлений и 4 удалений

Просмотреть файл

@ -123,6 +123,7 @@ 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

Просмотреть файл

@ -111,6 +111,8 @@
#include "mozAutoDocUpdate.h"
#include "nsHtml5Module.h"
#include "nsThreadUtils.h"
class nsINodeInfo;
class nsIDOMNodeList;
class nsRuleWalker;
@ -169,6 +171,47 @@ nsGenericHTMLElement::Init(nsINodeInfo *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
@ -2400,6 +2443,20 @@ nsGenericHTMLFormElement::BindToTree(nsIDocument* aDocument,
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;
}

Просмотреть файл

@ -811,6 +811,14 @@ protected:
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
*/

Просмотреть файл

@ -106,6 +106,8 @@ public:
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; }
@ -139,6 +141,11 @@ public:
virtual void DoneCreatingElement();
protected:
virtual PRBool AcceptAutofocus() const
{
return PR_TRUE;
}
PRInt8 mType;
PRPackedBool mHandlingClick;
PRPackedBool mDisabledChanged;
@ -202,6 +209,7 @@ nsHTMLButtonElement::GetForm(nsIDOMHTMLFormElement** 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)

Просмотреть файл

@ -395,6 +395,11 @@ protected:
nsGkAtoms::image, eIgnoreCase);
}
virtual PRBool AcceptAutofocus() const
{
return PR_TRUE;
}
/**
* Fire the onChange event
*/
@ -787,6 +792,7 @@ 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)

Просмотреть файл

@ -1202,6 +1202,7 @@ nsHTMLSelectElement::SetValue(const nsAString& aValue)
}
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)

Просмотреть файл

@ -468,6 +468,11 @@ protected:
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. */

Просмотреть файл

@ -200,6 +200,11 @@ protected:
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
@ -362,6 +367,7 @@ nsHTMLTextAreaElement::IsHTMLFocusable(PRBool *aIsFocusable, PRInt32 *aTabIndex)
}
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)

Просмотреть файл

@ -169,6 +169,12 @@ _TEST_FILES = test_bug589.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)

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -39,9 +39,10 @@
#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();

Просмотреть файл

@ -42,9 +42,10 @@
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;

Просмотреть файл

@ -39,9 +39,10 @@
#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);
};

Просмотреть файл

@ -41,9 +41,10 @@
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;

Просмотреть файл

@ -46,6 +46,8 @@ 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)

Просмотреть файл

@ -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();
}

Просмотреть файл

@ -117,6 +117,9 @@ 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