Modified: trunk/LayoutTests/fast/dom/HTMLDocument/active-element-frames-expected.txt (143298 => 143299)
--- trunk/LayoutTests/fast/dom/HTMLDocument/active-element-frames-expected.txt 2013-02-19 07:18:17 UTC (rev 143298)
+++ trunk/LayoutTests/fast/dom/HTMLDocument/active-element-frames-expected.txt 2013-02-19 07:22:32 UTC (rev 143299)
@@ -1,6 +1,7 @@
When the contents of an iframe have focus, the activeElement in the parent document should be the iframe element.
+
Focusing top/input-0
PASS: top document.activeElement is top/input-0
PASS: top/iframe-1 document.activeElement is top/iframe-1/body-1
@@ -36,3 +37,38 @@
PASS: top/iframe-1/iframe-3 document.activeElement is top/iframe-1/iframe-3/body-3
PASS: top/iframe-2/iframe-4 document.activeElement is top/iframe-2/iframe-4/input-4
+Focusing top/input-0 after focusing its window
+PASS: top document.activeElement is top/input-0
+PASS: top/iframe-1 document.activeElement is top/iframe-1/body-1
+PASS: top/iframe-2 document.activeElement is top/iframe-2/body-2
+PASS: top/iframe-1/iframe-3 document.activeElement is top/iframe-1/iframe-3/body-3
+PASS: top/iframe-2/iframe-4 document.activeElement is top/iframe-2/iframe-4/body-4
+
+Focusing top/iframe-1/input-1 after focusing its window
+PASS: top document.activeElement is top/iframe-1
+PASS: top/iframe-1 document.activeElement is top/iframe-1/input-1
+PASS: top/iframe-2 document.activeElement is top/iframe-2/body-2
+PASS: top/iframe-1/iframe-3 document.activeElement is top/iframe-1/iframe-3/body-3
+PASS: top/iframe-2/iframe-4 document.activeElement is top/iframe-2/iframe-4/body-4
+
+Focusing top/iframe-2/input-2 after focusing its window
+PASS: top document.activeElement is top/iframe-2
+PASS: top/iframe-1 document.activeElement is top/iframe-1/body-1
+PASS: top/iframe-2 document.activeElement is top/iframe-2/input-2
+PASS: top/iframe-1/iframe-3 document.activeElement is top/iframe-1/iframe-3/body-3
+PASS: top/iframe-2/iframe-4 document.activeElement is top/iframe-2/iframe-4/body-4
+
+Focusing top/iframe-1/iframe-3/input-3 after focusing its window
+PASS: top document.activeElement is top/iframe-1
+PASS: top/iframe-1 document.activeElement is top/iframe-1/iframe-3
+PASS: top/iframe-2 document.activeElement is top/iframe-2/body-2
+PASS: top/iframe-1/iframe-3 document.activeElement is top/iframe-1/iframe-3/input-3
+PASS: top/iframe-2/iframe-4 document.activeElement is top/iframe-2/iframe-4/body-4
+
+Focusing top/iframe-2/iframe-4/input-4 after focusing its window
+PASS: top document.activeElement is top/iframe-2
+PASS: top/iframe-1 document.activeElement is top/iframe-1/body-1
+PASS: top/iframe-2 document.activeElement is top/iframe-2/iframe-4
+PASS: top/iframe-1/iframe-3 document.activeElement is top/iframe-1/iframe-3/body-3
+PASS: top/iframe-2/iframe-4 document.activeElement is top/iframe-2/iframe-4/input-4
+
Modified: trunk/LayoutTests/fast/dom/HTMLDocument/active-element-frames.html (143298 => 143299)
--- trunk/LayoutTests/fast/dom/HTMLDocument/active-element-frames.html 2013-02-19 07:18:17 UTC (rev 143298)
+++ trunk/LayoutTests/fast/dom/HTMLDocument/active-element-frames.html 2013-02-19 07:22:32 UTC (rev 143299)
@@ -56,48 +56,60 @@
print('FAIL: ' + describe(doc) + ' document.activeElement is ' + describe(doc.activeElement) + ' but expected ' + describe(expected));
}
-if (window.testRunner)
- testRunner.dumpAsText();
+function setFocus(node, shouldFocusWindow)
+{
+ print('\nFocusing ' + describe(node) + (shouldFocusWindow ? ' after focusing its window' : ''));
+ if (shouldFocusWindow)
+ node.ownerDocument.defaultView.focus();
+ node.focus();
+}
-print('Focusing ' + describe(input0));
-input0.focus();
-assertActiveElement(doc0, input0);
-assertActiveElement(doc1, doc1.body);
-assertActiveElement(doc2, doc2.body);
-assertActiveElement(doc3, doc3.body);
-assertActiveElement(doc4, doc4.body);
+function test(shouldFocusWindow)
+{
+ setFocus(input0, shouldFocusWindow);
+ assertActiveElement(doc0, input0);
+ assertActiveElement(doc1, doc1.body);
+ assertActiveElement(doc2, doc2.body);
+ assertActiveElement(doc3, doc3.body);
+ assertActiveElement(doc4, doc4.body);
-print('\nFocusing ' + describe(input1));
-input1.focus();
-assertActiveElement(doc0, iframe1);
-assertActiveElement(doc1, input1);
-assertActiveElement(doc2, doc2.body);
-assertActiveElement(doc3, doc3.body);
-assertActiveElement(doc4, doc4.body);
+ setFocus(input1, shouldFocusWindow);
+ assertActiveElement(doc0, iframe1);
+ assertActiveElement(doc1, input1);
+ assertActiveElement(doc2, doc2.body);
+ assertActiveElement(doc3, doc3.body);
+ assertActiveElement(doc4, doc4.body);
-print('\nFocusing ' + describe(input2));
-input2.focus();
-assertActiveElement(doc0, iframe2);
-assertActiveElement(doc1, doc1.body);
-assertActiveElement(doc2, input2);
-assertActiveElement(doc3, doc3.body);
-assertActiveElement(doc4, doc4.body);
+ setFocus(input2, shouldFocusWindow);
+ assertActiveElement(doc0, iframe2);
+ assertActiveElement(doc1, doc1.body);
+ assertActiveElement(doc2, input2);
+ assertActiveElement(doc3, doc3.body);
+ assertActiveElement(doc4, doc4.body);
-print('\nFocusing ' + describe(input3));
-input3.focus();
-assertActiveElement(doc0, iframe1);
-assertActiveElement(doc1, iframe3);
-assertActiveElement(doc2, doc2.body);
-assertActiveElement(doc3, input3);
-assertActiveElement(doc4, doc4.body);
+ setFocus(input3, shouldFocusWindow);
+ assertActiveElement(doc0, iframe1);
+ assertActiveElement(doc1, iframe3);
+ assertActiveElement(doc2, doc2.body);
+ assertActiveElement(doc3, input3);
+ assertActiveElement(doc4, doc4.body);
-print('\nFocusing ' + describe(input4));
-input4.focus();
-assertActiveElement(doc0, iframe2);
-assertActiveElement(doc1, doc1.body);
-assertActiveElement(doc2, iframe4);
-assertActiveElement(doc3, doc3.body);
-assertActiveElement(doc4, input4);
+ setFocus(input4, shouldFocusWindow);
+ assertActiveElement(doc0, iframe2);
+ assertActiveElement(doc1, doc1.body);
+ assertActiveElement(doc2, iframe4);
+ assertActiveElement(doc3, doc3.body);
+ assertActiveElement(doc4, input4);
+}
+if (window.testRunner)
+ testRunner.dumpAsText();
+
+// Test focusing elements directly.
+test(false);
+
+// Test focusing elements after focusing their windows first.
+test(true);
+
</script>
</body>
Added: trunk/LayoutTests/fast/frames/frame-focus-blurs-active-element.html (0 => 143299)
--- trunk/LayoutTests/fast/frames/frame-focus-blurs-active-element.html (rev 0)
+++ trunk/LayoutTests/fast/frames/frame-focus-blurs-active-element.html 2013-02-19 07:22:32 UTC (rev 143299)
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<body>
+<input id="input" type="text"></input>
+<iframe id="iframe"></iframe>
+<script>
+if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+}
+
+var input = document.getElementById("input");
+var iframe = document.getElementById("iframe");
+
+window.addEventListener("blur", function(event) {
+ if (event.target === input)
+ console.log("PASS: blur event was fired on the active element when the iframe was focused.");
+ else if (event.target === window && window.testRunner)
+ testRunner.notifyDone();
+}, true);
+
+input.focus();
+iframe.contentWindow.focus();
+</script>
+</body>
+</html>
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (143298 => 143299)
--- trunk/Source/WebCore/ChangeLog 2013-02-19 07:18:17 UTC (rev 143298)
+++ trunk/Source/WebCore/ChangeLog 2013-02-19 07:22:32 UTC (rev 143299)
@@ -1,3 +1,49 @@
+2013-02-18 Andy Estes <[email protected]>
+
+ Focusing a new frame (via window.focus()) should blur the active element in the current frame
+ https://bugs.webkit.org/show_bug.cgi?id=110172
+
+ Reviewed by Ryosuke Niwa.
+
+ When a change in the focused node crosses a frame boundary, WebKit
+ doesn't always succeed in blurring the old focused node before focusing
+ the new one.
+
+ Each document remembers its focused node, and a Page-scoped
+ FocusController remembers the focused frame. If a new focused node is
+ in a different frame than the focused frame, FocusController tells the
+ old frame's document to clear its focused node before focusing the new
+ one (and remembering the new frame).
+
+ Unfortunately, web content can confuse FocusController by calling
+ window.focus() at the wrong time. Since window.focus() changes
+ FocusController's focused frame without focusing a new node,
+ FocusController won't think that a frame boundary is being crossed if a
+ node in this frame is later focused. Therefore it won't clear the old
+ frame's focused node (it won't even know which frame contained the old
+ focused node), causing at least two bugs:
+
+ 1) The node in the old frame will not receive a blur event.
+ 2) Calling document.activeElement on the main frame will return the
+ previously focused node, but the HTML5 spec says it should return
+ the frame owner element if a subframe has focus.
+
+ Fix both of these bugs by explicitly clearing the current frame's
+ focused node if window.focus() changes the focused frame. This fix
+ carries some compatibility risk by changing a long-standing behavior
+ of the engine (we've had this bug since the beginning of the project,
+ AFAICT). On the upside, it matches the behavior of both Firefox and IE,
+ matches what HTML5 says about subframe focus, and fixes at least one
+ well-known enterprise web app.
+
+ Tests: fast/dom/HTMLDocument/active-element-frames.html
+ fast/frames/frame-focus-blurs-active-element.html
+
+ * page/DOMWindow.cpp:
+ (WebCore::DOMWindow::focus): If the frame being focused is not the same
+ as the currently focused frame, clear the currently focused frame's
+ focused node.
+
2013-02-18 Simon Fraser <[email protected]>
Clean up the boolean argument to visibleContentRect
Modified: trunk/Source/WebCore/page/DOMWindow.cpp (143298 => 143299)
--- trunk/Source/WebCore/page/DOMWindow.cpp 2013-02-19 07:18:17 UTC (rev 143298)
+++ trunk/Source/WebCore/page/DOMWindow.cpp 2013-02-19 07:22:32 UTC (rev 143299)
@@ -58,6 +58,7 @@
#include "ExceptionCode.h"
#include "ExceptionCodePlaceholder.h"
#include "FloatRect.h"
+#include "FocusController.h"
#include "Frame.h"
#include "FrameLoadRequest.h"
#include "FrameLoader.h"
@@ -932,6 +933,11 @@
if (!m_frame)
return;
+ // Clear the current frame's focused node if a new frame is about to be focused.
+ Frame* focusedFrame = page->focusController()->focusedFrame();
+ if (focusedFrame && focusedFrame != m_frame)
+ focusedFrame->document()->setFocusedNode(0);
+
m_frame->eventHandler()->focusDocumentView();
}