mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-04-03 04:52:54 +00:00
Bug 1132518, make document navigation with F6/Shift+F6 work in e10s. This combines the document and tab navigation mechanisms together, r=smaug
This commit is contained in:
parent
bf46cf123d
commit
eee10c99f2
@ -2804,16 +2804,16 @@ nsDocShell::GetBusyFlags(uint32_t* aBusyFlags)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::TabToTreeOwner(bool aForward, bool* aTookFocus)
|
||||
nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation, bool* aTookFocus)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aTookFocus);
|
||||
|
||||
nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
|
||||
if (chromeFocus) {
|
||||
if (aForward) {
|
||||
*aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement());
|
||||
*aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
|
||||
} else {
|
||||
*aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement());
|
||||
*aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
|
||||
}
|
||||
} else {
|
||||
*aTookFocus = false;
|
||||
|
@ -46,7 +46,7 @@ interface nsITabParent;
|
||||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
|
||||
[scriptable, builtinclass, uuid(b3137b7c-d589-48aa-b89b-e02aa451d42c)]
|
||||
[scriptable, builtinclass, uuid(97471054-0BC8-4A57-BBFE-6C255BCBBB67)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
@ -347,9 +347,12 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
/*
|
||||
* Tells the docshell to offer focus to its tree owner.
|
||||
* This is currently only necessary for embedding chrome.
|
||||
* If forDocumentNavigation is true, then document navigation should be
|
||||
* performed, where only the root of documents are selected. Otherwise, the
|
||||
* next element in the parent should be returned. Returns true if focus was
|
||||
* successfully taken by the tree owner.
|
||||
*/
|
||||
void tabToTreeOwner(in boolean forward,
|
||||
out boolean tookFocus);
|
||||
bool tabToTreeOwner(in boolean forward, in boolean forDocumentNavigation);
|
||||
|
||||
/**
|
||||
* Current busy state for DocShell
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMRange.h"
|
||||
@ -33,6 +34,7 @@
|
||||
#include "nsFrameSelection.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "nsXULPopupManager.h"
|
||||
#include "nsMenuPopupFrame.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIObserverService.h"
|
||||
@ -41,6 +43,7 @@
|
||||
#include "nsStyleCoord.h"
|
||||
#include "SelectionCarets.h"
|
||||
#include "TabChild.h"
|
||||
#include "nsFrameLoader.h"
|
||||
|
||||
#include "mozilla/ContentEvents.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
@ -521,6 +524,10 @@ nsFocusManager::MoveFocus(nsIDOMWindow* aWindow, nsIDOMElement* aStartElement,
|
||||
nsCOMPtr<nsIContent> newFocus;
|
||||
nsresult rv = DetermineElementToMoveFocus(window, startContent, aType, noParentTraversal,
|
||||
getter_AddRefs(newFocus));
|
||||
if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get());
|
||||
@ -2422,9 +2429,19 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
{
|
||||
*aNextContent = nullptr;
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
||||
if (!docShell)
|
||||
return NS_OK;
|
||||
// True if we are navigating by document (F6/Shift+F6) or false if we are
|
||||
// navigating by element (Tab/Shift+Tab).
|
||||
bool forDocumentNavigation = false;
|
||||
|
||||
// This is used for document navigation only. It will be set to true if we
|
||||
// start navigating from a starting point. If this starting point is near the
|
||||
// end of the document (for example, an element on a statusbar), and there
|
||||
// are no child documents or panels before the end of the document, then we
|
||||
// will need to ensure that we don't consider the root chrome window when we
|
||||
// loop around and instead find the next child document/panel, as focus is
|
||||
// already in that window. This flag will be cleared once we navigate into
|
||||
// another document.
|
||||
bool mayFocusRoot = (aStartContent != nullptr);
|
||||
|
||||
nsCOMPtr<nsIContent> startContent = aStartContent;
|
||||
if (!startContent && aType != MOVEFOCUS_CARET) {
|
||||
@ -2450,19 +2467,24 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
LookAndFeel::GetInt(LookAndFeel::eIntID_TabFocusModel,
|
||||
&nsIContent::sTabFocusModel);
|
||||
|
||||
if (aType == MOVEFOCUS_ROOT) {
|
||||
// These types are for document navigation using F6.
|
||||
if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC ||
|
||||
aType == MOVEFOCUS_FIRSTDOC || aType == MOVEFOCUS_LASTDOC) {
|
||||
forDocumentNavigation = true;
|
||||
}
|
||||
|
||||
// If moving to the root or first document, find the root element and return.
|
||||
if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_FIRSTDOC) {
|
||||
NS_IF_ADDREF(*aNextContent = GetRootForFocus(aWindow, doc, false, false));
|
||||
return NS_OK;
|
||||
if (!*aNextContent && aType == MOVEFOCUS_FIRSTDOC) {
|
||||
// When looking for the first document, if the root wasn't focusable,
|
||||
// find the next focusable document.
|
||||
aType = MOVEFOCUS_FORWARDDOC;
|
||||
} else {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
if (aType == MOVEFOCUS_FORWARDDOC) {
|
||||
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, true));
|
||||
return NS_OK;
|
||||
}
|
||||
if (aType == MOVEFOCUS_BACKWARDDOC) {
|
||||
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, false));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsIContent* rootContent = doc->GetRootElement();
|
||||
NS_ENSURE_TRUE(rootContent, NS_OK);
|
||||
|
||||
@ -2474,17 +2496,19 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
startContent = rootContent;
|
||||
return GetNextTabbableContent(presShell, startContent,
|
||||
nullptr, startContent,
|
||||
true, 1, false, aNextContent);
|
||||
true, 1, false, false, aNextContent);
|
||||
}
|
||||
if (aType == MOVEFOCUS_LAST) {
|
||||
if (!aStartContent)
|
||||
startContent = rootContent;
|
||||
return GetNextTabbableContent(presShell, startContent,
|
||||
nullptr, startContent,
|
||||
false, 0, false, aNextContent);
|
||||
false, 0, false, false, aNextContent);
|
||||
}
|
||||
|
||||
bool forward = (aType == MOVEFOCUS_FORWARD || aType == MOVEFOCUS_CARET);
|
||||
bool forward = (aType == MOVEFOCUS_FORWARD ||
|
||||
aType == MOVEFOCUS_FORWARDDOC ||
|
||||
aType == MOVEFOCUS_CARET);
|
||||
bool doNavigation = true;
|
||||
bool ignoreTabIndex = false;
|
||||
// when a popup is open, we want to ensure that tab navigation occurs only
|
||||
@ -2519,7 +2543,7 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
nsGkAtoms::menuPopupFrame);
|
||||
}
|
||||
|
||||
if (popupFrame) {
|
||||
if (popupFrame && !forDocumentNavigation) {
|
||||
// Don't navigate outside of a popup, so pretend that the
|
||||
// root content is the popup itself
|
||||
rootContent = popupFrame->GetContent();
|
||||
@ -2551,13 +2575,24 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
}
|
||||
#endif
|
||||
if (popupFrame) {
|
||||
rootContent = popupFrame->GetContent();
|
||||
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
|
||||
startContent = rootContent;
|
||||
// When there is a popup open, and no starting content, start the search
|
||||
// at the topmost popup.
|
||||
startContent = popupFrame->GetContent();
|
||||
NS_ASSERTION(startContent, "Popup frame doesn't have a content node");
|
||||
// Unless we are searching for documents, set the root content to the
|
||||
// popup as well, so that we don't tab-navigate outside the popup.
|
||||
// When navigating by documents, we start at the popup but can navigate
|
||||
// outside of it to look for other panels and documents.
|
||||
if (!forDocumentNavigation) {
|
||||
rootContent = startContent;
|
||||
}
|
||||
|
||||
doc = startContent ? startContent->GetComposedDoc() : nullptr;
|
||||
}
|
||||
else {
|
||||
// Otherwise, for content shells, start from the location of the caret.
|
||||
if (docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
|
||||
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
||||
if (docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
|
||||
nsCOMPtr<nsIContent> endSelectionContent;
|
||||
GetSelectionLocation(doc, presShell,
|
||||
getter_AddRefs(startContent),
|
||||
@ -2611,7 +2646,8 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
nsIContent* originalStartContent = startContent;
|
||||
|
||||
LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent.get());
|
||||
LOGFOCUSNAVIGATION((" Tabindex: %d Ignore: %d", tabIndex, ignoreTabIndex));
|
||||
LOGFOCUSNAVIGATION((" Forward: %d Tabindex: %d Ignore: %d DocNav: %d",
|
||||
forward, tabIndex, ignoreTabIndex, forDocumentNavigation));
|
||||
|
||||
while (doc) {
|
||||
if (doNavigation) {
|
||||
@ -2620,22 +2656,29 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
skipOriginalContentCheck ? nullptr : originalStartContent,
|
||||
startContent, forward,
|
||||
tabIndex, ignoreTabIndex,
|
||||
forDocumentNavigation,
|
||||
getter_AddRefs(nextFocus));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
|
||||
// Navigation was redirected to a child process, so just return.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// found a content node to focus.
|
||||
if (nextFocus) {
|
||||
LOGCONTENTNAVIGATION("Next Content: %s", nextFocus.get());
|
||||
|
||||
// as long as the found node was not the same as the starting node,
|
||||
// set it as the return value.
|
||||
if (nextFocus != originalStartContent) {
|
||||
// set it as the return value. For document navigation, we can return
|
||||
// the same element in case there is only one content node that could
|
||||
// be returned, for example, in a child process document.
|
||||
if (nextFocus != originalStartContent || forDocumentNavigation) {
|
||||
nextFocus.forget(aNextContent);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (popupFrame) {
|
||||
if (popupFrame && !forDocumentNavigation) {
|
||||
// in a popup, so start again from the beginning of the popup. However,
|
||||
// if we already started at the beginning, then there isn't anything to
|
||||
// focus, so just return
|
||||
@ -2649,7 +2692,7 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
}
|
||||
|
||||
doNavigation = true;
|
||||
skipOriginalContentCheck = false;
|
||||
skipOriginalContentCheck = forDocumentNavigation;
|
||||
ignoreTabIndex = false;
|
||||
|
||||
if (aNoParentTraversal) {
|
||||
@ -2661,46 +2704,45 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
continue;
|
||||
}
|
||||
|
||||
// reached the beginning or end of the document. Traverse up to the parent
|
||||
// document and try again.
|
||||
nsCOMPtr<nsIDocShellTreeItem> docShellParent;
|
||||
docShell->GetParent(getter_AddRefs(docShellParent));
|
||||
if (docShellParent) {
|
||||
// move up to the parent shell and try again from there.
|
||||
// Reached the beginning or end of the document. Next, navigate up to the
|
||||
// parent document and try again.
|
||||
nsCOMPtr<nsPIDOMWindow> piWindow = doc->GetWindow();
|
||||
NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE);
|
||||
|
||||
// first, get the frame element this window is inside.
|
||||
nsCOMPtr<nsPIDOMWindow> piWindow = docShell->GetWindow();
|
||||
NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE);
|
||||
nsCOMPtr<nsIDocShell> docShell = piWindow->GetDocShell();
|
||||
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
||||
|
||||
// Next, retrieve the parent docshell, document and presshell.
|
||||
docShell = do_QueryInterface(docShellParent);
|
||||
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> piParentWindow = docShellParent->GetWindow();
|
||||
NS_ENSURE_TRUE(piParentWindow, NS_ERROR_FAILURE);
|
||||
doc = piParentWindow->GetExtantDoc();
|
||||
// Get the frame element this window is inside and, from that, get the
|
||||
// parent document and presshell. If there is no enclosing frame element,
|
||||
// then this is a top-level, embedded or remote window.
|
||||
startContent = piWindow->GetFrameElementInternal();
|
||||
if (startContent) {
|
||||
doc = startContent->GetComposedDoc();
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||
|
||||
rootContent = doc->GetRootElement();
|
||||
presShell = doc->GetShell();
|
||||
|
||||
rootContent = doc->GetRootElement();
|
||||
startContent = piWindow->GetFrameElementInternal();
|
||||
if (startContent) {
|
||||
nsIFrame* frame = startContent->GetPrimaryFrame();
|
||||
if (!frame)
|
||||
return NS_OK;
|
||||
// We can focus the root element now that we have moved to another document.
|
||||
mayFocusRoot = true;
|
||||
|
||||
frame->IsFocusable(&tabIndex, 0);
|
||||
if (tabIndex < 0) {
|
||||
tabIndex = 1;
|
||||
ignoreTabIndex = true;
|
||||
}
|
||||
nsIFrame* frame = startContent->GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// if the frame is inside a popup, make sure to scan only within the
|
||||
// popup. This handles the situation of tabbing amongst elements
|
||||
// inside an iframe which is itself inside a popup. Otherwise,
|
||||
// navigation would move outside the popup when tabbing outside the
|
||||
// iframe.
|
||||
frame->IsFocusable(&tabIndex, 0);
|
||||
if (tabIndex < 0) {
|
||||
tabIndex = 1;
|
||||
ignoreTabIndex = true;
|
||||
}
|
||||
|
||||
// if the frame is inside a popup, make sure to scan only within the
|
||||
// popup. This handles the situation of tabbing amongst elements
|
||||
// inside an iframe which is itself inside a popup. Otherwise,
|
||||
// navigation would move outside the popup when tabbing outside the
|
||||
// iframe.
|
||||
if (!forDocumentNavigation) {
|
||||
popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
|
||||
nsGkAtoms::menuPopupFrame);
|
||||
if (popupFrame) {
|
||||
@ -2708,17 +2750,13 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
|
||||
}
|
||||
}
|
||||
else {
|
||||
startContent = rootContent;
|
||||
tabIndex = forward ? 1 : 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no parent, so call the tree owner. This will tell the embedder that
|
||||
// it should take the focus.
|
||||
// There is no parent, so call the tree owner. This will tell the
|
||||
// embedder or parent process that it should take the focus.
|
||||
bool tookFocus;
|
||||
docShell->TabToTreeOwner(forward, &tookFocus);
|
||||
// if the tree owner, took the focus, blur the current content
|
||||
docShell->TabToTreeOwner(forward, forDocumentNavigation, &tookFocus);
|
||||
// If the tree owner took the focus, blur the current content.
|
||||
if (tookFocus) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = docShell->GetWindow();
|
||||
if (window->GetFocusedNode() == mFocusedContent)
|
||||
@ -2728,6 +2766,23 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we have reached the end of the top-level document, focus the
|
||||
// first element in the top-level document. This should always happen
|
||||
// when navigating by document forwards but when navigating backwards,
|
||||
// only do this if we started in another document or within a popup frame.
|
||||
// If the focus started in this window outside a popup however, we should
|
||||
// continue by looping around to the end again.
|
||||
if (forDocumentNavigation && (forward || mayFocusRoot || popupFrame)) {
|
||||
// HTML content documents can have their root element focused (a focus
|
||||
// ring appears around the entire content area frame). This root
|
||||
// appears in the tab order before all of the elements in the document.
|
||||
// Chrome documents however cannot be focused directly, so instead we
|
||||
// focus the first focusable element within the window.
|
||||
// For example, the urlbar.
|
||||
nsIContent* root = GetRootForFocus(piWindow, doc, true, true);
|
||||
return FocusFirst(root, aNextContent);
|
||||
}
|
||||
|
||||
// reset the tab index and start again from the beginning or end
|
||||
startContent = rootContent;
|
||||
tabIndex = forward ? 1 : 0;
|
||||
@ -2750,6 +2805,7 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation,
|
||||
nsIContent** aResultContent)
|
||||
{
|
||||
*aResultContent = nullptr;
|
||||
@ -2786,6 +2842,9 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
continue;
|
||||
}
|
||||
|
||||
// For tab navigation, pass false for aSkipPopupChecks so that we don't
|
||||
// iterate into or out of a popup. For document naviation pass true to
|
||||
// ignore these boundaries.
|
||||
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
||||
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
||||
presContext, startFrame,
|
||||
@ -2793,7 +2852,7 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
false, // aVisual
|
||||
false, // aLockInScrollView
|
||||
true, // aFollowOOFs
|
||||
false // aSkipPopupChecks
|
||||
aForDocumentNavigation // aSkipPopupChecks
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -2818,6 +2877,51 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
// Walk frames to find something tabbable matching mCurrentTabIndex
|
||||
nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
while (frame) {
|
||||
nsIContent* currentContent = frame->GetContent();
|
||||
|
||||
// For document navigation, check if this element is an open panel. Since
|
||||
// panels aren't focusable (tabIndex would be -1), we'll just assume that
|
||||
// for document navigation, the tabIndex is 0.
|
||||
if (aForDocumentNavigation && currentContent && (aCurrentTabIndex == 0) &&
|
||||
currentContent->IsXULElement(nsGkAtoms::panel)) {
|
||||
nsMenuPopupFrame* popupFrame = do_QueryFrame(frame);
|
||||
// Check if the panel is open. Closed panels are ignored since you can't
|
||||
// focus anything in them.
|
||||
if (popupFrame && popupFrame->IsOpen()) {
|
||||
// When moving backward, skip the popup we started in otherwise it
|
||||
// will be selected again.
|
||||
bool validPopup = true;
|
||||
if (!aForward) {
|
||||
nsIContent* content = aStartContent;
|
||||
while (content) {
|
||||
if (content == currentContent) {
|
||||
validPopup = false;
|
||||
break;
|
||||
}
|
||||
|
||||
content = content->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
if (validPopup) {
|
||||
// Since a panel isn't focusable itself, find the first focusable
|
||||
// content within the popup. If there isn't any focusable content
|
||||
// in the popup, skip this popup and continue iterating through the
|
||||
// frames. We pass the panel itself (currentContent) as the starting
|
||||
// and root content, so that we only find content within the panel.
|
||||
// Note also that we pass false for aForDocumentNavigation since we
|
||||
// want to locate the first content, not the first document.
|
||||
rv = GetNextTabbableContent(aPresShell, currentContent,
|
||||
nullptr, currentContent,
|
||||
true, 1, false, false,
|
||||
aResultContent);
|
||||
if (NS_SUCCEEDED(rv) && *aResultContent) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TabIndex not set defaults to 0 for form elements, anchors and other
|
||||
// elements that are normally focusable. Tabindex defaults to -1
|
||||
// for elements that are not normally focusable.
|
||||
@ -2825,17 +2929,16 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
// < 0 not tabbable at all
|
||||
// == 0 in normal tab order (last after positive tabindexed items)
|
||||
// > 0 can be tabbed to in the order specified by this value
|
||||
|
||||
int32_t tabIndex;
|
||||
frame->IsFocusable(&tabIndex, 0);
|
||||
|
||||
LOGCONTENTNAVIGATION("Next Tabbable %s:", frame->GetContent());
|
||||
LOGFOCUSNAVIGATION((" with tabindex: %d expected: %d", tabIndex, aCurrentTabIndex));
|
||||
|
||||
nsIContent* currentContent = frame->GetContent();
|
||||
if (tabIndex >= 0) {
|
||||
NS_ASSERTION(currentContent, "IsFocusable set a tabindex for a frame with no content");
|
||||
if (currentContent->IsHTMLElement(nsGkAtoms::img) &&
|
||||
if (!aForDocumentNavigation &&
|
||||
currentContent->IsHTMLElement(nsGkAtoms::img) &&
|
||||
currentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usemap)) {
|
||||
// This is an image with a map. Image map areas are not traversed by
|
||||
// nsIFrameTraversal so look for the next or previous area element.
|
||||
@ -2854,29 +2957,50 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// found a node with a matching tab index. Check if it is a child
|
||||
// frame. If so, navigate into the child frame instead.
|
||||
nsIDocument* doc = currentContent->GetComposedDoc();
|
||||
NS_ASSERTION(doc, "content not in document");
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
|
||||
if (subdoc) {
|
||||
if (!subdoc->EventHandlingSuppressed()) {
|
||||
bool checkSubDocument = true;
|
||||
if (aForDocumentNavigation) {
|
||||
// If this is a remote child browser, call NavigateDocument to have
|
||||
// the child process continue the navigation. Return a special error
|
||||
// code to have the caller return early. If the child ends up not
|
||||
// being focusable in some way, the child process will call back
|
||||
// into document navigation again by calling MoveFocus.
|
||||
TabParent* remote = TabParent::GetFrom(currentContent);
|
||||
if (remote) {
|
||||
remote->NavigateDocument(aForward);
|
||||
return NS_SUCCESS_DOM_NO_OPERATION;
|
||||
}
|
||||
|
||||
// Next, check if this a non-remote child document.
|
||||
nsIContent* docRoot = GetRootForChildDocument(currentContent);
|
||||
if (docRoot) {
|
||||
// If GetRootForChildDocument returned something then call
|
||||
// FocusFirst to find the root or first element to focus within
|
||||
// the child document. If this is a frameset though, skip this and
|
||||
// fall through to the checkSubDocument block below to iterate into
|
||||
// the frameset's frames and locate the first focusable frame.
|
||||
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
|
||||
return FocusFirst(docRoot, aResultContent);
|
||||
}
|
||||
} else {
|
||||
// Set checkSubDocument to false, as this was neither a frame
|
||||
// type element or a child document that was focusable.
|
||||
checkSubDocument = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkSubDocument) {
|
||||
// found a node with a matching tab index. Check if it is a child
|
||||
// frame. If so, navigate into the child frame instead.
|
||||
nsIDocument* doc = currentContent->GetComposedDoc();
|
||||
NS_ASSERTION(doc, "content not in document");
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
|
||||
if (subdoc && !subdoc->EventHandlingSuppressed()) {
|
||||
if (aForward) {
|
||||
// when tabbing forward into a frame, return the root
|
||||
// frame so that the canvas becomes focused.
|
||||
nsCOMPtr<nsPIDOMWindow> subframe = subdoc->GetWindow();
|
||||
if (subframe) {
|
||||
// If the subframe body is editable by contenteditable,
|
||||
// we should set the editor's root element rather than the
|
||||
// actual root element. Otherwise, we should set the focus
|
||||
// to the root content.
|
||||
*aResultContent =
|
||||
nsLayoutUtils::GetEditableRootContentByContentEditable(subdoc);
|
||||
if (!*aResultContent ||
|
||||
!((*aResultContent)->GetPrimaryFrame())) {
|
||||
*aResultContent =
|
||||
GetRootForFocus(subframe, subdoc, false, true);
|
||||
}
|
||||
*aResultContent = GetRootForFocus(subframe, subdoc, false, true);
|
||||
if (*aResultContent) {
|
||||
NS_ADDREF(*aResultContent);
|
||||
return NS_OK;
|
||||
@ -2889,29 +3013,29 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
rv = GetNextTabbableContent(subShell, rootElement,
|
||||
aOriginalStartContent, rootElement,
|
||||
aForward, (aForward ? 1 : 0),
|
||||
false, aResultContent);
|
||||
false, aForDocumentNavigation, aResultContent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (*aResultContent)
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise, use this as the next content node to tab to, unless
|
||||
// this was the element we started on. This would happen for
|
||||
// instance on an element with child frames, where frame navigation
|
||||
// could return the original element again. In that case, just skip
|
||||
// it. Also, if the next content node is the root content, then
|
||||
// return it. This latter case would happen only if someone made a
|
||||
// popup focusable.
|
||||
// Also, when going backwards, check to ensure that the focus
|
||||
// wouldn't be redirected. Otherwise, for example, when an input in
|
||||
// a textbox is focused, the enclosing textbox would be found and
|
||||
// the same inner input would be returned again.
|
||||
else if (currentContent == aRootContent ||
|
||||
(currentContent != startContent &&
|
||||
(aForward || !GetRedirectedFocus(currentContent)))) {
|
||||
NS_ADDREF(*aResultContent = currentContent);
|
||||
return NS_OK;
|
||||
// otherwise, use this as the next content node to tab to, unless
|
||||
// this was the element we started on. This would happen for
|
||||
// instance on an element with child frames, where frame navigation
|
||||
// could return the original element again. In that case, just skip
|
||||
// it. Also, if the next content node is the root content, then
|
||||
// return it. This latter case would happen only if someone made a
|
||||
// popup focusable.
|
||||
// Also, when going backwards, check to ensure that the focus
|
||||
// wouldn't be redirected. Otherwise, for example, when an input in
|
||||
// a textbox is focused, the enclosing textbox would be found and
|
||||
// the same inner input would be returned again.
|
||||
else if (currentContent == aRootContent ||
|
||||
(currentContent != startContent &&
|
||||
(aForward || !GetRedirectedFocus(currentContent)))) {
|
||||
NS_ADDREF(*aResultContent = currentContent);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2944,13 +3068,14 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
// A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
|
||||
if (aCurrentTabIndex == (aForward ? 0 : 1)) {
|
||||
// if going backwards, the canvas should be focused once the beginning
|
||||
// has been reached.
|
||||
// has been reached, so get the root element.
|
||||
if (!aForward) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetCurrentWindow(aRootContent);
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
||||
NS_IF_ADDREF(*aResultContent =
|
||||
GetRootForFocus(window, aRootContent->GetComposedDoc(),
|
||||
false, true));
|
||||
|
||||
nsCOMPtr<nsIContent> docRoot =
|
||||
GetRootForFocus(window, aRootContent->GetComposedDoc(), false, true);
|
||||
FocusFirst(docRoot, aResultContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3056,26 +3181,41 @@ nsFocusManager::GetNextTabIndex(nsIContent* aParent,
|
||||
return tabIndex;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFocusManager::FocusFirst(nsIContent* aRootContent, nsIContent** aNextContent)
|
||||
{
|
||||
if (!aRootContent) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIDocument* doc = aRootContent->GetComposedDoc();
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
|
||||
if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
|
||||
// If the found content is in a chrome shell, navigate forward one
|
||||
// tabbable item so that the first item is focused. Note that we
|
||||
// always go forward and not back here.
|
||||
nsIPresShell* presShell = doc->GetShell();
|
||||
if (presShell) {
|
||||
return GetNextTabbableContent(presShell, aRootContent,
|
||||
nullptr, aRootContent,
|
||||
true, 1, false, false,
|
||||
aNextContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_ADDREF(*aNextContent = aRootContent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
|
||||
nsIDocument* aDocument,
|
||||
bool aIsForDocNavigation,
|
||||
bool aForDocumentNavigation,
|
||||
bool aCheckVisibility)
|
||||
{
|
||||
// the root element's canvas may be focused as long as the document is in a
|
||||
// a non-chrome shell and does not contain a frameset.
|
||||
if (aIsForDocNavigation) {
|
||||
nsCOMPtr<Element> docElement = aWindow->GetFrameElementInternal();
|
||||
// document navigation skips iframes and frames that are specifically non-focusable
|
||||
if (docElement) {
|
||||
if (docElement->NodeInfo()->NameAtom() == nsGkAtoms::iframe)
|
||||
return nullptr;
|
||||
|
||||
nsIFrame* frame = docElement->GetPrimaryFrame();
|
||||
if (!frame || !frame->IsFocusable(nullptr, 0))
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (!aForDocumentNavigation) {
|
||||
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
||||
if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
|
||||
return nullptr;
|
||||
@ -3085,9 +3225,15 @@ nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
|
||||
if (aCheckVisibility && !IsWindowVisible(aWindow))
|
||||
return nullptr;
|
||||
|
||||
Element *rootElement = aDocument->GetRootElement();
|
||||
if (!rootElement) {
|
||||
return nullptr;
|
||||
// If the body is contenteditable, use the editor's root element rather than
|
||||
// the actual root element.
|
||||
nsCOMPtr<nsIContent> rootElement =
|
||||
nsLayoutUtils::GetEditableRootContentByContentEditable(aDocument);
|
||||
if (!rootElement || !rootElement->GetPrimaryFrame()) {
|
||||
rootElement = aDocument->GetRootElement();
|
||||
if (!rootElement) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (aCheckVisibility && !rootElement->GetPrimaryFrame()) {
|
||||
@ -3096,291 +3242,43 @@ nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
|
||||
|
||||
// Finally, check if this is a frameset
|
||||
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
|
||||
if (htmlDoc && aDocument->GetHtmlChildElement(nsGkAtoms::frameset)) {
|
||||
return nullptr;
|
||||
if (htmlDoc) {
|
||||
nsIContent* htmlChild = aDocument->GetHtmlChildElement(nsGkAtoms::frameset);
|
||||
if (htmlChild) {
|
||||
// In document navigation mode, return the frameset so that navigation
|
||||
// descends into the child frames.
|
||||
return aForDocumentNavigation ? htmlChild : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return rootElement;
|
||||
}
|
||||
|
||||
void
|
||||
nsFocusManager::GetLastDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
|
||||
while (curItem) {
|
||||
int32_t childCount = 0;
|
||||
curItem->GetChildCount(&childCount);
|
||||
if (!childCount) {
|
||||
curItem.forget(aResult);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
curItem->GetChildAt(childCount - 1, getter_AddRefs(curItem));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsFocusManager::GetNextDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
|
||||
int32_t childCount = 0;
|
||||
aItem->GetChildCount(&childCount);
|
||||
if (childCount) {
|
||||
aItem->GetChildAt(0, aResult);
|
||||
if (*aResult)
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
|
||||
while (curItem) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentItem;
|
||||
curItem->GetParent(getter_AddRefs(parentItem));
|
||||
if (!parentItem)
|
||||
return;
|
||||
|
||||
// Note that we avoid using GetChildOffset() here because docshell
|
||||
// child offsets can't be trusted to be correct. bug 162283.
|
||||
nsCOMPtr<nsIDocShellTreeItem> iterItem;
|
||||
childCount = 0;
|
||||
parentItem->GetChildCount(&childCount);
|
||||
for (int32_t index = 0; index < childCount; ++index) {
|
||||
parentItem->GetChildAt(index, getter_AddRefs(iterItem));
|
||||
if (iterItem == curItem) {
|
||||
++index;
|
||||
if (index < childCount) {
|
||||
parentItem->GetChildAt(index, aResult);
|
||||
if (*aResult)
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
curItem = parentItem;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentItem;
|
||||
aItem->GetParent(getter_AddRefs(parentItem));
|
||||
if (!parentItem)
|
||||
return;
|
||||
|
||||
// Note that we avoid using GetChildOffset() here because docshell
|
||||
// child offsets can't be trusted to be correct. bug 162283.
|
||||
int32_t childCount = 0;
|
||||
parentItem->GetChildCount(&childCount);
|
||||
nsCOMPtr<nsIDocShellTreeItem> prevItem, iterItem;
|
||||
for (int32_t index = 0; index < childCount; ++index) {
|
||||
parentItem->GetChildAt(index, getter_AddRefs(iterItem));
|
||||
if (iterItem == aItem)
|
||||
break;
|
||||
prevItem = iterItem;
|
||||
}
|
||||
|
||||
if (prevItem)
|
||||
GetLastDocShell(prevItem, aResult);
|
||||
else
|
||||
parentItem.forget(aResult);
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward)
|
||||
nsFocusManager::GetRootForChildDocument(nsIContent* aContent)
|
||||
{
|
||||
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||
if (!pm)
|
||||
// Check for elements that represent child documents, that is, browsers,
|
||||
// editors or frames from a frameset. We don't include iframes since we
|
||||
// consider them to be an integral part of the same window or page.
|
||||
if (!aContent ||
|
||||
!(aContent->IsXULElement(nsGkAtoms::browser) ||
|
||||
aContent->IsXULElement(nsGkAtoms::editor) ||
|
||||
aContent->IsHTMLElement(nsGkAtoms::frame))) {
|
||||
return nullptr;
|
||||
|
||||
// Iterate through the array backwards if aForward is false.
|
||||
nsTArray<nsIFrame *> popups;
|
||||
pm->GetVisiblePopups(popups);
|
||||
int32_t i = aForward ? 0 : popups.Length() - 1;
|
||||
int32_t 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 = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip over non-panels
|
||||
if (!popupFrame->GetContent()->IsXULElement(nsGkAtoms::panel) ||
|
||||
(aDocument && popupFrame->GetContent()->GetComposedDoc() != 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,
|
||||
nullptr, popup,
|
||||
true, 1, false,
|
||||
getter_AddRefs(nextFocus));
|
||||
if (NS_SUCCEEDED(rv) && nextFocus) {
|
||||
return nextFocus.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableDocument(nsIContent* aStartContent, bool aForward)
|
||||
{
|
||||
// If currentPopup is set, then the starting content is in a panel.
|
||||
nsIFrame* currentPopup = nullptr;
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
nsCOMPtr<nsIDocShell> startDocShell;
|
||||
|
||||
if (aStartContent) {
|
||||
doc = aStartContent->GetComposedDoc();
|
||||
if (doc) {
|
||||
startDocShell = 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) {
|
||||
startDocShell = mFocusedWindow->GetDocShell();
|
||||
doc = mFocusedWindow->GetExtantDoc();
|
||||
} else if (mActiveWindow) {
|
||||
startDocShell = mActiveWindow->GetDocShell();
|
||||
doc = mActiveWindow->GetExtantDoc();
|
||||
}
|
||||
|
||||
if (!startDocShell)
|
||||
nsIDocument* doc = aContent->GetComposedDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// perform a depth first search (preorder) of the docshell tree
|
||||
// looking for an HTML Frame or a chrome document
|
||||
nsIContent* content = aStartContent;
|
||||
nsCOMPtr<nsIDocShellTreeItem> curItem = startDocShell.get();
|
||||
nsCOMPtr<nsIDocShellTreeItem> nextItem;
|
||||
do {
|
||||
// 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.
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
|
||||
if (!subdoc || subdoc->EventHandlingSuppressed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool checkPopups = false;
|
||||
nsCOMPtr<nsPIDOMWindow> nextFrame = nullptr;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
startDocShell->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;
|
||||
startDocShell->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 = nextItem ? nextItem->GetWindow() : nullptr;
|
||||
}
|
||||
|
||||
if (!nextFrame)
|
||||
return nullptr;
|
||||
|
||||
// Clear currentPopup for the next iteration
|
||||
currentPopup = nullptr;
|
||||
|
||||
// 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 = nextFrame->GetExtantDoc();
|
||||
if (!doc || doc->EventHandlingSuppressed()) {
|
||||
content = nullptr;
|
||||
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, nullptr, 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,
|
||||
nullptr, rootElement,
|
||||
true, 1, false,
|
||||
getter_AddRefs(nextFocus));
|
||||
return NS_SUCCEEDED(rv) ? nextFocus.get() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} while (!content);
|
||||
|
||||
return content;
|
||||
nsCOMPtr<nsPIDOMWindow> window = subdoc->GetWindow();
|
||||
return GetRootForFocus(window, subdoc, true, true);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -25,6 +25,12 @@ class nsIDocShellTreeItem;
|
||||
class nsPIDOMWindow;
|
||||
class nsIMessageBroadcaster;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class TabParent;
|
||||
}
|
||||
}
|
||||
|
||||
struct nsDelayedBlurOrFocusEvent;
|
||||
|
||||
/**
|
||||
@ -387,6 +393,7 @@ protected:
|
||||
bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation,
|
||||
nsIContent** aResultContent);
|
||||
|
||||
/**
|
||||
@ -415,65 +422,32 @@ protected:
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aForward);
|
||||
|
||||
/**
|
||||
* Focus the first focusable content within the document with a root node of
|
||||
* aRootContent. For content documents, this will be aRootContent itself, but
|
||||
* for chrome documents, this will locate the next focusable content.
|
||||
*/
|
||||
nsresult FocusFirst(nsIContent* aRootContent, nsIContent** aNextContent);
|
||||
|
||||
/**
|
||||
* Retrieves and returns the root node from aDocument to be focused. Will
|
||||
* return null if the root node cannot be focused. There are several reasons
|
||||
* for this:
|
||||
*
|
||||
* - if aIsForDocNavigation is true, and aWindow is in an <iframe>.
|
||||
* - if aIsForDocNavigation is false, and aWindow is a chrome shell.
|
||||
* - if aForDocumentNavigation is false and aWindow is a chrome shell.
|
||||
* - if aCheckVisibility is true and the aWindow is not visible.
|
||||
* - if aDocument is a frameset document.
|
||||
*/
|
||||
nsIContent* GetRootForFocus(nsPIDOMWindow* aWindow,
|
||||
nsIDocument* aDocument,
|
||||
bool aIsForDocNavigation,
|
||||
bool aForDocumentNavigation,
|
||||
bool aCheckVisibility);
|
||||
|
||||
/**
|
||||
* Get the last docshell child of aItem and return it in aResult.
|
||||
* Retrieves and returns the root node as with GetRootForFocus but only if
|
||||
* aContent is a frame with a valid child document.
|
||||
*/
|
||||
void GetLastDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult);
|
||||
|
||||
/**
|
||||
* Get the next docshell child of aItem and return it in aResult.
|
||||
*/
|
||||
void GetNextDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult);
|
||||
|
||||
/**
|
||||
* Get the previous docshell child of aItem and return it in aResult.
|
||||
*/
|
||||
void GetPreviousDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult);
|
||||
|
||||
/**
|
||||
* 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(nsIContent* aStartContent, bool aForward);
|
||||
nsIContent* GetRootForChildDocument(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Retreives a focusable element within the current selection of aWindow.
|
||||
|
@ -8,7 +8,7 @@
|
||||
interface nsIDocument;
|
||||
interface nsIContent;
|
||||
|
||||
[scriptable, uuid(c0716002-5602-4002-a0de-cc69b924b2c6)]
|
||||
[scriptable, uuid(2487F9CA-D05F-4BD1-8F43-5964E746C482)]
|
||||
/**
|
||||
* The focus manager deals with all focus related behaviour. Only one element
|
||||
* in the entire application may have the focus at a time; this element
|
||||
@ -97,7 +97,8 @@ interface nsIFocusManager : nsISupports
|
||||
* MOVEFOCUS_CARET, then the focus is cleared. If aType is any other value,
|
||||
* the focus is not changed.
|
||||
*
|
||||
* Returns the element that was focused.
|
||||
* Returns the element that was focused. The return value may be null if focus
|
||||
* was moved into a child process.
|
||||
*/
|
||||
nsIDOMElement moveFocus(in nsIDOMWindow aWindow, in nsIDOMElement aStartElement,
|
||||
in unsigned long aType, in unsigned long aFlags);
|
||||
@ -215,6 +216,11 @@ interface nsIFocusManager : nsISupports
|
||||
*/
|
||||
const unsigned long MOVEFOCUS_CARET = 8;
|
||||
|
||||
/** move focus to the first focusable document */
|
||||
const unsigned long MOVEFOCUS_FIRSTDOC = 9;
|
||||
/** move focus to the last focusable document */
|
||||
const unsigned long MOVEFOCUS_LASTDOC = 10;
|
||||
|
||||
/**
|
||||
* Called when a window has been raised.
|
||||
*/
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
[scriptable, uuid(4d4576eb-ecfe-47b9-93b8-4121518621ad)]
|
||||
[scriptable, uuid(A10D887D-7FC2-48A2-ABC6-A2027423860C)]
|
||||
interface nsITabParent : nsISupports
|
||||
{
|
||||
void injectTouchEvent(in AString aType,
|
||||
@ -27,5 +27,11 @@ interface nsITabParent : nsISupports
|
||||
|
||||
readonly attribute uint64_t tabId;
|
||||
|
||||
/**
|
||||
* If aForward is true, navigate to the first focusable document.
|
||||
* If aForward is false, navigate to the last focusable document.
|
||||
*/
|
||||
void navigateDocument(in bool aForward);
|
||||
|
||||
readonly attribute boolean hasContentOpener;
|
||||
};
|
||||
|
@ -150,9 +150,9 @@ parent:
|
||||
parent:
|
||||
/**
|
||||
* When child sends this message, parent should move focus to
|
||||
* the next or previous focusable element.
|
||||
* the next or previous focusable element or document.
|
||||
*/
|
||||
MoveFocus(bool forward);
|
||||
MoveFocus(bool forward, bool forDocumentNavigation);
|
||||
|
||||
Event(RemoteDOMEvent aEvent);
|
||||
|
||||
@ -703,6 +703,11 @@ child:
|
||||
*/
|
||||
SetIsDocShellActive(bool aIsActive);
|
||||
|
||||
/**
|
||||
* Navigate by document.
|
||||
*/
|
||||
NavigateDocument(bool aForward);
|
||||
|
||||
/**
|
||||
* The parent (chrome thread) requests that the child inform it when
|
||||
* the graphics objects are ready to display.
|
||||
|
@ -1436,16 +1436,16 @@ TabChild::Blur()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabChild::FocusNextElement()
|
||||
TabChild::FocusNextElement(bool aForDocumentNavigation)
|
||||
{
|
||||
SendMoveFocus(true);
|
||||
SendMoveFocus(true, aForDocumentNavigation);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabChild::FocusPrevElement()
|
||||
TabChild::FocusPrevElement(bool aForDocumentNavigation)
|
||||
{
|
||||
SendMoveFocus(false);
|
||||
SendMoveFocus(false, aForDocumentNavigation);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2940,6 +2940,24 @@ TabChild::RecvSetIsDocShellActive(const bool& aIsActive)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvNavigateDocument(const bool& aForward)
|
||||
{
|
||||
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (fm) {
|
||||
nsCOMPtr<nsIDOMElement> result;
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(WebNavigation());
|
||||
|
||||
// Move to the first or last document.
|
||||
fm->MoveFocus(window, nullptr, aForward ? nsIFocusManager::MOVEFOCUS_FIRSTDOC :
|
||||
nsIFocusManager::MOVEFOCUS_LASTDOC,
|
||||
nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result));
|
||||
SendRequestFocus(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PRenderFrameChild*
|
||||
TabChild::AllocPRenderFrameChild()
|
||||
{
|
||||
|
@ -525,6 +525,7 @@ protected:
|
||||
virtual bool RecvDestroy() override;
|
||||
virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override;
|
||||
virtual bool RecvSetIsDocShellActive(const bool& aIsActive) override;
|
||||
virtual bool RecvNavigateDocument(const bool& aForward) override;
|
||||
|
||||
virtual bool RecvRequestNotifyAfterRemotePaint() override;
|
||||
|
||||
|
@ -509,13 +509,17 @@ TabParent::ActorDestroy(ActorDestroyReason why)
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvMoveFocus(const bool& aForward)
|
||||
TabParent::RecvMoveFocus(const bool& aForward, const bool& aForDocumentNavigation)
|
||||
{
|
||||
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
|
||||
if (fm) {
|
||||
nsCOMPtr<nsIDOMElement> dummy;
|
||||
uint32_t type = aForward ? uint32_t(nsIFocusManager::MOVEFOCUS_FORWARD)
|
||||
: uint32_t(nsIFocusManager::MOVEFOCUS_BACKWARD);
|
||||
|
||||
uint32_t type = aForward ?
|
||||
(aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARDDOC) :
|
||||
static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARD)) :
|
||||
(aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARDDOC) :
|
||||
static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARD));
|
||||
nsCOMPtr<nsIDOMElement> frame = do_QueryInterface(mFrameElement);
|
||||
fm->MoveFocus(nullptr, frame, type, nsIFocusManager::FLAG_BYKEY,
|
||||
getter_AddRefs(dummy));
|
||||
@ -2932,6 +2936,13 @@ TabParent::SetHasContentOpener(bool aHasContentOpener)
|
||||
mHasContentOpener = aHasContentOpener;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::NavigateDocument(bool aForward)
|
||||
{
|
||||
unused << SendNavigateDocument(aForward);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class LayerTreeUpdateRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
|
@ -125,7 +125,8 @@ public:
|
||||
void AddWindowListeners();
|
||||
void DidRefresh() override;
|
||||
|
||||
virtual bool RecvMoveFocus(const bool& aForward) override;
|
||||
virtual bool RecvMoveFocus(const bool& aForward,
|
||||
const bool& aForDocumentNavigation) override;
|
||||
virtual bool RecvEvent(const RemoteDOMEvent& aEvent) override;
|
||||
virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent) override;
|
||||
virtual bool RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent) override;
|
||||
|
@ -12,19 +12,21 @@
|
||||
* to the embedding chrome. See mozilla bug #70224 for gratuitous info.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(d2206418-1dd1-11b2-8e55-acddcd2bcfb8)]
|
||||
[scriptable, uuid(947B2EE6-51ED-4C2B-9F45-426C27CA84C6)]
|
||||
interface nsIWebBrowserChromeFocus : nsISupports
|
||||
{
|
||||
/**
|
||||
* Set the focus at the next focusable element in the chrome.
|
||||
* Set the focus at the next focusable element in the chrome. If
|
||||
* aForDocumentNavigation is true, this was a document navigation, so
|
||||
* focus the parent window.
|
||||
*/
|
||||
|
||||
void focusNextElement();
|
||||
void focusNextElement(in bool aForDocumentNavigation);
|
||||
|
||||
/**
|
||||
* Set the focus at the previous focusable element in the chrome.
|
||||
*/
|
||||
|
||||
void focusPrevElement();
|
||||
void focusPrevElement(in bool aForDocumentNavigation);
|
||||
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user