mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Bug 653230, add panels with focusable elements into the document tab navigation order, r=smaug
This commit is contained in:
parent
ef8516d8a0
commit
463ad224a7
@ -2343,8 +2343,17 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIContent> startContent = aStartContent;
|
||||
if (!startContent && aType != MOVEFOCUS_CARET)
|
||||
startContent = aWindow->GetFocusedNode();
|
||||
if (!startContent && aType != MOVEFOCUS_CARET) {
|
||||
if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC) {
|
||||
// When moving between documents, make sure to get the right
|
||||
// starting content in a descendant.
|
||||
nsCOMPtr<nsPIDOMWindow> focusedWindow;
|
||||
startContent = GetFocusedDescendant(aWindow, true, getter_AddRefs(focusedWindow));
|
||||
}
|
||||
else {
|
||||
startContent = aWindow->GetFocusedNode();
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
if (startContent)
|
||||
@ -2362,11 +2371,11 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
return NS_OK;
|
||||
}
|
||||
if (aType == MOVEFOCUS_FORWARDDOC) {
|
||||
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(true));
|
||||
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, true));
|
||||
return NS_OK;
|
||||
}
|
||||
if (aType == MOVEFOCUS_BACKWARDDOC) {
|
||||
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(false));
|
||||
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, false));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -3136,66 +3145,192 @@ nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem* aItem,
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableDocument(bool aForward)
|
||||
nsFocusManager::GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward)
|
||||
{
|
||||
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||
if (!pm)
|
||||
return nsnull;
|
||||
|
||||
// Iterate through the array backwards if aForward is false.
|
||||
nsTArray<nsIFrame *> popups = pm->GetVisiblePopups();
|
||||
PRInt32 i = aForward ? 0 : popups.Length() - 1;
|
||||
PRInt32 end = aForward ? popups.Length() : -1;
|
||||
|
||||
for (; i != end; aForward ? i++ : i--) {
|
||||
nsIFrame* popupFrame = popups[i];
|
||||
if (aCurrentPopup) {
|
||||
// If the current popup is set, then we need to skip over this popup and
|
||||
// wait until the currently focused popup is found. Once found, the
|
||||
// current popup will be cleared so that the next popup is used.
|
||||
if (aCurrentPopup == popupFrame)
|
||||
aCurrentPopup = nsnull;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip over non-panels
|
||||
if (popupFrame->GetContent()->Tag() != nsGkAtoms::panel ||
|
||||
(aDocument && popupFrame->GetContent()->GetCurrentDoc() != aDocument)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the first focusable content within the popup. If there isn't any
|
||||
// focusable content in the popup, skip to the next popup.
|
||||
nsIPresShell* presShell = popupFrame->PresContext()->GetPresShell();
|
||||
if (presShell) {
|
||||
nsCOMPtr<nsIContent> nextFocus;
|
||||
nsIContent* popup = popupFrame->GetContent();
|
||||
nsresult rv = GetNextTabbableContent(presShell, popup,
|
||||
nsnull, popup,
|
||||
true, 1, false,
|
||||
getter_AddRefs(nextFocus));
|
||||
if (NS_SUCCEEDED(rv) && nextFocus) {
|
||||
return nextFocus.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableDocument(nsIContent* aStartContent, bool aForward)
|
||||
{
|
||||
// If currentPopup is set, then the starting content is in a panel.
|
||||
nsIFrame* currentPopup = nsnull;
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
nsCOMPtr<nsIDocShellTreeItem> startItem;
|
||||
if (mFocusedWindow) {
|
||||
|
||||
if (aStartContent) {
|
||||
doc = aStartContent->GetCurrentDoc();
|
||||
if (doc) {
|
||||
startItem = do_QueryInterface(doc->GetWindow()->GetDocShell());
|
||||
}
|
||||
|
||||
// Check if the starting content is inside a panel. Document navigation
|
||||
// must start from this panel instead of the document root.
|
||||
nsIContent* content = aStartContent;
|
||||
while (content) {
|
||||
if (content->NodeInfo()->Equals(nsGkAtoms::panel, kNameSpaceID_XUL)) {
|
||||
currentPopup = content->GetPrimaryFrame();
|
||||
break;
|
||||
}
|
||||
content = content->GetParent();
|
||||
}
|
||||
}
|
||||
else if (mFocusedWindow) {
|
||||
startItem = do_QueryInterface(mFocusedWindow->GetDocShell());
|
||||
doc = do_QueryInterface(mFocusedWindow->GetExtantDocument());
|
||||
}
|
||||
else {
|
||||
nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mActiveWindow);
|
||||
startItem = do_QueryInterface(webnav);
|
||||
|
||||
if (mActiveWindow) {
|
||||
doc = do_QueryInterface(mActiveWindow->GetExtantDocument());
|
||||
}
|
||||
}
|
||||
|
||||
if (!startItem)
|
||||
return nsnull;
|
||||
|
||||
// perform a depth first search (preorder) of the docshell tree
|
||||
// looking for an HTML Frame or a chrome document
|
||||
nsIContent* content = nsnull;
|
||||
nsIContent* content = aStartContent;
|
||||
nsCOMPtr<nsIDocShellTreeItem> curItem = startItem;
|
||||
nsCOMPtr<nsIDocShellTreeItem> nextItem;
|
||||
do {
|
||||
if (aForward) {
|
||||
GetNextDocShell(curItem, getter_AddRefs(nextItem));
|
||||
if (!nextItem) {
|
||||
// wrap around to the beginning, which is the top of the tree
|
||||
startItem->GetRootTreeItem(getter_AddRefs(nextItem));
|
||||
}
|
||||
}
|
||||
else {
|
||||
GetPreviousDocShell(curItem, getter_AddRefs(nextItem));
|
||||
if (!nextItem) {
|
||||
// wrap around to the end, which is the last item in the tree
|
||||
nsCOMPtr<nsIDocShellTreeItem> rootItem;
|
||||
startItem->GetRootTreeItem(getter_AddRefs(rootItem));
|
||||
GetLastDocShell(rootItem, getter_AddRefs(nextItem));
|
||||
// If moving forward, check for a panel in the starting document. If one
|
||||
// exists with focusable content, return that content instead of the next
|
||||
// document. If currentPopup is set, then, another panel may exist. If no
|
||||
// such panel exists, then continue on to check the next document.
|
||||
// When moving backwards, and the starting content is in a panel, then
|
||||
// check for additional panels in the starting document. If the starting
|
||||
// content is not in a panel, move back to the previous document and check
|
||||
// for panels there.
|
||||
|
||||
bool checkPopups = false;
|
||||
nsCOMPtr<nsPIDOMWindow> nextFrame = nsnull;
|
||||
|
||||
if (doc && (aForward || currentPopup)) {
|
||||
nsIContent* popupContent = GetNextTabbablePanel(doc, currentPopup, aForward);
|
||||
if (popupContent)
|
||||
return popupContent;
|
||||
|
||||
if (!aForward && currentPopup) {
|
||||
// The starting content was in a popup, yet no other popups were
|
||||
// found. Move onto the starting content's document.
|
||||
nextFrame = doc->GetWindow();
|
||||
}
|
||||
}
|
||||
|
||||
curItem = nextItem;
|
||||
nsCOMPtr<nsPIDOMWindow> nextFrame = do_GetInterface(nextItem);
|
||||
// Look for the next or previous document.
|
||||
if (!nextFrame) {
|
||||
if (aForward) {
|
||||
GetNextDocShell(curItem, getter_AddRefs(nextItem));
|
||||
if (!nextItem) {
|
||||
// wrap around to the beginning, which is the top of the tree
|
||||
startItem->GetRootTreeItem(getter_AddRefs(nextItem));
|
||||
}
|
||||
}
|
||||
else {
|
||||
GetPreviousDocShell(curItem, getter_AddRefs(nextItem));
|
||||
if (!nextItem) {
|
||||
// wrap around to the end, which is the last item in the tree
|
||||
nsCOMPtr<nsIDocShellTreeItem> rootItem;
|
||||
startItem->GetRootTreeItem(getter_AddRefs(rootItem));
|
||||
GetLastDocShell(rootItem, getter_AddRefs(nextItem));
|
||||
}
|
||||
|
||||
// When going back to the previous document, check for any focusable
|
||||
// popups in that previous document first.
|
||||
checkPopups = true;
|
||||
}
|
||||
|
||||
curItem = nextItem;
|
||||
nextFrame = do_GetInterface(nextItem);
|
||||
}
|
||||
|
||||
if (!nextFrame)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(nextFrame->GetExtantDocument());
|
||||
if (doc && !doc->EventHandlingSuppressed()) {
|
||||
content = GetRootForFocus(nextFrame, doc, true, true);
|
||||
if (content && !GetRootForFocus(nextFrame, doc, false, false)) {
|
||||
// if the found content is in a chrome shell or a frameset, navigate
|
||||
// forward one tabbable item so that the first item is focused. Note
|
||||
// that we always go forward and not back here.
|
||||
nsCOMPtr<nsIContent> nextFocus;
|
||||
Element* rootElement = doc->GetRootElement();
|
||||
nsIPresShell* presShell = doc->GetShell();
|
||||
if (presShell) {
|
||||
nsresult rv = GetNextTabbableContent(presShell, rootElement,
|
||||
nsnull, rootElement,
|
||||
true, 1, false,
|
||||
getter_AddRefs(nextFocus));
|
||||
return NS_SUCCEEDED(rv) ? nextFocus.get() : nsnull;
|
||||
}
|
||||
// Clear currentPopup for the next iteration
|
||||
currentPopup = nsnull;
|
||||
|
||||
// If event handling is suppressed, move on to the next document. Set
|
||||
// content to null so that the popup check will be skipped on the next
|
||||
// loop iteration.
|
||||
doc = do_QueryInterface(nextFrame->GetExtantDocument());
|
||||
if (!doc || doc->EventHandlingSuppressed()) {
|
||||
content = nsnull;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkPopups) {
|
||||
// When iterating backwards, check the panels of the previous document
|
||||
// first. If a panel exists that has focusable content, focus that.
|
||||
// Otherwise, continue on to focus the document.
|
||||
nsIContent* popupContent = GetNextTabbablePanel(doc, nsnull, false);
|
||||
if (popupContent)
|
||||
return popupContent;
|
||||
}
|
||||
|
||||
content = GetRootForFocus(nextFrame, doc, true, true);
|
||||
if (content && !GetRootForFocus(nextFrame, doc, false, false)) {
|
||||
// if the found content is in a chrome shell or a frameset, navigate
|
||||
// forward one tabbable item so that the first item is focused. Note
|
||||
// that we always go forward and not back here.
|
||||
nsCOMPtr<nsIContent> nextFocus;
|
||||
Element* rootElement = doc->GetRootElement();
|
||||
nsIPresShell* presShell = doc->GetShell();
|
||||
if (presShell) {
|
||||
nsresult rv = GetNextTabbableContent(presShell, rootElement,
|
||||
nsnull, rootElement,
|
||||
true, 1, false,
|
||||
getter_AddRefs(nextFocus));
|
||||
return NS_SUCCEEDED(rv) ? nextFocus.get() : nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
} while (!content);
|
||||
|
||||
return content;
|
||||
|
@ -459,16 +459,31 @@ protected:
|
||||
nsIDocShellTreeItem** aResult);
|
||||
|
||||
/**
|
||||
* Get the tabbable next document from the currently focused frame if
|
||||
* aForward is true, or the previously tabbable document if aForward is
|
||||
* false. If this document is a chrome or frameset document, returns
|
||||
* the first focusable element within this document, otherwise, returns
|
||||
* the root node of the document.
|
||||
* Determine the first panel with focusable content in document tab order
|
||||
* from the given document. aForward indicates the direction to scan. If
|
||||
* aCurrentPopup is set to a panel, the next or previous popup after
|
||||
* aCurrentPopup after it is used. If aCurrentPopup is null, then the first
|
||||
* or last popup is used. If a panel has no focusable content, it is skipped.
|
||||
* Null is returned if no panel is open or no open panel contains a focusable
|
||||
* element.
|
||||
*/
|
||||
nsIContent* GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward);
|
||||
|
||||
/**
|
||||
* Get the tabbable next document from aStartContent or, if null, the
|
||||
* currently focused frame if aForward is true, or the previously tabbable
|
||||
* document if aForward is false. If this document is a chrome or frameset
|
||||
* document, returns the first focusable element within this document,
|
||||
* otherwise, returns the root node of the document.
|
||||
*
|
||||
*
|
||||
* Panels with focusable content are also placed in the cycling order, just
|
||||
* after the document containing that panel.
|
||||
*
|
||||
* This method would be used for document navigation, which is typically
|
||||
* invoked by pressing F6.
|
||||
*/
|
||||
nsIContent* GetNextTabbableDocument(bool aForward);
|
||||
nsIContent* GetNextTabbableDocument(nsIContent* aStartContent, bool aForward);
|
||||
|
||||
/**
|
||||
* Retreives a focusable element within the current selection of aWindow.
|
||||
|
@ -74,6 +74,8 @@ _TEST_FILES = \
|
||||
test_moving_xhr.xul \
|
||||
test_nodesFromRect.html \
|
||||
489127.html \
|
||||
test_focus_docnav.xul \
|
||||
window_focus_docnav.xul \
|
||||
$(NULL)
|
||||
|
||||
ifeq (WINNT,$(OS_ARCH))
|
||||
|
@ -3,7 +3,8 @@
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function () opener.framesetWindowLoaded(window));
|
||||
if (opener)
|
||||
SimpleTest.waitForFocus(function () opener.framesetWindowLoaded(window));
|
||||
</script>
|
||||
|
||||
<frameset rows="30%, 70%">
|
||||
|
28
dom/tests/mochitest/chrome/test_focus_docnav.xul
Normal file
28
dom/tests/mochitest/chrome/test_focus_docnav.xul
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window onload="runTest();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
function runTest()
|
||||
{
|
||||
window.open("window_focus_docnav.xul", "_blank", "chrome,width=600,height=550");
|
||||
}
|
||||
</script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display">
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
</window>
|
104
dom/tests/mochitest/chrome/window_focus_docnav.xul
Normal file
104
dom/tests/mochitest/chrome/window_focus_docnav.xul
Normal file
@ -0,0 +1,104 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
|
||||
<window onload="start()"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
|
||||
<textbox id="textbox"/>
|
||||
|
||||
<panel id="panel2" onpopupshown="runTests(this, 2);" onpopuphidden="document.getElementById('panel').hidePopup()">
|
||||
<textbox id="p2textbox" value="Popup2"/>
|
||||
</panel>
|
||||
<panel id="panel" onpopupshown="runTests(this, 1);"
|
||||
onpopuphidden="done()">
|
||||
<textbox id="p1textbox" value="Popup1"/>
|
||||
</panel>
|
||||
|
||||
<browser id="browser" type="content" src="focus_frameset.html" width="500" height="400"/>
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
var fm = Components.classes["@mozilla.org/focus-manager;1"].
|
||||
getService(Components.interfaces.nsIFocusManager);
|
||||
|
||||
function is(l, r, n) { window.opener.wrappedJSObject.SimpleTest.is(l,r,n); }
|
||||
function ok(v, n) { window.opener.wrappedJSObject.SimpleTest.ok(v,n); }
|
||||
|
||||
function done()
|
||||
{
|
||||
var opener = window.opener;
|
||||
window.close();
|
||||
opener.wrappedJSObject.SimpleTest.finish();
|
||||
}
|
||||
|
||||
function previous(expectedWindow, expectedElement, desc)
|
||||
{
|
||||
synthesizeKey("VK_F6", { shiftKey: true });
|
||||
is(fm.focusedWindow, expectedWindow, desc);
|
||||
is(fm.focusedElement, expectedElement, desc + " element");
|
||||
}
|
||||
|
||||
function next(expectedWindow, expectedElement, desc)
|
||||
{
|
||||
synthesizeKey("VK_F6", { });
|
||||
is(fm.focusedWindow, expectedWindow, desc);
|
||||
is(fm.focusedElement, expectedElement, desc + " element" + "::" + (fm.focusedElement ? fm.focusedElement.parentNode.id : "<none>"));
|
||||
}
|
||||
|
||||
// This test runs through three cases. Document navigation forward and
|
||||
// backward using the F6 key when no popups are open, with one popup open and
|
||||
// with two popups open.
|
||||
function runTests(panel, popupCount)
|
||||
{
|
||||
if (!popupCount || popupCount > 2)
|
||||
popupCount = 0;
|
||||
|
||||
fm.clearFocus(window);
|
||||
|
||||
var childwin = document.getElementById("browser").contentWindow;
|
||||
|
||||
if (popupCount) {
|
||||
if (popupCount == 2) {
|
||||
next(window, document.getElementById("p2textbox").inputField, "First into popup 2 with " + popupCount);
|
||||
}
|
||||
|
||||
next(window, document.getElementById("p1textbox").inputField, "First into popup 1 with " + popupCount);
|
||||
}
|
||||
|
||||
next(childwin.frames[0], childwin.frames[0].document.documentElement, "First with " + popupCount);
|
||||
next(childwin.frames[1], childwin.frames[1].document.documentElement, "Second with " + popupCount);
|
||||
previous(childwin.frames[0], childwin.frames[0].document.documentElement, "Second back with " + popupCount);
|
||||
|
||||
if (popupCount) {
|
||||
previous(window, document.getElementById("p1textbox").inputField, "First back from popup 1 with " + popupCount);
|
||||
|
||||
if (popupCount == 2) {
|
||||
previous(window, document.getElementById("p2textbox").inputField, "First back from popup 2 with " + popupCount);
|
||||
}
|
||||
}
|
||||
|
||||
previous(window, document.getElementById("textbox").inputField, "First back with " + popupCount);
|
||||
|
||||
if (panel == document.getElementById("panel"))
|
||||
document.getElementById("panel2").openPopup(null, "after_start", 100, 20);
|
||||
else if (panel == document.getElementById("panel2"))
|
||||
panel.hidePopup();
|
||||
else
|
||||
document.getElementById("panel").openPopup(null, "after_start");
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
window.opener.wrappedJSObject.SimpleTest.waitForExplicitFinish();
|
||||
window.opener.wrappedJSObject.SimpleTest.waitForFocus(
|
||||
function() { runTests(null, 0); },
|
||||
document.getElementById("browser").contentWindow);
|
||||
}
|
||||
|
||||
]]></script>
|
||||
|
||||
</window>
|
Loading…
Reference in New Issue
Block a user