Bug 670317, add a focus manager flag to restrict tab navigation to the same frame, r=smaug
authorNeil Deakin <neil@mozilla.com>
Mon, 13 Feb 2012 14:24:28 -0500
changeset 86739 5185e21c55ec3abacf098b4069e0fd953a6353e1
parent 86738 a98cf6ce2f0a09eebc150b49beedd18217e7ad9e
child 86740 da94847d66d438a964cf5e75a813543a8badbcde
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs670317
milestone13.0a1
Bug 670317, add a focus manager flag to restrict tab navigation to the same frame, r=smaug
content/html/content/src/nsHTMLLegendElement.cpp
dom/base/nsFocusManager.cpp
dom/base/nsFocusManager.h
dom/interfaces/base/nsIFocusManager.idl
dom/tests/mochitest/general/Makefile.in
dom/tests/mochitest/general/test_focus_legend_noparent.html
--- a/content/html/content/src/nsHTMLLegendElement.cpp
+++ b/content/html/content/src/nsHTMLLegendElement.cpp
@@ -183,17 +183,18 @@ nsHTMLLegendElement::Focus()
 
   // If the legend isn't focusable, focus whatever is focusable following
   // the legend instead, bug 81481.
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   if (!fm)
     return NS_OK;
 
   nsCOMPtr<nsIDOMElement> result;
-  return fm->MoveFocus(nsnull, this, nsIFocusManager::MOVEFOCUS_FORWARD, 0,
+  return fm->MoveFocus(nsnull, this, nsIFocusManager::MOVEFOCUS_FORWARD,
+                       nsIFocusManager::FLAG_NOPARENTFRAME,
                        getter_AddRefs(result));
 }
 
 void
 nsHTMLLegendElement::PerformAccesskey(bool aKeyCausesActivation,
                                       bool aIsTrustedEvent)
 {
   // just use the same behaviour as the focus method
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -522,18 +522,19 @@ nsFocusManager::MoveFocus(nsIDOMWindow* 
   else {
     window = aWindow ? do_QueryInterface(aWindow) : mFocusedWindow;
     NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
     window = window->GetOuterWindow();
   }
 
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
+  bool noParentTraversal = aFlags & FLAG_NOPARENTFRAME;
   nsCOMPtr<nsIContent> newFocus;
-  nsresult rv = DetermineElementToMoveFocus(window, startContent, aType,
+  nsresult rv = DetermineElementToMoveFocus(window, startContent, aType, noParentTraversal,
                                             getter_AddRefs(newFocus));
   NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DEBUG_FOCUS_NAVIGATION
   PRINTTAGF("-> Element to be focused: %s\n", newFocus);
 #endif
 
   if (newFocus) {
@@ -2327,17 +2328,17 @@ nsFocusManager::GetSelectionLocation(nsI
   NS_IF_ADDREF(*aEndContent);
 
   return rv;
 }
 
 nsresult
 nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
                                             nsIContent* aStartContent,
-                                            PRInt32 aType,
+                                            PRInt32 aType, bool aNoParentTraversal,
                                             nsIContent** aNextContent)
 {
   *aNextContent = nsnull;
 
   nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
   if (!docShell)
     return NS_OK;
 
@@ -2557,16 +2558,22 @@ nsFocusManager::DetermineElementToMoveFo
         return NS_OK;
       }
     }
 
     doNavigation = true;
     skipOriginalContentCheck = false;
     ignoreTabIndex = false;
 
+    if (aNoParentTraversal) {
+      startContent = rootContent;
+      tabIndex = forward ? 1 : 0;
+      continue;
+    }
+
     // reached the beginning or end of the document. Traverse up to the parent
     // document and try again.
     nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(docShell);
 
     nsCOMPtr<nsIDocShellTreeItem> docShellParent;
     dsti->GetParent(getter_AddRefs(docShellParent));
     if (docShellParent) {
       // move up to the parent shell and try again from there.
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -342,21 +342,23 @@ protected:
   /**
    * Helper function for MoveFocus which determines the next element
    * to move the focus to and returns it in aNextContent.
    *
    * aWindow is the window to adjust the focus within, and aStart is
    * the element to start navigation from. For tab key navigation,
    * this should be the currently focused element.
    *
-   * aType is the type passed to MoveFocus.
+   * aType is the type passed to MoveFocus. If aNoParentTraversal is set,
+   * navigation is not done to parent documents and iteration returns to the
+   * beginning (or end) of the starting document.
    */
   nsresult DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
                                        nsIContent* aStart,
-                                       PRInt32 aType,
+                                       PRInt32 aType, bool aNoParentTraversal,
                                        nsIContent** aNextContent);
 
   /**
    * Retrieve the next tabbable element within a document, using focusability
    * and tabindex to determine the tab order. The element is returned in
    * aResultContent.
    *
    * aRootContent is the root node -- nodes above this will not be examined.
--- a/dom/interfaces/base/nsIFocusManager.idl
+++ b/dom/interfaces/base/nsIFocusManager.idl
@@ -186,16 +186,24 @@ interface nsIFocusManager : nsISupports
    * switch focus to that window. Instead, just update the focus within that
    * window and leave the application focus as is. This flag will have no
    * effect if a child window is focused and an attempt is made to adjust the
    * focus in an ancestor, as the frame must be switched in this case.
    */
   const unsigned long FLAG_NOSWITCHFRAME = 4;
 
   /**
+   * This flag is only used when passed to moveFocus. If set, focus is never
+   * moved to the parent frame of the starting element's document, instead
+   * iterating around to the beginning of that document again. Child frames
+   * are navigated as normal.
+   */
+  const unsigned long FLAG_NOPARENTFRAME = 8;
+
+  /**
    * Focus is changing due to a mouse operation, for instance the mouse was
    * clicked on an element.
    */
   const unsigned long FLAG_BYMOUSE = 0x1000;
 
   /**
    * Focus is changing due to a key operation, for instance pressing the tab
    * key. This flag would normally be passed when MOVEFOCUS_FORWARD or
--- a/dom/tests/mochitest/general/Makefile.in
+++ b/dom/tests/mochitest/general/Makefile.in
@@ -75,16 +75,17 @@ include $(topsrcdir)/config/rules.mk
 		browserFrameHelpers.js \
 		test_browserFrame1.html \
 		test_browserFrame2.html \
 		test_browserFrame3.html \
 		test_browserFrame4.html \
 		test_browserFrame5.html \
 		test_browserFrame6.html \
 		test_for_of.html \
+		test_focus_legend_noparent.html \
 		$(NULL)
 
 _CHROME_FILES = \
 		test_innerScreen.xul \
 		test_offsets.xul \
 		test_offsets.js \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_focus_legend_noparent.html
@@ -0,0 +1,34 @@
+<html>
+<head>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<script type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest()
+{
+    window.focus();
+    var g = document.createElementNS("http://www.w3.org/1999/xhtml", "legend");
+    document.body.appendChild(g);
+    setTimeout(g.focus.bind(g), 0);
+    setTimeout(done, 10);
+}
+
+function done()
+{
+  ok(document.hasFocus(), "document is still focused");
+  is(document.activeElement, document.body, "document has no focused element")
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(runTest);
+</script>
+</head>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<body onblur="dump('blurred window!\n')"></body>
+</html>