Title: [143299] trunk
Revision
143299
Author
[email protected]
Date
2013-02-18 23:22:32 -0800 (Mon, 18 Feb 2013)

Log Message

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.

Source/WebCore:

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.

LayoutTests:

* fast/dom/HTMLDocument/active-element-frames-expected.txt:
* fast/dom/HTMLDocument/active-element-frames.html: Modified to run the
test a second time, focusing each element's frame before focusing the
element itself.
* fast/frames/frame-focus-blurs-active-element-expected.txt: Added.
* fast/frames/frame-focus-blurs-active-element.html: Added a test that
verifies a blur event is fired on the active element when a new frame
is focused.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (143298 => 143299)


--- trunk/LayoutTests/ChangeLog	2013-02-19 07:18:17 UTC (rev 143298)
+++ trunk/LayoutTests/ChangeLog	2013-02-19 07:22:32 UTC (rev 143299)
@@ -1,3 +1,19 @@
+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.
+
+        * fast/dom/HTMLDocument/active-element-frames-expected.txt:
+        * fast/dom/HTMLDocument/active-element-frames.html: Modified to run the
+        test a second time, focusing each element's frame before focusing the
+        element itself.
+        * fast/frames/frame-focus-blurs-active-element-expected.txt: Added.
+        * fast/frames/frame-focus-blurs-active-element.html: Added a test that
+        verifies a blur event is fired on the active element when a new frame
+        is focused.
+
 2013-02-18  Kondapally Kalyan  <[email protected]>
 
         [EFL][WebGL] Enable test webgl/conformance/canvas/texture-bindings-unaffected-on-resize.html.

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-expected.txt (0 => 143299)


--- trunk/LayoutTests/fast/frames/frame-focus-blurs-active-element-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/frames/frame-focus-blurs-active-element-expected.txt	2013-02-19 07:22:32 UTC (rev 143299)
@@ -0,0 +1,2 @@
+CONSOLE MESSAGE: line 17: PASS: blur event was fired on the active element when the iframe was focused.
+ 

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();
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to