Bug 687787: support focusin/focusout based on webkit/blink r=smaug

Blink and webkit launch focusin after focus and focusout after blur. Despite
this contradiction with the spec, it is best to mirror this new way, as there
is little guidance or existing code to clarify implementation amiguities that
can arise from the spec.

If focus/blur is fired on a window or document, or the event triggers a change
of focus, do not fire the corresponding focusin/focusout. Otherwise, always
fire the corresponding event.

Additionally, add a mochitest and a w3c-platform-test.

MozReview-Commit-ID: AgQ8JBxKIqK
This commit is contained in:
Kevin Wern 2016-10-06 21:39:53 -04:00
Родитель 41d2aeeed2
Коммит 19f18efeac
10 изменённых файлов: 913 добавлений и 0 удалений

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

@ -2021,6 +2021,49 @@ public:
nsCOMPtr<EventTarget> mRelatedTarget;
};
class FocusInOutEvent : public Runnable
{
public:
FocusInOutEvent(nsISupports* aTarget, EventMessage aEventMessage,
nsPresContext* aContext,
nsPIDOMWindowOuter* aOriginalFocusedWindow,
nsIContent* aOriginalFocusedContent,
EventTarget* aRelatedTarget)
: mTarget(aTarget)
, mContext(aContext)
, mEventMessage(aEventMessage)
, mOriginalFocusedWindow(aOriginalFocusedWindow)
, mOriginalFocusedContent(aOriginalFocusedContent)
, mRelatedTarget(aRelatedTarget)
{
}
NS_IMETHOD Run() override
{
nsCOMPtr<nsIContent> originalWindowFocus = mOriginalFocusedWindow ?
mOriginalFocusedWindow->GetFocusedNode() :
nullptr;
// Blink does not check that focus is the same after blur, but WebKit does.
// Opt to follow Blink's behavior (see bug 687787).
if (mEventMessage == eFocusOut ||
originalWindowFocus == mOriginalFocusedContent) {
InternalFocusEvent event(true, mEventMessage);
event.mFlags.mBubbles = true;
event.mFlags.mCancelable = false;
event.mRelatedTarget = mRelatedTarget;
return EventDispatcher::Dispatch(mTarget, mContext, &event);
}
return NS_OK;
}
nsCOMPtr<nsISupports> mTarget;
RefPtr<nsPresContext> mContext;
EventMessage mEventMessage;
nsCOMPtr<nsPIDOMWindowOuter> mOriginalFocusedWindow;
nsCOMPtr<nsIContent> mOriginalFocusedContent;
nsCOMPtr<EventTarget> mRelatedTarget;
};
static nsIDocument*
GetDocumentHelper(EventTarget* aTarget)
{
@ -2033,6 +2076,26 @@ GetDocumentHelper(EventTarget* aTarget)
return node->OwnerDoc();
}
void nsFocusManager::SendFocusInOrOutEvent(EventMessage aEventMessage,
nsIPresShell* aPresShell,
nsISupports* aTarget,
nsPIDOMWindowOuter* aCurrentFocusedWindow,
nsIContent* aCurrentFocusedContent,
EventTarget* aRelatedTarget)
{
NS_ASSERTION(aEventMessage == eFocusIn || aEventMessage == eFocusOut,
"Wrong event type for SendFocusInOrOutEvent");
nsContentUtils::AddScriptRunner(
new FocusInOutEvent(
aTarget,
aEventMessage,
aPresShell->GetPresContext(),
aCurrentFocusedWindow,
aCurrentFocusedContent,
aRelatedTarget));
}
void
nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage,
nsIPresShell* aPresShell,
@ -2049,6 +2112,11 @@ nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage,
nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(aTarget);
nsCOMPtr<nsIDocument> eventTargetDoc = GetDocumentHelper(eventTarget);
nsCOMPtr<nsIDocument> relatedTargetDoc = GetDocumentHelper(aRelatedTarget);
nsCOMPtr<nsPIDOMWindowOuter> currentWindow = mFocusedWindow;
nsCOMPtr<nsPIDOMWindowInner> targetWindow = do_QueryInterface(aTarget);
nsCOMPtr<nsIDocument> targetDocument = do_QueryInterface(aTarget);
nsCOMPtr<nsIContent> currentFocusedContent = currentWindow ?
currentWindow->GetFocusedNode() : nullptr;
// set aRelatedTarget to null if it's not in the same document as eventTarget
if (eventTargetDoc != relatedTargetDoc) {
@ -2099,6 +2167,19 @@ nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage,
nsContentUtils::AddScriptRunner(
new FocusBlurEvent(aTarget, aEventMessage, aPresShell->GetPresContext(),
aWindowRaised, aIsRefocus, aRelatedTarget));
// Check that the target is not a window or document before firing
// focusin/focusout. Other browsers do not fire focusin/focusout on window,
// despite being required in the spec, so follow their behavior.
//
// As for document, we should not even fire focus/blur, but until then, we
// need this check. targetDocument should be removed once bug 1228802 is
// resolved.
if (!targetWindow && !targetDocument) {
EventMessage focusInOrOutMessage = aEventMessage == eFocus ? eFocusIn : eFocusOut;
SendFocusInOrOutEvent(focusInOrOutMessage, aPresShell, aTarget,
currentWindow, currentFocusedContent, aRelatedTarget);
}
}
}

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

@ -296,6 +296,30 @@ protected:
bool aIsRefocus = false,
mozilla::dom::EventTarget* aRelatedTarget = nullptr);
/**
* Send a focusin or focusout event
*
* aEventMessage should be either eFocusIn or eFocusOut.
*
* aTarget is the content the event will fire on (the object that gained
* focus for focusin, the object blurred for focusout).
*
* aCurrentFocusedWindow is the window focused before the focus/blur event
* was fired.
*
* aCurrentFocusedContent is the content focused before the focus/blur event
* was fired.
*
* aRelatedTarget is the content related to the event (the object
* losing focus for focusin, the object getting focus for focusout).
*/
void SendFocusInOrOutEvent(mozilla::EventMessage aEventMessage,
nsIPresShell* aPresShell,
nsISupports* aTarget,
nsPIDOMWindowOuter* aCurrentFocusedWindow,
nsIContent* aCurrentFocusedContent,
mozilla::dom::EventTarget* aRelatedTarget = nullptr);
/**
* Scrolls aContent into view unless the FLAG_NOSCROLL flag is set.
*/

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

@ -794,6 +794,8 @@ GK_ATOM(onfailed, "onfailed")
GK_ATOM(onfetch, "onfetch")
GK_ATOM(onfinish, "onfinish")
GK_ATOM(onfocus, "onfocus")
GK_ATOM(onfocusin, "onfocusin")
GK_ATOM(onfocusout, "onfocusout")
GK_ATOM(onfrequencychange, "onfrequencychange")
GK_ATOM(onfullscreenchange, "onfullscreenchange")
GK_ATOM(onfullscreenerror, "onfullscreenerror")

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

@ -503,6 +503,14 @@ FORWARDED_EVENT(focus,
eFocus,
EventNameType_HTMLXUL,
eFocusEventClass)
FORWARDED_EVENT(focusin,
eFocusIn,
EventNameType_HTMLXUL,
eFocusEventClass)
FORWARDED_EVENT(focusout,
eFocusOut,
EventNameType_HTMLXUL,
eFocusEventClass)
FORWARDED_EVENT(load,
eLoad,
EventNameType_All,

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

@ -202,3 +202,4 @@ run-if = (e10s && os != "win") # Bug 1270043, crash at windows platforms; Bug126
[test_passive_listeners.html]
[test_paste_image.html]
[test_messageEvent_init.html]
[test_bug687787.html]

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

@ -0,0 +1,617 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=687787
-->
<head>
<title>Test for Bug 687787</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/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=687787">Mozilla Bug 687787</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
var content = document.getElementById('content');
var eventStack = [];
function _callback(e){
var event = {'type' : e.type, 'target' : e.target, 'relatedTarget' : e.relatedTarget }
eventStack.push(event);
}
function clearEventStack(){
eventStack = [];
}
window.addEventListener("focus", _callback, true);
window.addEventListener("focusin", _callback, true);
window.addEventListener("focusout", _callback, true);
window.addEventListener("blur", _callback, true);
function CompareEventToExpected(e, expected) {
if (expected == null || e == null)
return false;
if (e.type == expected.type && e.target == expected.target && e.relatedTarget == expected.relatedTarget)
return true;
return false;
}
function TestEventOrderNormal() {
var input1 = document.createElement('input');
var input2 = document.createElement('input');
var input3 = document.createElement('input');
var content = document.getElementById('content');
input1.setAttribute('id', 'input1');
input2.setAttribute('id', 'input2');
input3.setAttribute('id', 'input3');
input1.setAttribute('type', 'text');
input2.setAttribute('type', 'text');
input3.setAttribute('type', 'text');
content.appendChild(input1);
content.appendChild(input2);
content.appendChild(input3);
content.style.display = 'block'
expectedEventOrder = [
{'type' : 'blur',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focusout',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focus',
'target' : input2,
'relatedTarget' : input1},
{'type' : 'focusin',
'target' : input2,
'relatedTarget' : input1},
{'type' : 'blur',
'target' : input2,
'relatedTarget' : input3},
{'type' : 'focusout',
'target' : input2,
'relatedTarget' : input3},
{'type' : 'focus',
'target' : input3,
'relatedTarget' : input2},
{'type' : 'focusin',
'target' : input3,
'relatedTarget' : input2},
]
input1.focus();
clearEventStack();
input2.focus();
input3.focus();
for (var i = 0; i < expectedEventOrder.length || i < eventStack.length ; i++) {
ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ 'Expected ('
+ expectedEventOrder[i].type + ','
+ (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ 'Actual ('
+ eventStack[i].type + ','
+ (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
}
content.innerHTML = '';
}
function TestEventOrderNormalFiresAtRightTime() {
var input1 = document.createElement('input');
var input2 = document.createElement('input');
var input3 = document.createElement('input');
var content = document.getElementById('content');
input1.setAttribute('id', 'input1');
input2.setAttribute('id', 'input2');
input3.setAttribute('id', 'input3');
input1.setAttribute('type', 'text');
input2.setAttribute('type', 'text');
input3.setAttribute('type', 'text');
input1.onblur = function(e)
{
ok(document.activeElement == document.body, 'input1: not focused when blur fires')
}
input1.addEventListener('focusout', function(e)
{
ok(document.activeElement == document.body, 'input1: not focused when focusout fires')
});
input2.onfocus = function(e)
{
ok(document.activeElement == input2, 'input2: focused when focus fires')
}
input2.addEventListener('focusin', function(e)
{
ok(document.activeElement == input2, 'input2: focused when focusin fires')
});
content.appendChild(input1);
content.appendChild(input2);
content.style.display = 'block'
expectedEventOrder = [
{'type' : 'blur',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focusout',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focus',
'target' : input2,
'relatedTarget' : input1},
{'type' : 'focusin',
'target' : input2,
'relatedTarget' : input1},
]
input1.focus();
clearEventStack();
input2.focus();
for (var i = 0; i < expectedEventOrder.length || i < eventStack.length ; i++) {
ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ 'Expected ('
+ expectedEventOrder[i].type + ','
+ (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ 'Actual ('
+ eventStack[i].type + ','
+ (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
}
content.innerHTML = '';
}
function TestFocusOutRedirectsFocus() {
var input1 = document.createElement('input');
var input2 = document.createElement('input');
var input3 = document.createElement('input');
var content = document.getElementById('content');
input1.setAttribute('id', 'input1');
input2.setAttribute('id', 'input2');
input3.setAttribute('id', 'input3');
input1.setAttribute('type', 'text');
input2.setAttribute('type', 'text');
input3.setAttribute('type', 'text');
input1.addEventListener('focusout', function () {
input3.focus();
});
content.appendChild(input1);
content.appendChild(input2);
content.appendChild(input3);
content.style.display = 'block'
expectedEventOrder = [
{'type' : 'blur',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focusout',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focus',
'target' : input3,
'relatedTarget' : null},
{'type' : 'focusin',
'target' : input3,
'relatedTarget' : null},
]
input1.focus();
clearEventStack();
input2.focus();
for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ 'Expected ('
+ expectedEventOrder[i].type + ','
+ (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ 'Actual ('
+ eventStack[i].type + ','
+ (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
}
content.innerHTML = '';
}
function TestFocusInRedirectsFocus() {
var input1 = document.createElement('input');
var input2 = document.createElement('input');
var input3 = document.createElement('input');
var content = document.getElementById('content');
input1.setAttribute('id', 'input1');
input2.setAttribute('id', 'input2');
input3.setAttribute('id', 'input3');
input1.setAttribute('type', 'text');
input2.setAttribute('type', 'text');
input3.setAttribute('type', 'text');
input2.addEventListener('focusin', function () {
input3.focus();
});
content.appendChild(input1);
content.appendChild(input2);
content.appendChild(input3);
content.style.display = 'block'
expectedEventOrder = [
{'type' : 'blur',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focusout',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focus',
'target' : input2,
'relatedTarget' : input1},
{'type' : 'focusin',
'target' : input2,
'relatedTarget' : input1},
{'type' : 'blur',
'target' : input2,
'relatedTarget' : input3},
{'type' : 'focusout',
'target' : input2,
'relatedTarget' : input3},
{'type' : 'focus',
'target' : input3,
'relatedTarget' : input2},
{'type' : 'focusin',
'target' : input3,
'relatedTarget' : input2},
]
input1.focus();
clearEventStack();
input2.focus();
for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ 'Expected ('
+ expectedEventOrder[i].type + ','
+ (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ 'Actual ('
+ eventStack[i].type + ','
+ (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
}
content.innerHTML = '';
}
function TestBlurRedirectsFocus() {
var input1 = document.createElement('input');
var input2 = document.createElement('input');
var input3 = document.createElement('input');
var content = document.getElementById('content');
input1.setAttribute('id', 'input1');
input2.setAttribute('id', 'input2');
input3.setAttribute('id', 'input3');
input1.setAttribute('type', 'text');
input2.setAttribute('type', 'text');
input3.setAttribute('type', 'text');
input1.onblur = function () {
input3.focus();
}
content.appendChild(input1);
content.appendChild(input2);
content.appendChild(input3);
content.style.display = 'block'
expectedEventOrder = [
{'type' : 'blur',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focus',
'target' : input3,
'relatedTarget' : null},
{'type' : 'focusin',
'target' : input3,
'relatedTarget' : null},
{'type' : 'focusout',
'target' : input1,
'relatedTarget' : input2},
]
input1.focus();
clearEventStack();
input2.focus();
for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ 'Expected ('
+ expectedEventOrder[i].type + ','
+ (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ 'Actual ('
+ eventStack[i].type + ','
+ (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
}
content.innerHTML = '';
}
function TestFocusRedirectsFocus() {
var input1 = document.createElement('input');
var input2 = document.createElement('input');
var input3 = document.createElement('input');
var content = document.getElementById('content');
input1.setAttribute('id', 'input1');
input2.setAttribute('id', 'input2');
input3.setAttribute('id', 'input3');
input1.setAttribute('type', 'text');
input2.setAttribute('type', 'text');
input3.setAttribute('type', 'text');
input2.onfocus = function () {
input3.focus();
}
content.appendChild(input1);
content.appendChild(input2);
content.appendChild(input3);
content.style.display = 'block'
expectedEventOrder = [
{'type' : 'blur',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focusout',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focus',
'target' : input2,
'relatedTarget' : input1},
{'type' : 'blur',
'target' : input2,
'relatedTarget' : input3},
{'type' : 'focusout',
'target' : input2,
'relatedTarget' : input3},
{'type' : 'focus',
'target' : input3,
'relatedTarget' : input2},
{'type' : 'focusin',
'target' : input3,
'relatedTarget' : input2},
]
input1.focus();
clearEventStack();
input2.focus();
for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ 'Expected ('
+ expectedEventOrder[i].type + ','
+ (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ 'Actual ('
+ eventStack[i].type + ','
+ (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
}
content.innerHTML = '';
}
function TestEventOrderDifferentDocument() {
var input1 = document.createElement('input');
var input2 = document.createElement('input');
var iframe1 = document.createElement('iframe');
var content = document.getElementById('content');
input1.setAttribute('id', 'input1');
input2.setAttribute('id', 'input2');
iframe1.setAttribute('id', 'iframe1');
input1.setAttribute('type', 'text');
input2.setAttribute('type', 'text');
content.appendChild(input1);
content.appendChild(iframe1);
iframe1.contentDocument.body.appendChild(input2);
content.style.display = 'block'
iframe1.contentDocument.addEventListener("focus", _callback, true);
iframe1.contentDocument.addEventListener("focusin", _callback, true);
iframe1.contentDocument.addEventListener("focusout", _callback, true);
iframe1.contentDocument.addEventListener("blur", _callback, true);
expectedEventOrder = [
{'type' : 'blur',
'target' : input1,
'relatedTarget' : null},
{'type' : 'focusout',
'target' : input1,
'relatedTarget' : null},
{'type' : 'blur',
'target' : document,
'relatedTarget' : null},
{'type' : 'blur',
'target' : window,
'relatedTarget' : null},
{'type' : 'focus',
'target' : iframe1.contentDocument,
'relatedTarget' : null},
{'type' : 'focus',
'target' : input2,
'relatedTarget' : null},
{'type' : 'focusin',
'target' : input2,
'relatedTarget' : null},
]
input1.focus();
clearEventStack();
input2.focus();
for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ 'Expected ('
+ expectedEventOrder[i].type + ','
+ (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ 'Actual ('
+ eventStack[i].type + ','
+ (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
}
content.innerHTML = '';
}
function TestFocusOutMovesTarget() {
var input1 = document.createElement('input');
var input2 = document.createElement('input');
var iframe1 = document.createElement('iframe');
var content = document.getElementById('content');
input1.setAttribute('id', 'input1');
input2.setAttribute('id', 'input2');
iframe1.setAttribute('id', 'iframe1');
input1.setAttribute('type', 'text');
input2.setAttribute('type', 'text');
input1.addEventListener('focusout', function () {
iframe1.contentDocument.body.appendChild(input2);
});
content.appendChild(input1);
content.appendChild(input2);
content.appendChild(iframe1);
content.style.display = 'block'
iframe1.contentDocument.addEventListener("focus", _callback, true);
iframe1.contentDocument.addEventListener("focusin", _callback, true);
iframe1.contentDocument.addEventListener("focusout", _callback, true);
iframe1.contentDocument.addEventListener("blur", _callback, true);
expectedEventOrder = [
{'type' : 'blur',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focusout',
'target' : input1,
'relatedTarget' : input2},
{'type' : 'focus',
'target' : input2,
'relatedTarget' : null},
{'type' : 'focusin',
'target' : input2,
'relatedTarget' : null},
]
input1.focus();
clearEventStack();
input2.focus();
for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ 'Expected ('
+ expectedEventOrder[i].type + ','
+ (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ 'Actual ('
+ eventStack[i].type + ','
+ (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
}
content.innerHTML = '';
}
function TestBlurWindowAndRefocusInputOnlyFiresFocusInOnInput() {
var input1 = document.createElement('input');
var content = document.getElementById('content');
input1.setAttribute('id', 'input1');
input1.setAttribute('type', 'text');
content.appendChild(input1);
expectedEventOrder = [
{'type' : 'focus',
'target' : document,
'relatedTarget' : null},
{'type' : 'focus',
'target' : window,
'relatedTarget' : null},
{'type' : 'focus',
'target' : input1,
'relatedTarget' : null},
{'type' : 'focusin',
'target' : input1,
'relatedTarget' : null},
]
window.blur();
clearEventStack();
input1.focus();
for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ 'Expected ('
+ expectedEventOrder[i].type + ','
+ (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ 'Actual ('
+ eventStack[i].type + ','
+ (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
}
content.innerHTML = '';
}
TestEventOrderNormal();
TestEventOrderNormalFiresAtRightTime();
TestFocusOutRedirectsFocus();
TestFocusInRedirectsFocus();
TestBlurRedirectsFocus();
TestFocusRedirectsFocus();
TestFocusOutMovesTarget();
TestEventOrderDifferentDocument();
TestBlurWindowAndRefocusInputOnlyFiresFocusInOnInput();
</script>
</pre>
</body>
</html>

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

@ -38693,6 +38693,12 @@
"url": "/secure-contexts/shared-worker-secure-first.https.html"
}
],
"uievents/order-of-events/focus-events/focus-automated-blink-webkit.html": [
{
"path": "uievents/order-of-events/focus-events/focus-automated-blink-webkit.html",
"url": "/uievents/order-of-events/focus-events/focus-automated-blink-webkit.html"
}
],
"webaudio/the-audio-api/the-constantsourcenode-interface/test-constantsourcenode.html": [
{
"path": "webaudio/the-audio-api/the-constantsourcenode-interface/test-constantsourcenode.html",

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

@ -0,0 +1,170 @@
<!DOCTYPE html>
<!-- Modified from Chris Rebert's manual version -->
<!-- This documents the behavior according to blink's implementation -->
<html>
<head>
<meta charset="utf-8">
<title>Focus-related events should fire in the correct order</title>
<link rel="help" href="https://w3c.github.io/uievents/#events-focusevent-event-order">
<meta name="flags" content="interact">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body id="body">
<input type="text" id="a" value="First">
<input type="text" id="b" value="Second">
<br>
<input type="text" id="c" value="Third">
<iframe id="iframe">
</iframe>
<br>
<script>
var test_id = 0;
var tests = ['normal', 'iframe']
function record(evt) {
if (done && (evt.type == 'focusin' || evt.type == 'focus') && (evt.target == c)) {
startNext();
}
if (!done) {
var activeElement = document.activeElement ?
(document.activeElement.tagName === 'IFRAME' ?
document.activeElement.contentDocument.activeElement.id :
document.activeElement.id) : null;
events[tests[test_id]].push(evt.type);
targets[tests[test_id]].push(evt.target.id);
focusedElements[tests[test_id]].push(activeElement);
relatedTargets[tests[test_id]].push(evt.relatedTarget ? evt.relatedTarget.id : null);
}
}
function startNext() {
done = false;
test_id++;
}
function finish() {
done = true;
}
var relevantEvents = [
'focus',
'blur',
'focusin',
'focusout'
];
var iframe = document.getElementById('iframe');
var a = document.getElementById('a');
var b = document.getElementById('b');
var c = document.getElementById('c');
var d = document.createElement('input');
d.setAttribute('id', 'd');
d.setAttribute('type', 'text');
d.setAttribute('value', 'Fourth');
var events = {'normal': [], 'iframe': []};
var targets = {'normal': [], 'iframe': []};
var focusedElements = {'normal': [], 'iframe': []};
var relatedTargets = {'normal': [], 'iframe': []};
var done = false;
var async_test_normal = async_test('Focus-related events should fire in the correct order (same DocumentOwner)');
var async_test_iframe_static = async_test('Focus-related events should fire in the correct order (different DocumentOwner)');
window.onload = function(evt) {
iframe.contentDocument.body.appendChild(d);
var inputs = [a, b, c, d];
for (var i = 0; i < inputs.length; i++) {
for (var k = 0; k < relevantEvents.length; k++) {
inputs[i].addEventListener(relevantEvents[k], record, false);
}
}
a.addEventListener('focusin', function() { b.focus(); }, false);
b.addEventListener('focusin', function() {
console.log(events['normal']);
console.log(targets['normal']);
console.log(relatedTargets['normal']);
console.log(focusedElements['normal']);
async_test_normal.step( function() {
assert_array_equals(
events['normal'],
['focus', 'focusin', 'blur', 'focusout', 'focus', 'focusin'],
'Focus-related events should fire in this order: focusin, focus, focusout, focusin, blur, focus'
);
assert_array_equals(
targets['normal'],
[ 'a', 'a', 'a', 'a', 'b', 'b'],
'Focus-related events should fire at the correct targets'
);
assert_array_equals(
relatedTargets['normal'],
[ null, null, 'b', 'b', 'a', 'a'],
'Focus-related events should reference correct relatedTargets'
);
assert_array_equals(
focusedElements['normal'],
[ 'a', 'a', 'body', 'body', 'b', 'b'],
'Focus-related events should fire at the correct time relative to actual focus changes'
);
async_test_normal.done();
});
b.addEventListener('focusout', function() { finish(); c.focus(); });
b.blur();
}, false);
c.addEventListener('focusin', function() {d.focus();});
d.addEventListener('focusin', function() {
console.log(events['iframe']);
console.log(targets['iframe']);
console.log(relatedTargets['iframe']);
console.log(focusedElements['iframe']);
async_test_iframe_static.step(function() {
assert_array_equals(
events['iframe'],
['focus', 'focusin', 'blur', 'focusout', 'focus', 'focusin'],
'Focus-related events should fire in this order: focusin, focus, focusout, focusin, blur, focus'
);
assert_array_equals(
targets['iframe'],
[ 'c', 'c', 'c', 'c', 'd', 'd'],
'Focus-related events should fire at the correct targets'
);
assert_array_equals(
relatedTargets['iframe'],
[ null, null, null, null, null, null],
'Focus-related events should reference correct relatedTargets'
);
assert_array_equals(
focusedElements['iframe'],
[ 'c', 'c', 'body', 'body', 'd', 'd'],
'Focus-related events should fire at the correct time relative to actual focus changes'
);
async_test_iframe_static.done();
});
d.addEventListener('focusout', function() { finish();});
}, false);
a.focus();
}
</script>
</body>
</html>

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

@ -129,6 +129,8 @@ NS_EVENT_MESSAGE(eFormInvalid)
//Need separate focus/blur notifications for non-native widgets
NS_EVENT_MESSAGE(eFocus)
NS_EVENT_MESSAGE(eBlur)
NS_EVENT_MESSAGE(eFocusIn)
NS_EVENT_MESSAGE(eFocusOut)
NS_EVENT_MESSAGE(eDragEnter)
NS_EVENT_MESSAGE(eDragOver)

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

@ -3047,6 +3047,8 @@ case _value: eventName.AssignLiteral(_name) ; break
_ASSIGN_eventName(eDragOver,"eDragOver");
_ASSIGN_eventName(eEditorInput,"eEditorInput");
_ASSIGN_eventName(eFocus,"eFocus");
_ASSIGN_eventName(eFocusIn,"eFocusIn");
_ASSIGN_eventName(eFocusOut,"eFocusOut");
_ASSIGN_eventName(eFormSelect,"eFormSelect");
_ASSIGN_eventName(eFormChange,"eFormChange");
_ASSIGN_eventName(eFormReset,"eFormReset");