Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozila.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Mozilla Corporation
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2008
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsFocusManager.h"
|
|
|
|
|
|
|
|
#include "nsIInterfaceRequestor.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsIEnumerator.h"
|
|
|
|
#include "nsTPtrArray.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
#include "nsIPrefBranch2.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIDOMWindow.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
|
|
|
#include "nsIDOMElement.h"
|
|
|
|
#include "nsIDOMXULElement.h"
|
|
|
|
#include "nsIDOMNSHTMLFrameElement.h"
|
|
|
|
#include "nsIDOMHTMLInputElement.h"
|
|
|
|
#include "nsIDOMHTMLMapElement.h"
|
|
|
|
#include "nsIDOMHTMLLegendElement.h"
|
|
|
|
#include "nsIDOMDocumentRange.h"
|
|
|
|
#include "nsIDOMRange.h"
|
|
|
|
#include "nsIHTMLDocument.h"
|
|
|
|
#include "nsIFormControlFrame.h"
|
|
|
|
#include "nsGenericHTMLElement.h"
|
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsIEditorDocShell.h"
|
|
|
|
#include "nsIDocShellTreeItem.h"
|
|
|
|
#include "nsIDocShellTreeOwner.h"
|
|
|
|
#include "nsLayoutUtils.h"
|
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsIContentViewer.h"
|
|
|
|
#include "nsFrameTraversal.h"
|
|
|
|
#include "nsObjectFrame.h"
|
|
|
|
#include "nsEventDispatcher.h"
|
|
|
|
#include "nsIEventStateManager.h"
|
|
|
|
#include "nsIMEStateManager.h"
|
|
|
|
#include "nsIWebNavigation.h"
|
|
|
|
#include "nsCaret.h"
|
|
|
|
#include "nsWidgetsCID.h"
|
|
|
|
#include "nsILookAndFeel.h"
|
|
|
|
#include "nsIWidget.h"
|
|
|
|
#include "nsIBaseWindow.h"
|
|
|
|
#include "nsIViewManager.h"
|
|
|
|
#include "nsFrameSelection.h"
|
|
|
|
#include "nsXULPopupManager.h"
|
|
|
|
#include "nsImageMapUtils.h"
|
|
|
|
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
#include "nsIDOMXULTextboxElement.h"
|
|
|
|
#include "nsIDOMXULMenuListElement.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//#define DEBUG_FOCUS 1
|
|
|
|
//#define DEBUG_FOCUS_NAVIGATION 1
|
|
|
|
#define PRINTTAGF(format, content) \
|
|
|
|
{ \
|
|
|
|
nsAutoString tag(NS_LITERAL_STRING("(none)")); \
|
|
|
|
if (content) \
|
|
|
|
content->Tag()->ToString(tag); \
|
|
|
|
printf(format, NS_ConvertUTF16toUTF8(tag).get()); \
|
|
|
|
}
|
|
|
|
|
|
|
|
struct nsDelayedBlurOrFocusEvent
|
|
|
|
{
|
|
|
|
nsDelayedBlurOrFocusEvent(PRUint32 aType,
|
|
|
|
nsIPresShell* aPresShell,
|
|
|
|
nsIDocument* aDocument,
|
|
|
|
nsPIDOMEventTarget* aTarget)
|
|
|
|
: mType(aType),
|
|
|
|
mPresShell(aPresShell),
|
|
|
|
mDocument(aDocument),
|
|
|
|
mTarget(aTarget) { }
|
|
|
|
|
|
|
|
nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent& aOther)
|
|
|
|
: mType(aOther.mType),
|
|
|
|
mPresShell(aOther.mPresShell),
|
|
|
|
mDocument(aOther.mDocument),
|
|
|
|
mTarget(aOther.mTarget) { }
|
|
|
|
|
|
|
|
PRUint32 mType;
|
|
|
|
nsCOMPtr<nsIPresShell> mPresShell;
|
|
|
|
nsCOMPtr<nsIDocument> mDocument;
|
|
|
|
nsCOMPtr<nsPIDOMEventTarget> mTarget;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIFocusManager)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
|
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsFocusManager)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFocusManager)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mActiveWindow)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFocusedWindow)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFocusedContent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstBlurEvent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstFocusEvent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindowBeingLowered)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFocusManager)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mActiveWindow)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFocusedWindow)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFocusedContent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstBlurEvent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstFocusEvent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindowBeingLowered)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
|
|
|
|
|
|
|
|
nsIFocusManager* nsFocusManager::sInstance = nsnull;
|
|
|
|
|
|
|
|
nsFocusManager::nsFocusManager()
|
|
|
|
{ }
|
|
|
|
|
|
|
|
nsFocusManager::~nsFocusManager()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIPrefBranch2> prefBranch =
|
|
|
|
do_QueryInterface(nsContentUtils::GetPrefBranch());
|
|
|
|
|
|
|
|
if (prefBranch) {
|
|
|
|
prefBranch->RemoveObserver("accessibility.browsewithcaret", this);
|
|
|
|
prefBranch->RemoveObserver("accessibility.tabfocus_applies_to_xul", this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsresult
|
|
|
|
nsFocusManager::Init()
|
|
|
|
{
|
|
|
|
nsFocusManager* fm = new nsFocusManager();
|
|
|
|
NS_ENSURE_TRUE(fm, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
NS_ADDREF(fm);
|
|
|
|
sInstance = fm;
|
|
|
|
|
|
|
|
nsIContent::sTabFocusModelAppliesToXUL =
|
|
|
|
nsContentUtils::GetBoolPref("accessibility.tabfocus_applies_to_xul",
|
|
|
|
nsIContent::sTabFocusModelAppliesToXUL);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPrefBranch2> prefBranch =
|
|
|
|
do_QueryInterface(nsContentUtils::GetPrefBranch());
|
|
|
|
prefBranch->AddObserver("accessibility.browsewithcaret", fm, PR_TRUE);
|
|
|
|
prefBranch->AddObserver("accessibility.tabfocus_applies_to_xul", fm, PR_TRUE);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
void
|
|
|
|
nsFocusManager::Shutdown()
|
|
|
|
{
|
|
|
|
NS_IF_RELEASE(sInstance);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::Observe(nsISupports *aSubject,
|
|
|
|
const char *aTopic,
|
|
|
|
const PRUnichar *aData)
|
|
|
|
{
|
|
|
|
nsDependentString data(aData);
|
|
|
|
if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
|
|
|
if (data.EqualsLiteral("accessibility.browsewithcaret")) {
|
|
|
|
UpdateCaret(PR_FALSE, PR_TRUE, mFocusedContent);
|
|
|
|
}
|
|
|
|
else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
|
|
|
|
nsIContent::sTabFocusModelAppliesToXUL =
|
|
|
|
nsContentUtils::GetBoolPref("accessibility.tabfocus_applies_to_xul",
|
|
|
|
nsIContent::sTabFocusModelAppliesToXUL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// given a frame content node, retrieve the nsIDOMWindow displayed in it
|
|
|
|
static nsPIDOMWindow*
|
|
|
|
GetContentWindow(nsIContent* aContent)
|
|
|
|
{
|
|
|
|
nsIDocument* doc = aContent->GetCurrentDoc();
|
|
|
|
if (doc) {
|
|
|
|
nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
|
|
|
|
if (subdoc)
|
|
|
|
return subdoc->GetWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the current window for the given content node
|
|
|
|
static nsPIDOMWindow*
|
|
|
|
GetCurrentWindow(nsIContent* aContent)
|
|
|
|
{
|
|
|
|
nsIDocument *doc = aContent->GetCurrentDoc();
|
|
|
|
return doc ? doc->GetWindow() : nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsIContent*
|
|
|
|
nsFocusManager::GetFocusedDescendant(nsPIDOMWindow* aWindow, PRBool aDeep,
|
|
|
|
nsPIDOMWindow** aFocusedWindow)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(aWindow, nsnull);
|
|
|
|
|
|
|
|
*aFocusedWindow = nsnull;
|
|
|
|
|
|
|
|
nsIContent* currentContent = nsnull;
|
|
|
|
nsPIDOMWindow* window = aWindow->GetOuterWindow();
|
|
|
|
while (window) {
|
|
|
|
*aFocusedWindow = window;
|
|
|
|
currentContent = window->GetFocusedNode();
|
|
|
|
if (!currentContent || !aDeep)
|
|
|
|
break;
|
|
|
|
|
|
|
|
window = GetContentWindow(currentContent);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IF_ADDREF(*aFocusedWindow);
|
|
|
|
|
|
|
|
return currentContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsIContent*
|
|
|
|
nsFocusManager::GetRedirectedFocus(nsIContent* aContent)
|
|
|
|
{
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
if (aContent->IsNodeOfType(nsINode::eXUL)) {
|
|
|
|
nsCOMPtr<nsIDOMNode> inputField;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMXULTextBoxElement> textbox = do_QueryInterface(aContent);
|
|
|
|
if (textbox) {
|
|
|
|
textbox->GetInputField(getter_AddRefs(inputField));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aContent);
|
|
|
|
if (menulist) {
|
|
|
|
menulist->GetInputField(getter_AddRefs(inputField));
|
|
|
|
}
|
|
|
|
else if (aContent->Tag() == nsGkAtoms::scale) {
|
|
|
|
nsCOMPtr<nsIDocument> doc = aContent->GetCurrentDoc();
|
|
|
|
if (!doc)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsINodeList* children = doc->BindingManager()->GetXBLChildNodesFor(aContent);
|
|
|
|
if (children) {
|
|
|
|
nsIContent* child = children->GetNodeAt(0);
|
|
|
|
if (child && child->Tag() == nsGkAtoms::slider)
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inputField) {
|
|
|
|
nsCOMPtr<nsIContent> retval = do_QueryInterface(inputField);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::GetActiveWindow(nsIDOMWindow** aWindow)
|
|
|
|
{
|
|
|
|
NS_IF_ADDREF(*aWindow = mActiveWindow);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::SetActiveWindow(nsIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
// only top-level windows can be made active
|
|
|
|
nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aWindow);
|
2009-06-15 08:05:47 -07:00
|
|
|
NS_ASSERTION(!piWindow || piWindow->IsOuterWindow(), "outer window expected");
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
|
|
|
NS_ENSURE_TRUE(piWindow && (piWindow == piWindow->GetPrivateRoot()),
|
|
|
|
NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
RaiseWindow(piWindow);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::GetFocusedWindow(nsIDOMWindow** aFocusedWindow)
|
|
|
|
{
|
|
|
|
NS_IF_ADDREF(*aFocusedWindow = mFocusedWindow);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsFocusManager::SetFocusedWindow(nsIDOMWindow* aWindowToFocus)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("<<SetFocusedWindow begin>>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> windowToFocus(do_QueryInterface(aWindowToFocus));
|
|
|
|
NS_ENSURE_TRUE(windowToFocus, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
windowToFocus = windowToFocus->GetOuterWindow();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> frameContent =
|
|
|
|
do_QueryInterface(windowToFocus->GetFrameElementInternal());
|
|
|
|
if (frameContent) {
|
|
|
|
// pass false so that the caret does not get updated and scrolling does
|
|
|
|
// not occur.
|
|
|
|
SetFocusInner(frameContent, 0, PR_FALSE);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// this is a top-level window. If the window has a child frame focused,
|
|
|
|
// clear the focus. Otherwise, focus should already be in this frame, or
|
|
|
|
// already cleared. This ensures that focus will be in this frame and not
|
|
|
|
// in a child.
|
|
|
|
nsIContent* content = windowToFocus->GetFocusedNode();
|
|
|
|
if (content) {
|
|
|
|
nsCOMPtr<nsIDOMWindow> childWindow = GetContentWindow(content);
|
|
|
|
if (childWindow)
|
|
|
|
ClearFocus(childWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> rootWindow = windowToFocus->GetPrivateRoot();
|
|
|
|
if (rootWindow)
|
|
|
|
RaiseWindow(rootWindow);
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("<<SetFocusedWindow end>>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::GetFocusedElement(nsIDOMElement** aFocusedElement)
|
|
|
|
{
|
|
|
|
if (mFocusedContent)
|
|
|
|
CallQueryInterface(mFocusedContent, aFocusedElement);
|
|
|
|
else
|
|
|
|
*aFocusedElement = nsnull;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::GetLastFocusMethod(nsIDOMWindow* aWindow, PRUint32* aLastFocusMethod)
|
|
|
|
{
|
|
|
|
// the focus method is stored on the inner window
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
|
|
|
|
if (window)
|
|
|
|
window = window->GetCurrentInnerWindow();
|
|
|
|
if (!window)
|
|
|
|
window = mFocusedWindow;
|
|
|
|
|
|
|
|
*aLastFocusMethod = window ? window->GetFocusMethod() : 0;
|
|
|
|
|
|
|
|
NS_ASSERTION((*aLastFocusMethod & FOCUSMETHOD_MASK) == *aLastFocusMethod,
|
|
|
|
"invalid focus method");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::SetFocus(nsIDOMElement* aElement, PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("<<SetFocus>>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> newFocus = do_QueryInterface(aElement);
|
|
|
|
NS_ENSURE_ARG(newFocus);
|
|
|
|
|
|
|
|
SetFocusInner(newFocus, aFlags, PR_TRUE);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::MoveFocus(nsIDOMWindow* aWindow, nsIDOMElement* aStartElement,
|
|
|
|
PRUint32 aType, PRUint32 aFlags, nsIDOMElement** aElement)
|
|
|
|
{
|
|
|
|
*aElement = nsnull;
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("<<MoveFocus Type: %d Flags: %d>>\n<<", aType, aFlags);
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> focusedWindow = mFocusedWindow;
|
|
|
|
if (focusedWindow) {
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(focusedWindow->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
nsCAutoString spec;
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf(" [%p] Focused Window: %s", mFocusedWindow.get(), spec.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PRINTTAGF(">> $[[%s]]\n", mFocusedContent);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window;
|
|
|
|
nsCOMPtr<nsIContent> startContent;
|
|
|
|
if (aStartElement) {
|
|
|
|
startContent = do_QueryInterface(aStartElement);
|
|
|
|
NS_ENSURE_TRUE(startContent, NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
window = GetCurrentWindow(startContent);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
window = aWindow ? do_QueryInterface(aWindow) : mFocusedWindow;
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
|
|
|
window = window->GetOuterWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> newFocus;
|
|
|
|
nsresult rv = DetermineElementToMoveFocus(window, startContent, aType,
|
|
|
|
getter_AddRefs(newFocus));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS_NAVIGATION
|
|
|
|
PRINTTAGF("-> Element to be focused: %s\n", newFocus);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (newFocus) {
|
|
|
|
// for caret movement, pass false for the aFocusChanged argument,
|
|
|
|
// otherwise the caret will end up moving to the focus position. This
|
|
|
|
// would be a problem because the caret would move to the beginning of the
|
|
|
|
// focused link making it impossible to navigate the caret over a link.
|
|
|
|
SetFocusInner(newFocus, aFlags, aType != MOVEFOCUS_CARET);
|
|
|
|
CallQueryInterface(newFocus, aElement);
|
|
|
|
}
|
|
|
|
else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) {
|
|
|
|
// no content was found, so clear the focus for these two types.
|
|
|
|
ClearFocus(window);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("<<MoveFocus end>>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::ClearFocus(nsIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("<<ClearFocus begin>>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// if the window to clear is the focused window or an ancestor of the
|
|
|
|
// focused window, then blur the existing focused content. Otherwise, the
|
|
|
|
// focus is somewhere else so just update the current node.
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
window = window->GetOuterWindow();
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
if (IsSameOrAncestor(window, mFocusedWindow)) {
|
|
|
|
PRBool isAncestor = (window != mFocusedWindow);
|
|
|
|
if (Blur(window, nsnull, isAncestor)) {
|
|
|
|
// if we are clearing the focus on an ancestor of the focused window,
|
|
|
|
// the ancestor will become the new focused window, so focus it
|
|
|
|
if (isAncestor)
|
|
|
|
Focus(window, nsnull, 0, PR_TRUE, PR_FALSE, PR_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
window->SetFocusedNode(nsnull);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("<<ClearFocus end>>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::GetFocusedElementForWindow(nsIDOMWindow* aWindow,
|
|
|
|
PRBool aDeep,
|
|
|
|
nsIDOMWindow** aFocusedWindow,
|
|
|
|
nsIDOMElement** aElement)
|
|
|
|
{
|
|
|
|
*aElement = nsnull;
|
|
|
|
if (aFocusedWindow)
|
|
|
|
*aFocusedWindow = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
window = window->GetOuterWindow();
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> focusedWindow;
|
|
|
|
nsCOMPtr<nsIContent> focusedContent =
|
|
|
|
GetFocusedDescendant(window, aDeep, getter_AddRefs(focusedWindow));
|
|
|
|
if (focusedContent)
|
|
|
|
CallQueryInterface(focusedContent, aElement);
|
|
|
|
|
|
|
|
if (aFocusedWindow)
|
|
|
|
NS_IF_ADDREF(*aFocusedWindow = focusedWindow);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::MoveCaretToFocus(nsIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
PRInt32 itemType = nsIDocShellTreeItem::typeChrome;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
|
|
|
|
if (dsti) {
|
|
|
|
dsti->GetItemType(&itemType);
|
|
|
|
if (itemType != nsIDocShellTreeItem::typeChrome) {
|
|
|
|
// don't move the caret for editable documents
|
|
|
|
nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(dsti));
|
|
|
|
if (editorDocShell) {
|
|
|
|
PRBool isEditable;
|
|
|
|
editorDocShell->GetEditable(&isEditable);
|
|
|
|
if (isEditable)
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(dsti);
|
|
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
docShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
|
|
|
|
nsCOMPtr<nsIContent> content = window->GetFocusedNode();
|
|
|
|
if (content)
|
|
|
|
MoveCaretToFocus(presShell, content);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::WindowRaised(nsIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
|
|
|
NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("Window %p Raised [Currently: %p %p] <<", aWindow, mActiveWindow.get(), mFocusedWindow.get());
|
|
|
|
nsCAutoString spec;
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf("[%p] Raised Window: %s", aWindow, spec.get());
|
|
|
|
}
|
|
|
|
if (mActiveWindow) {
|
|
|
|
doc = do_QueryInterface(mActiveWindow->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf(" [%p] Active Window: %s", mActiveWindow.get(), spec.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf(">>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (mActiveWindow == window) {
|
|
|
|
// The window is already active, so there is no need to focus anything,
|
|
|
|
// but make sure that the right widget is focused. This is a special case
|
|
|
|
// for Windows because when restoring a minimized window, a second
|
|
|
|
// activation will occur and the top-level widget could be focused instead
|
|
|
|
// of the child we want. We solve this by calling SetFocus to ensure that
|
|
|
|
// what the focus manager thinks should be the current widget is actually
|
|
|
|
// focused.
|
|
|
|
EnsureCurrentWidgetFocused();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// lower the existing window, if any. This shouldn't happen usually.
|
|
|
|
if (mActiveWindow)
|
|
|
|
WindowLowered(mActiveWindow);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(aWindow));
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(webnav));
|
|
|
|
// If there's no docShellAsItem, this window must have been closed,
|
|
|
|
// in that case there is no tree owner.
|
|
|
|
NS_ENSURE_TRUE(docShellAsItem, NS_OK);
|
|
|
|
|
|
|
|
// set this as the active window
|
|
|
|
mActiveWindow = window;
|
|
|
|
|
|
|
|
// ensure that the window is enabled and visible
|
|
|
|
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
|
|
|
docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
|
|
|
|
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
|
|
|
|
if (baseWindow) {
|
|
|
|
PRBool isEnabled = PR_TRUE;
|
|
|
|
if (NS_SUCCEEDED(baseWindow->GetEnabled(&isEnabled)) && !isEnabled) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
baseWindow->SetVisibility(PR_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// inform the DOM window that it has activated, so that the active attribute
|
|
|
|
// is updated on the window
|
|
|
|
window->ActivateOrDeactivate(PR_TRUE);
|
|
|
|
|
|
|
|
// retrieve the last focused element within the window that was raised
|
|
|
|
nsCOMPtr<nsPIDOMWindow> currentWindow;
|
|
|
|
nsCOMPtr<nsIContent> currentFocus =
|
|
|
|
GetFocusedDescendant(window, PR_TRUE, getter_AddRefs(currentWindow));
|
|
|
|
|
|
|
|
NS_ASSERTION(currentWindow, "window raised with no window current");
|
|
|
|
if (!currentWindow)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> currentDocShell = currentWindow->GetDocShell();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
currentDocShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (presShell) {
|
|
|
|
// disable selection mousedown state on activation
|
|
|
|
// XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
|
|
|
|
nsCOMPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
|
|
|
|
frameSelection->SetMouseDownState(PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
Focus(currentWindow, currentFocus, 0, PR_TRUE, PR_FALSE, PR_TRUE);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::WindowLowered(nsIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
|
|
|
NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("Window %p Lowered [Currently: %p %p] <<", aWindow, mActiveWindow.get(), mFocusedWindow.get());
|
|
|
|
nsCAutoString spec;
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf("[%p] Lowered Window: %s", aWindow, spec.get());
|
|
|
|
}
|
|
|
|
if (mActiveWindow) {
|
|
|
|
doc = do_QueryInterface(mActiveWindow->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf(" [%p] Active Window: %s", mActiveWindow.get(), spec.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf(">>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (mActiveWindow != window)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// inform the DOM window that it has deactivated, so that the active
|
|
|
|
// attribute is updated on the window
|
|
|
|
window->ActivateOrDeactivate(PR_FALSE);
|
|
|
|
|
|
|
|
// keep track of the window being lowered, so that attempts to raise the
|
|
|
|
// window can be prevented until we return. Otherwise, focus can get into
|
|
|
|
// an unusual state.
|
|
|
|
mWindowBeingLowered = mActiveWindow;
|
|
|
|
mActiveWindow = nsnull;
|
|
|
|
|
|
|
|
if (mFocusedWindow)
|
|
|
|
Blur(nsnull, nsnull, PR_TRUE);
|
|
|
|
|
|
|
|
mWindowBeingLowered = nsnull;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG(aDocument);
|
|
|
|
NS_ENSURE_ARG(aContent);
|
|
|
|
|
|
|
|
nsPIDOMWindow *window = aDocument->GetWindow();
|
|
|
|
if (!window)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// if the content is currently focused in the window, or is an ancestor
|
|
|
|
// of the currently focused element, reset the focus within that window.
|
|
|
|
nsCOMPtr<nsIContent> content = window->GetFocusedNode();
|
|
|
|
if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) {
|
|
|
|
window->SetFocusedNode(nsnull);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
|
|
|
|
if (docShell) {
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
docShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
nsIMEStateManager::OnRemoveContent(presShell->GetPresContext(), content);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this window is currently focused, clear the global focused
|
|
|
|
// element as well, but don't fire any events.
|
2009-07-29 07:36:03 -07:00
|
|
|
if (window == mFocusedWindow) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
mFocusedContent = nsnull;
|
2009-07-29 07:36:03 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Check if the node that was focused is an iframe or similar by looking
|
|
|
|
// if it has a subdocument. This would indicate that this focused iframe
|
|
|
|
// and its descendants will be going away. We will need to move the
|
|
|
|
// focus somewhere else, so just clear the focus in the toplevel window
|
|
|
|
// so that no element is focused.
|
|
|
|
nsIDocument* subdoc = aDocument->GetSubDocumentFor(content);
|
|
|
|
if (subdoc) {
|
|
|
|
nsCOMPtr<nsISupports> container = subdoc->GetContainer();
|
|
|
|
nsCOMPtr<nsPIDOMWindow> childWindow = do_GetInterface(container);
|
|
|
|
if (childWindow && IsSameOrAncestor(childWindow, mFocusedWindow)) {
|
|
|
|
ClearFocus(mActiveWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::WindowShown(nsIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
window = window->GetOuterWindow();
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("Window %p Shown [Currently: %p %p] <<", window.get(), mActiveWindow.get(), mFocusedWindow.get());
|
|
|
|
nsCAutoString spec;
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf("Shown Window: %s", spec.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mFocusedWindow) {
|
|
|
|
doc = do_QueryInterface(mFocusedWindow->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf(" Focused Window: %s", spec.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf(">>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (mFocusedWindow != window)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> currentWindow;
|
|
|
|
nsCOMPtr<nsIContent> currentFocus =
|
|
|
|
GetFocusedDescendant(window, PR_TRUE, getter_AddRefs(currentWindow));
|
|
|
|
if (currentWindow)
|
|
|
|
Focus(currentWindow, currentFocus, 0, PR_TRUE, PR_FALSE, PR_FALSE);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::WindowHidden(nsIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
// if there is no window or it is not the same or an ancestor of the
|
|
|
|
// currently focused window, just return, as the current focus will not
|
|
|
|
// be affected.
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
window = window->GetOuterWindow();
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("Window %p Hidden [Currently: %p %p] <<", window.get(), mActiveWindow.get(), mFocusedWindow.get());
|
|
|
|
nsCAutoString spec;
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf("Hide Window: %s", spec.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mFocusedWindow) {
|
|
|
|
doc = do_QueryInterface(mFocusedWindow->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf(" Focused Window: %s", spec.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mActiveWindow) {
|
|
|
|
doc = do_QueryInterface(mActiveWindow->GetExtantDocument());
|
|
|
|
if (doc) {
|
|
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
|
|
printf(" Active Window: %s", spec.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf(">>\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!IsSameOrAncestor(window, mFocusedWindow))
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// at this point, we know that the window being hidden is either the focused
|
|
|
|
// window, or an ancestor of the focused window. Either way, the focus is no
|
|
|
|
// longer valid, so it needs to be updated.
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
focusedDocShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (presShell) {
|
|
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
|
|
presContext->EventStateManager()->SetContentState(mFocusedContent, NS_EVENT_STATE_FOCUS);
|
|
|
|
}
|
|
|
|
|
|
|
|
mFocusedContent = nsnull;
|
|
|
|
|
|
|
|
nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
|
|
|
|
if (presShell) {
|
|
|
|
nsIMEStateManager::OnChangeFocus(presShell->GetPresContext(), nsnull);
|
|
|
|
SetCaretVisible(presShell, PR_FALSE, nsnull);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the docshell being hidden is being destroyed, then we want to move
|
|
|
|
// focus somewhere else. Call ClearFocus on the toplevel window, which
|
|
|
|
// will have the effect of clearing the focus and moving the focused window
|
|
|
|
// to the toplevel window. But if the window isn't being destroyed, we are
|
|
|
|
// likely just loading a new document in it, so we want to maintain the
|
|
|
|
// focused window so that the new document gets properly focused.
|
|
|
|
PRBool beingDestroyed;
|
|
|
|
nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell();
|
|
|
|
docShellBeingHidden->IsBeingDestroyed(&beingDestroyed);
|
|
|
|
if (beingDestroyed) {
|
|
|
|
// There is usually no need to do anything if a toplevel window is going
|
|
|
|
// away, as we assume that WindowLowered will be called. However, this may
|
|
|
|
// not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
|
|
|
|
// a leak. So if the active window is being destroyed, call WindowLowered
|
|
|
|
// directly.
|
|
|
|
NS_ASSERTION(mFocusedWindow->IsOuterWindow(), "outer window expected");
|
|
|
|
if (mActiveWindow == mFocusedWindow || mActiveWindow == window)
|
|
|
|
WindowLowered(mActiveWindow);
|
|
|
|
else
|
|
|
|
ClearFocus(mActiveWindow);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the window being hidden is an ancestor of the focused window, adjust
|
|
|
|
// the focused window so that it points to the one being hidden. This
|
|
|
|
// ensures that the focused window isn't in a chain of frames that doesn't
|
|
|
|
// exist any more.
|
|
|
|
if (window != mFocusedWindow) {
|
|
|
|
nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(window));
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
|
|
|
|
if (dsti) {
|
2009-06-22 17:40:55 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
|
|
|
|
dsti->GetParent(getter_AddRefs(parentDsti));
|
|
|
|
nsCOMPtr<nsPIDOMWindow> parentWindow = do_GetInterface(parentDsti);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (parentWindow)
|
|
|
|
parentWindow->SetFocusedNode(nsnull);
|
|
|
|
}
|
|
|
|
|
|
|
|
mFocusedWindow = window;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsFocusManager::FireDelayedEvents(nsIDocument* aDocument)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG(aDocument);
|
|
|
|
|
|
|
|
// fire any delayed focus and blur events in the same order that they were added
|
|
|
|
for (PRUint32 i = 0; i < mDelayedBlurFocusEvents.Length(); i++)
|
|
|
|
{
|
|
|
|
if (mDelayedBlurFocusEvents[i].mDocument == aDocument &&
|
|
|
|
!aDocument->EventHandlingSuppressed()) {
|
|
|
|
PRUint32 type = mDelayedBlurFocusEvents[i].mType;
|
|
|
|
nsCOMPtr<nsPIDOMEventTarget> target = mDelayedBlurFocusEvents[i].mTarget;
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = mDelayedBlurFocusEvents[i].mPresShell;
|
|
|
|
mDelayedBlurFocusEvents.RemoveElementAt(i);
|
|
|
|
SendFocusOrBlurEvent(type, presShell, aDocument, target, 0);
|
|
|
|
--i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
void
|
|
|
|
nsFocusManager::EnsureCurrentWidgetFocused()
|
|
|
|
{
|
|
|
|
if (!mFocusedWindow)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// get the main child widget for the focused window and ensure that the
|
|
|
|
// platform knows that this widget is focused.
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
|
|
|
|
if (docShell) {
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
docShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (presShell) {
|
|
|
|
nsIViewManager* vm = presShell->GetViewManager();
|
|
|
|
if (vm) {
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
2009-07-21 17:45:05 -07:00
|
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (widget)
|
|
|
|
widget->SetFocus(PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::SetFocusInner(nsIContent* aNewContent, PRInt32 aFlags,
|
|
|
|
PRBool aFocusChanged)
|
|
|
|
{
|
|
|
|
// if the element is not focusable, just return and leave the focus as is
|
|
|
|
nsCOMPtr<nsIContent> contentToFocus = CheckIfFocusable(aNewContent, aFlags);
|
|
|
|
if (!contentToFocus)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// check if the element to focus is a frame (iframe) containing a child
|
|
|
|
// document. Frames are never directly focused; instead focusing a frame
|
|
|
|
// means focus what is inside the frame. To do this, the descendant content
|
|
|
|
// within the frame is retrieved and that will be focused instead.
|
|
|
|
nsCOMPtr<nsPIDOMWindow> newWindow;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> subWindow = GetContentWindow(contentToFocus);
|
|
|
|
if (subWindow) {
|
|
|
|
contentToFocus = GetFocusedDescendant(subWindow, PR_TRUE, getter_AddRefs(newWindow));
|
|
|
|
// since a window is being refocused, clear aFocusChanged so that the
|
|
|
|
// caret position isn't updated.
|
|
|
|
aFocusChanged = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// unless it was set above, retrieve the window for the element to focus
|
|
|
|
if (!newWindow)
|
|
|
|
newWindow = GetCurrentWindow(contentToFocus);
|
|
|
|
|
|
|
|
// if the element is already focused, just return. Note that this happens
|
|
|
|
// after the frame check above so that we compare the element that will be
|
|
|
|
// focused rather than the frame it is in.
|
|
|
|
if (!newWindow || newWindow == mFocusedWindow && contentToFocus == mFocusedContent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// don't allow focus to be placed in docshells or descendants of docshells
|
2009-07-13 04:55:56 -07:00
|
|
|
// that are being destroyed. Also, ensure that the page hasn't been
|
|
|
|
// unloaded. The prevents content from being refocused during an unload event.
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsCOMPtr<nsIDocShell> newDocShell = newWindow->GetDocShell();
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = newDocShell;
|
|
|
|
while (docShell) {
|
2009-07-13 04:55:56 -07:00
|
|
|
PRBool inUnload;
|
|
|
|
docShell->GetIsInUnload(&inUnload);
|
|
|
|
if (inUnload)
|
|
|
|
return;
|
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
PRBool beingDestroyed;
|
|
|
|
docShell->IsBeingDestroyed(&beingDestroyed);
|
|
|
|
if (beingDestroyed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(docShell);
|
2009-06-22 17:40:55 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
|
|
|
|
dsti->GetParent(getter_AddRefs(parentDsti));
|
|
|
|
docShell = do_QueryInterface(parentDsti);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// if the new element is in the same window as the currently focused element
|
|
|
|
PRBool isElementInFocusedWindow = (mFocusedWindow == newWindow);
|
|
|
|
|
|
|
|
// to check if the new element is in the active window, compare the
|
|
|
|
// new root docshell for the new element with the active window's docshell.
|
|
|
|
PRBool isElementInActiveWindow = PR_FALSE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(newWindow);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
|
|
|
|
nsCOMPtr<nsPIDOMWindow> newRootWindow;
|
|
|
|
if (dsti) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> root;
|
|
|
|
dsti->GetRootTreeItem(getter_AddRefs(root));
|
|
|
|
newRootWindow = do_GetInterface(root);
|
|
|
|
|
|
|
|
isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
PRINTTAGF("Shift Focus: %s", contentToFocus);
|
|
|
|
printf(" Flags: %d Current Window: %p New Window: %p Current Element: %p",
|
|
|
|
aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedContent.get());
|
|
|
|
printf(" In Active Window: %d In Focused Window: %d\n",
|
|
|
|
isElementInActiveWindow, isElementInFocusedWindow);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
|
|
|
|
// shifted away from the current element if the new shell to focus is
|
|
|
|
// the same or an ancestor shell of the currently focused shell.
|
|
|
|
PRBool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
|
|
|
|
IsSameOrAncestor(newWindow, mFocusedWindow);
|
|
|
|
|
|
|
|
// if the element is in the active window, frame switching is allowed and
|
|
|
|
// the content is in a visible window, fire blur and focus events.
|
2009-08-02 03:40:37 -07:00
|
|
|
if (isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow)) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
// return if blurring fails or the focus changes during the blur
|
|
|
|
if (mFocusedWindow) {
|
|
|
|
// if the focus is being moved to another element in the same document,
|
|
|
|
// or to a descendant, pass the existing window to Blur so that the
|
|
|
|
// current node in the existing window is cleared. If moving to a
|
|
|
|
// window elsewhere, we want to maintain the current node in the
|
|
|
|
// window but still blur it.
|
|
|
|
PRBool currentIsSameOrAncestor = IsSameOrAncestor(mFocusedWindow, newWindow);
|
|
|
|
// find the common ancestor of the currently focused window and the new
|
|
|
|
// window. The ancestor will need to have its currently focused node
|
|
|
|
// cleared once the document has been blurred. Otherwise, we'll be in a
|
|
|
|
// state where a document is blurred yet the chain of windows above it
|
|
|
|
// still points to that document.
|
|
|
|
// For instance, in the following frame tree:
|
|
|
|
// A
|
|
|
|
// B C
|
|
|
|
// D
|
|
|
|
// D is focused and we want to focus C. Once D has been blurred, we need
|
|
|
|
// to clear out the focus in A, otherwise A would still maintain that B
|
|
|
|
// was focused, and B that D was focused.
|
|
|
|
nsCOMPtr<nsPIDOMWindow> commonAncestor;
|
|
|
|
if (!isElementInFocusedWindow)
|
|
|
|
commonAncestor = GetCommonAncestor(newWindow, mFocusedWindow);
|
|
|
|
|
2009-06-10 23:40:41 -07:00
|
|
|
if (!Blur(currentIsSameOrAncestor ? mFocusedWindow.get() : nsnull,
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
commonAncestor, !isElementInFocusedWindow))
|
2009-06-20 17:04:04 -07:00
|
|
|
return;
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Focus(newWindow, contentToFocus, aFlags, !isElementInFocusedWindow,
|
|
|
|
aFocusChanged, PR_FALSE);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// otherwise, for inactive windows, update the node in the window, and
|
|
|
|
// raise the window if desired.
|
|
|
|
if (allowFrameSwitch)
|
|
|
|
AdjustWindowFocus(newWindow);
|
|
|
|
|
|
|
|
// set the focus node and method as needed
|
|
|
|
PRUint32 focusMethod = aFocusChanged ? aFlags & FOCUSMETHOD_MASK :
|
|
|
|
newWindow->GetFocusMethod();
|
|
|
|
newWindow->SetFocusedNode(contentToFocus, focusMethod);
|
|
|
|
if (aFocusChanged) {
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = newWindow->GetDocShell();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
docShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (presShell)
|
|
|
|
ScrollIntoView(presShell, contentToFocus, aFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
// update the commands even when inactive so that the attributes for that
|
|
|
|
// window are up to date.
|
|
|
|
if (allowFrameSwitch)
|
|
|
|
newWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
|
|
|
|
|
|
|
|
if (aFlags & FLAG_RAISE)
|
|
|
|
RaiseWindow(newRootWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsFocusManager::IsSameOrAncestor(nsPIDOMWindow* aPossibleAncestor,
|
|
|
|
nsPIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIWebNavigation> awebnav(do_GetInterface(aPossibleAncestor));
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> ancestordsti = do_QueryInterface(awebnav);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWebNavigation> fwebnav(do_GetInterface(aWindow));
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(fwebnav);
|
|
|
|
while (dsti) {
|
|
|
|
if (dsti == ancestordsti)
|
|
|
|
return PR_TRUE;
|
2009-06-22 17:40:55 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
|
|
|
|
dsti->GetParent(getter_AddRefs(parentDsti));
|
|
|
|
dsti.swap(parentDsti);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<nsPIDOMWindow>
|
|
|
|
nsFocusManager::GetCommonAncestor(nsPIDOMWindow* aWindow1,
|
|
|
|
nsPIDOMWindow* aWindow2)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(aWindow1));
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti1 = do_QueryInterface(webnav);
|
2009-07-29 07:36:03 -07:00
|
|
|
NS_ENSURE_TRUE(dsti1, nsnull);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
|
|
|
webnav = do_GetInterface(aWindow2);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti2 = do_QueryInterface(webnav);
|
2009-07-29 07:36:03 -07:00
|
|
|
NS_ENSURE_TRUE(dsti2, nsnull);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
|
|
|
nsAutoTPtrArray<nsIDocShellTreeItem, 30> parents1, parents2;
|
|
|
|
do {
|
|
|
|
parents1.AppendElement(dsti1);
|
2009-06-22 17:40:55 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti1;
|
|
|
|
dsti1->GetParent(getter_AddRefs(parentDsti1));
|
|
|
|
dsti1.swap(parentDsti1);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
} while (dsti1);
|
|
|
|
do {
|
|
|
|
parents2.AppendElement(dsti2);
|
2009-06-22 17:40:55 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti2;
|
|
|
|
dsti2->GetParent(getter_AddRefs(parentDsti2));
|
|
|
|
dsti2.swap(parentDsti2);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
} while (dsti2);
|
|
|
|
|
|
|
|
PRUint32 pos1 = parents1.Length();
|
|
|
|
PRUint32 pos2 = parents2.Length();
|
|
|
|
nsIDocShellTreeItem* parent = nsnull;
|
|
|
|
PRUint32 len;
|
|
|
|
for (len = PR_MIN(pos1, pos2); len > 0; --len) {
|
|
|
|
nsIDocShellTreeItem* child1 = parents1.ElementAt(--pos1);
|
|
|
|
nsIDocShellTreeItem* child2 = parents2.ElementAt(--pos2);
|
|
|
|
if (child1 != child2) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
parent = child1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(parent);
|
|
|
|
return window.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::AdjustWindowFocus(nsPIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
PRBool isVisible = IsWindowVisible(aWindow);
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window(aWindow);
|
|
|
|
while (window) {
|
|
|
|
// get the containing <iframe> or equivalent element so that it can be
|
|
|
|
// focused below.
|
|
|
|
nsCOMPtr<nsIContent> frameContent =
|
|
|
|
do_QueryInterface(window->GetFrameElementInternal());
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(window));
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
|
2009-06-22 17:40:55 -07:00
|
|
|
if (!dsti)
|
|
|
|
return;
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
|
|
|
|
dsti->GetParent(getter_AddRefs(parentDsti));
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
2009-06-22 17:40:55 -07:00
|
|
|
window = do_GetInterface(parentDsti);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (window) {
|
|
|
|
// if the parent window is visible but aWindow was not, then we have
|
|
|
|
// likely moved up and out from a hidden tab to the browser window, or a
|
|
|
|
// similar such arrangement. Stop adjusting the current nodes.
|
|
|
|
if (IsWindowVisible(window) != isVisible)
|
|
|
|
break;
|
|
|
|
|
|
|
|
window->SetFocusedNode(frameContent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsFocusManager::IsWindowVisible(nsPIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
if (!aWindow)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
|
|
|
nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(docShell));
|
|
|
|
if (!baseWin)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
PRBool visible = PR_FALSE;
|
|
|
|
baseWin->GetVisibility(&visible);
|
|
|
|
return visible;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent*
|
|
|
|
nsFocusManager::CheckIfFocusable(nsIContent* aContent, PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
if (!aContent)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// this is a special case for some XUL elements where an anonymous child is
|
|
|
|
// actually focusable and not the element itself.
|
|
|
|
nsIContent* redirectedFocus = GetRedirectedFocus(aContent);
|
|
|
|
if (redirectedFocus)
|
|
|
|
return CheckIfFocusable(redirectedFocus, aFlags);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> doc = aContent->GetCurrentDoc();
|
|
|
|
// can't focus elements that are not in documents
|
|
|
|
if (!doc)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// Make sure that our frames are up to date
|
|
|
|
if (doc)
|
|
|
|
doc->FlushPendingNotifications(Flush_Frames);
|
|
|
|
|
|
|
|
nsIPresShell *shell = doc->GetPrimaryShell();
|
|
|
|
if (!shell)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// the root content can always be focused
|
|
|
|
if (aContent == doc->GetRootContent())
|
|
|
|
return aContent;
|
|
|
|
|
|
|
|
// cannot focus content in print preview mode. Only the root can be focused.
|
|
|
|
nsPresContext* presContext = shell->GetPresContext();
|
|
|
|
if (presContext && presContext->Type() == nsPresContext::eContext_PrintPreview)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsIFrame* frame = shell->GetPrimaryFrameFor(aContent);
|
|
|
|
if (!frame)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
if (aContent->Tag() == nsGkAtoms::area && aContent->IsNodeOfType(nsINode::eHTML)) {
|
|
|
|
// HTML areas do not have their own frame, and the img frame we get from
|
|
|
|
// GetPrimaryFrameFor() is not relevant as to whether it is focusable or
|
|
|
|
// not, so we have to do all the relevant checks manually for them.
|
|
|
|
return frame->AreAncestorViewsVisible() &&
|
|
|
|
frame->GetStyleVisibility()->IsVisible() &&
|
|
|
|
aContent->IsFocusable() ? aContent : nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this is a child frame content node, check if it is visible and
|
|
|
|
// call the content node's IsFocusable method instead of the frame's
|
|
|
|
// IsFocusable method. This skips checking the style system and ensures that
|
|
|
|
// offscreen browsers can still be focused.
|
|
|
|
nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
|
|
|
|
if (subdoc && IsWindowVisible(subdoc->GetWindow())) {
|
|
|
|
const nsStyleUserInterface* ui = frame->GetStyleUserInterface();
|
|
|
|
PRInt32 tabIndex = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE ||
|
|
|
|
ui->mUserFocus == NS_STYLE_USER_FOCUS_NONE) ? -1 : 0;
|
|
|
|
return aContent->IsFocusable(&tabIndex) ? aContent : nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
return frame->IsFocusable(nsnull, aFlags & FLAG_BYMOUSE) ? aContent : nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
|
|
|
|
nsPIDOMWindow* aAncestorWindowToFocus,
|
|
|
|
PRBool aIsLeavingDocument)
|
|
|
|
{
|
|
|
|
// hold a reference to the focused content, which may be null
|
|
|
|
nsCOMPtr<nsIContent> content = mFocusedContent;
|
2009-06-20 17:04:04 -07:00
|
|
|
if (content) {
|
|
|
|
if (!content->IsInDoc()) {
|
|
|
|
mFocusedContent = nsnull;
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
if (content == mFirstBlurEvent)
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// hold a reference to the focused window
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = mFocusedWindow;
|
|
|
|
if (!window) {
|
|
|
|
mFocusedContent = nsnull;
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
return PR_TRUE;
|
2009-06-20 17:04:04 -07:00
|
|
|
}
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
|
2009-06-20 17:04:04 -07:00
|
|
|
if (!docShell) {
|
|
|
|
mFocusedContent = nsnull;
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
return PR_TRUE;
|
2009-06-20 17:04:04 -07:00
|
|
|
}
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
|
|
|
// Keep a ref to presShell since dispatching the DOM event may cause
|
|
|
|
// the document to be destroyed.
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
docShell->GetPresShell(getter_AddRefs(presShell));
|
2009-06-20 17:04:04 -07:00
|
|
|
if (!presShell) {
|
|
|
|
mFocusedContent = nsnull;
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
return PR_TRUE;
|
2009-06-20 17:04:04 -07:00
|
|
|
}
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
|
|
|
PRBool clearFirstBlurEvent = PR_FALSE;
|
|
|
|
if (!mFirstBlurEvent) {
|
|
|
|
mFirstBlurEvent = content;
|
|
|
|
clearFirstBlurEvent = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there is still an active window, adjust the IME state.
|
|
|
|
// This has to happen before the focus is cleared below, otherwise, the IME
|
|
|
|
// compositionend event won't get fired at the element being blurred.
|
|
|
|
nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
|
|
|
|
nsIMEStateManager::OnChangeFocus(presShell->GetPresContext(), nsnull);
|
|
|
|
|
|
|
|
// now adjust the actual focus, by clearing the fields in the focus manager
|
|
|
|
// and in the window.
|
|
|
|
mFocusedContent = nsnull;
|
|
|
|
if (aWindowToClear)
|
|
|
|
aWindowToClear->SetFocusedNode(nsnull);
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
PRINTTAGF("**Element %s has been blurred\n", content);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
PRBool isRootContent = content && content == content->GetCurrentDoc()->GetRootContent();
|
|
|
|
if (content) {
|
|
|
|
if (!isRootContent) {
|
|
|
|
// unusual to pass a content node to SetContentState on a blur,
|
|
|
|
// but we are just calling it to get the ContentStatesChanged notifications
|
|
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
|
|
presContext->EventStateManager()->SetContentState(content, NS_EVENT_STATE_FOCUS);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if an object/plug-in is being blurred, move the system focus to the
|
|
|
|
// parent window, otherwise events will still get fired at the plugin.
|
2009-06-16 11:34:13 -07:00
|
|
|
// But don't do this if we are blurring due to the window being lowered,
|
|
|
|
// otherwise, the parent window can get raised again.
|
|
|
|
if (mActiveWindow) {
|
|
|
|
nsIFrame* contentFrame = presShell->GetPrimaryFrameFor(content);
|
|
|
|
nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
|
|
|
|
if (objectFrame) {
|
|
|
|
// note that the presshell's widget is being retrieved here, not the one
|
|
|
|
// for the object frame.
|
|
|
|
nsIViewManager* vm = presShell->GetViewManager();
|
|
|
|
if (vm) {
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
2009-07-21 17:45:05 -07:00
|
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
2009-06-16 11:34:13 -07:00
|
|
|
if (widget)
|
|
|
|
widget->SetFocus(PR_TRUE);
|
|
|
|
}
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool result = PR_TRUE;
|
|
|
|
if (content && !isRootContent) {
|
|
|
|
// if there is an active window, update commands. If there isn't an active
|
|
|
|
// window, then this was a blur caused by the active window being lowered,
|
|
|
|
// so there is no need to update the commands
|
|
|
|
if (mActiveWindow)
|
|
|
|
window->UpdateCommands(NS_LITERAL_STRING("focus"));
|
|
|
|
|
|
|
|
SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell,
|
|
|
|
content->GetCurrentDoc(), content, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we are leaving the document or the window was lowered, make the caret
|
|
|
|
// invisible.
|
|
|
|
if (aIsLeavingDocument || !mActiveWindow)
|
|
|
|
SetCaretVisible(presShell, PR_FALSE, nsnull);
|
|
|
|
|
|
|
|
// at this point, it is expected that this window will be still be
|
|
|
|
// focused, but the focused content will be null, as it was cleared before
|
|
|
|
// the event. If this isn't the case, then something else was focused during
|
|
|
|
// the blur event above and we should just return. However, if
|
|
|
|
// aIsLeavingDocument is set, a new document is desired, so make sure to
|
|
|
|
// blur the document and window.
|
|
|
|
if (mFocusedWindow != window ||
|
|
|
|
(mFocusedContent != nsnull && !aIsLeavingDocument)) {
|
|
|
|
result = PR_FALSE;
|
|
|
|
}
|
|
|
|
else if (aIsLeavingDocument) {
|
|
|
|
window->TakeFocus(PR_FALSE, 0);
|
|
|
|
|
|
|
|
// clear the focus so that the ancestor frame hierarchy is in the correct
|
|
|
|
// state. Pass true because aAncestorWindowToFocus is thought to be
|
|
|
|
// focused at this point.
|
|
|
|
if (aAncestorWindowToFocus)
|
|
|
|
aAncestorWindowToFocus->SetFocusedNode(nsnull, 0, PR_TRUE);
|
|
|
|
|
|
|
|
mFocusedWindow = nsnull;
|
|
|
|
mFocusedContent = nsnull;
|
|
|
|
|
|
|
|
// pass 1 for the focus method when calling SendFocusOrBlurEvent just so
|
2009-06-22 12:49:10 -07:00
|
|
|
// that the check is made for suppressed documents. Check to ensure that
|
|
|
|
// the document isn't null in case someone closed it during the blur above
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
|
2009-06-22 12:49:10 -07:00
|
|
|
if (doc)
|
|
|
|
SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell, doc, doc, 1);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (mFocusedWindow == nsnull)
|
|
|
|
SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell, doc, window, 1);
|
|
|
|
|
|
|
|
// check if a different window was focused
|
|
|
|
result = (mFocusedWindow == nsnull && mActiveWindow);
|
|
|
|
}
|
|
|
|
else if (mActiveWindow) {
|
|
|
|
// Otherwise, the blur of the element without blurring the document
|
|
|
|
// occured normally. Call UpdateCaret to redisplay the caret at the right
|
|
|
|
// location within the document. This is needed to ensure that the caret
|
|
|
|
// used for caret browsing is made visible again when an input field is
|
|
|
|
// blurred.
|
|
|
|
UpdateCaret(PR_FALSE, PR_TRUE, nsnull);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clearFirstBlurEvent)
|
|
|
|
mFirstBlurEvent = nsnull;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::Focus(nsPIDOMWindow* aWindow,
|
|
|
|
nsIContent* aContent,
|
|
|
|
PRUint32 aFlags,
|
|
|
|
PRBool aIsNewDocument,
|
|
|
|
PRBool aFocusChanged,
|
|
|
|
PRBool aWindowRaised)
|
|
|
|
{
|
|
|
|
if (!aWindow)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (aContent && aContent == mFirstFocusEvent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Keep a reference to the presShell since dispatching the DOM event may
|
|
|
|
// cause the document to be destroyed.
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
2009-06-22 12:49:10 -07:00
|
|
|
if (!docShell)
|
|
|
|
return;
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
docShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (!presShell)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If the focus actually changed, set the focus method (mouse, keyboard, etc).
|
|
|
|
// Otherwise, just get the current focus method and use that. This ensures
|
|
|
|
// that the method is set during the document and window focus events.
|
|
|
|
PRUint32 focusMethod = aFocusChanged ? aFlags & FOCUSMETHOD_MASK :
|
|
|
|
aWindow->GetFocusMethod();
|
|
|
|
|
|
|
|
if (!IsWindowVisible(aWindow)) {
|
|
|
|
// if the window isn't visible, for instance because it is a hidden tab,
|
|
|
|
// update the current focus and scroll it into view but don't do anything else
|
|
|
|
if (CheckIfFocusable(aContent, aFlags)) {
|
|
|
|
aWindow->SetFocusedNode(aContent, focusMethod);
|
|
|
|
if (aFocusChanged)
|
|
|
|
ScrollIntoView(presShell, aContent, aFlags);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool clearFirstFocusEvent = PR_FALSE;
|
|
|
|
if (!mFirstFocusEvent) {
|
|
|
|
mFirstFocusEvent = aContent;
|
|
|
|
clearFirstFocusEvent = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
PRINTTAGF("**Element %s has been focused", aContent);
|
|
|
|
nsCOMPtr<nsIDocument> docm = do_QueryInterface(aWindow->GetExtantDocument());
|
|
|
|
if (docm)
|
|
|
|
PRINTTAGF(" from %s", docm->GetRootContent());
|
|
|
|
printf(" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %d]\n",
|
|
|
|
aIsNewDocument, aFocusChanged, aWindowRaised, aFlags);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// if this is a new document, update the parent chain of frames so that
|
|
|
|
// focus can be traversed from the top level down to the newly focused
|
|
|
|
// window.
|
|
|
|
if (aIsNewDocument)
|
|
|
|
AdjustWindowFocus(aWindow);
|
|
|
|
|
|
|
|
// indicate that the window has taken focus.
|
|
|
|
if (aWindow->TakeFocus(PR_TRUE, focusMethod))
|
|
|
|
aIsNewDocument = PR_TRUE;
|
|
|
|
|
|
|
|
mFocusedWindow = aWindow;
|
|
|
|
|
|
|
|
// update the system focus.
|
|
|
|
nsIViewManager* vm = presShell->GetViewManager();
|
|
|
|
if (vm) {
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
2009-07-21 17:45:05 -07:00
|
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (widget)
|
|
|
|
widget->SetFocus(PR_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if switching to a new document, first fire the focus event on the
|
|
|
|
// document and then the window.
|
|
|
|
if (aIsNewDocument) {
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aWindow->GetExtantDocument());
|
2009-06-22 12:49:10 -07:00
|
|
|
if (doc)
|
|
|
|
SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, doc,
|
|
|
|
doc, aFlags & FOCUSMETHOD_MASK);
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (mFocusedWindow == aWindow && mFocusedContent == nsnull)
|
|
|
|
SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, doc,
|
|
|
|
aWindow, aFlags & FOCUSMETHOD_MASK);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check to ensure that the element is still focusable, and that nothing
|
|
|
|
// else was focused during the events above.
|
|
|
|
if (CheckIfFocusable(aContent, aFlags) &&
|
|
|
|
mFocusedWindow == aWindow && mFocusedContent == nsnull) {
|
|
|
|
mFocusedContent = aContent;
|
|
|
|
aWindow->SetFocusedNode(aContent, focusMethod);
|
|
|
|
|
|
|
|
// don't fire events on the root content
|
|
|
|
PRBool isRootContent = aContent &&
|
|
|
|
aContent->IsInDoc() &&
|
|
|
|
aContent == aContent->GetCurrentDoc()->GetRootContent();
|
|
|
|
if (!isRootContent) {
|
|
|
|
// if the focused element changed, scroll it into view
|
|
|
|
if (aFocusChanged)
|
|
|
|
ScrollIntoView(presShell, aContent, aFlags);
|
|
|
|
|
|
|
|
// inform the EventStateManager so that content state change notifications
|
|
|
|
// are made.
|
|
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
|
|
presContext->EventStateManager()->SetContentState(aContent, NS_EVENT_STATE_FOCUS);
|
|
|
|
|
|
|
|
// if this is an object/plug-in, focus the plugin's widget
|
|
|
|
nsIFrame* contentFrame = presShell->GetPrimaryFrameFor(aContent);
|
|
|
|
nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
|
|
|
|
if (objectFrame) {
|
2009-07-21 17:45:00 -07:00
|
|
|
nsIWidget* widget = objectFrame->GetWidget();
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (widget)
|
|
|
|
widget->SetFocus(PR_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIMEStateManager::OnChangeFocus(presContext, aContent);
|
|
|
|
|
|
|
|
// as long as this focus wasn't because a window was raised, update the
|
|
|
|
// commands
|
|
|
|
// XXXndeakin P2 someone could adjust the focus during the update
|
|
|
|
if (!aWindowRaised)
|
|
|
|
aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
|
|
|
|
|
|
|
|
SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, aContent->GetCurrentDoc(),
|
|
|
|
aContent, aFlags & FOCUSMETHOD_MASK);
|
|
|
|
|
|
|
|
nsIMEStateManager::OnTextStateFocus(presContext, aContent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
|
|
nsIMEStateManager::OnTextStateBlur(presContext, nsnull);
|
|
|
|
nsIMEStateManager::OnChangeFocus(presContext, nsnull);
|
|
|
|
|
|
|
|
if (!aWindowRaised)
|
|
|
|
aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// update the caret visibility and position to match the newly focused
|
|
|
|
// element. However, don't update the position if this was a focus due to a
|
|
|
|
// mouse click as the selection code would already have moved the caret as
|
|
|
|
// needed. If this is a different document than was focused before, also
|
|
|
|
// update the caret's visibility. If this is the same document, the caret
|
|
|
|
// visibility should be the same as before so there is no need to update it.
|
|
|
|
if (mFocusedContent == aContent)
|
|
|
|
UpdateCaret(aFocusChanged && !(aFlags & FLAG_BYMOUSE), aIsNewDocument,
|
|
|
|
mFocusedContent);
|
|
|
|
|
|
|
|
if (clearFirstFocusEvent)
|
|
|
|
mFirstFocusEvent = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::SendFocusOrBlurEvent(PRUint32 aType,
|
|
|
|
nsIPresShell* aPresShell,
|
|
|
|
nsIDocument* aDocument,
|
|
|
|
nsISupports* aTarget,
|
|
|
|
PRUint32 aFocusMethod)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aType == NS_FOCUS_CONTENT || aType == NS_BLUR_CONTENT,
|
|
|
|
"Wrong event type for SendFocusOrBlurEvent");
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMEventTarget> eventTarget = do_QueryInterface(aTarget);
|
|
|
|
|
|
|
|
// for focus events, if this event was from a mouse or key and event
|
|
|
|
// handling on the document is suppressed, queue the event and fire it
|
|
|
|
// later. For blur events, a non-zero value would be set for aFocusMethod.
|
2009-06-22 12:49:10 -07:00
|
|
|
if (aFocusMethod && aDocument && aDocument->EventHandlingSuppressed()) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
for (PRUint32 i = mDelayedBlurFocusEvents.Length(); i > 0; --i) {
|
|
|
|
// if this event was already queued, remove it and append it to the end
|
|
|
|
if (mDelayedBlurFocusEvents[i - 1].mType == aType &&
|
|
|
|
mDelayedBlurFocusEvents[i - 1].mPresShell == aPresShell &&
|
|
|
|
mDelayedBlurFocusEvents[i - 1].mDocument == aDocument &&
|
|
|
|
mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget) {
|
|
|
|
mDelayedBlurFocusEvents.RemoveElementAt(i - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mDelayedBlurFocusEvents.AppendElement(
|
|
|
|
nsDelayedBlurOrFocusEvent(aType, aPresShell, aDocument, eventTarget));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsPresContext> presContext = aPresShell->GetPresContext();
|
|
|
|
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
|
|
nsEvent event(PR_TRUE, aType);
|
|
|
|
event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
|
|
|
|
|
|
|
|
nsEventDispatcher::Dispatch(aTarget, presContext, &event, nsnull, &status);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::ScrollIntoView(nsIPresShell* aPresShell,
|
|
|
|
nsIContent* aContent,
|
|
|
|
PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
// if the noscroll flag isn't set, scroll the newly focused element into view
|
|
|
|
if (!(aFlags & FLAG_NOSCROLL))
|
|
|
|
aPresShell->ScrollContentIntoView(aContent,
|
|
|
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
|
|
|
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::RaiseWindow(nsPIDOMWindow* aWindow)
|
|
|
|
{
|
|
|
|
// don't raise windows that are already raised or are in the process of
|
|
|
|
// being lowered
|
2009-07-13 04:55:56 -07:00
|
|
|
if (!aWindow || aWindow == mActiveWindow || aWindow == mWindowBeingLowered)
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
return;
|
|
|
|
|
2009-07-13 04:55:56 -07:00
|
|
|
#ifdef XP_WIN
|
|
|
|
// Windows would rather we focus the child widget, otherwise, the toplevel
|
|
|
|
// widget will always end up being focused. Fortunately, focusing the child
|
|
|
|
// widget will also have the effect of raising the window this widget is in.
|
|
|
|
// But on other platforms, we can just focus the toplevel widget to raise
|
|
|
|
// the window.
|
|
|
|
nsCOMPtr<nsPIDOMWindow> childWindow;
|
|
|
|
GetFocusedDescendant(aWindow, PR_TRUE, getter_AddRefs(childWindow));
|
|
|
|
if (!childWindow)
|
|
|
|
childWindow = aWindow;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
|
|
|
if (!docShell)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
docShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (!presShell)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsIViewManager* vm = presShell->GetViewManager();
|
|
|
|
if (vm) {
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
2009-07-21 17:45:05 -07:00
|
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
2009-07-13 04:55:56 -07:00
|
|
|
if (widget)
|
|
|
|
widget->SetFocus(PR_TRUE);
|
|
|
|
}
|
|
|
|
#else
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
|
|
|
|
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(webnav);
|
|
|
|
if (treeOwnerAsWin) {
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
|
|
treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
|
|
|
|
if (widget)
|
|
|
|
widget->SetFocus(PR_TRUE);
|
|
|
|
}
|
2009-07-13 04:55:56 -07:00
|
|
|
#endif
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::UpdateCaret(PRBool aMoveCaretToFocus,
|
|
|
|
PRBool aUpdateVisibility,
|
|
|
|
nsIContent* aContent)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_FOCUS
|
|
|
|
printf("Update Caret: %d %d\n", aMoveCaretToFocus, aUpdateVisibility);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!mFocusedWindow)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// this is called when a document is focused or when the caretbrowsing
|
|
|
|
// preference is changed
|
|
|
|
nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(focusedDocShell);
|
|
|
|
if (!dsti)
|
|
|
|
return;
|
|
|
|
|
|
|
|
PRInt32 itemType;
|
|
|
|
dsti->GetItemType(&itemType);
|
|
|
|
if (itemType == nsIDocShellTreeItem::typeChrome)
|
|
|
|
return; // Never browse with caret in chrome
|
|
|
|
|
|
|
|
PRPackedBool browseWithCaret =
|
|
|
|
nsContentUtils::GetBoolPref("accessibility.browsewithcaret");
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
focusedDocShell->GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (!presShell)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If this is an editable document which isn't contentEditable, or a
|
|
|
|
// contentEditable document and the node to focus is contentEditable,
|
|
|
|
// return, so that we don't mess with caret visibility.
|
|
|
|
PRBool isEditable = PR_FALSE;
|
|
|
|
nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(dsti));
|
|
|
|
if (editorDocShell) {
|
|
|
|
editorDocShell->GetEditable(&isEditable);
|
|
|
|
|
|
|
|
if (isEditable) {
|
|
|
|
nsCOMPtr<nsIHTMLDocument> doc =
|
|
|
|
do_QueryInterface(presShell->GetDocument());
|
|
|
|
|
|
|
|
PRBool isContentEditableDoc =
|
|
|
|
doc && doc->GetEditingState() == nsIHTMLDocument::eContentEditable;
|
|
|
|
|
|
|
|
PRBool isFocusEditable =
|
|
|
|
aContent && aContent->HasFlag(NODE_IS_EDITABLE);
|
|
|
|
if (!isContentEditableDoc || isFocusEditable)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isEditable && aMoveCaretToFocus)
|
|
|
|
MoveCaretToFocus(presShell, aContent);
|
|
|
|
|
|
|
|
if (!aUpdateVisibility)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// XXXndeakin this doesn't seem right. It should be checking for this only
|
|
|
|
// on the nearest ancestor frame which is a chrome frame. But this is
|
|
|
|
// what the existing code does, so just leave it for now.
|
|
|
|
if (!browseWithCaret) {
|
|
|
|
nsCOMPtr<nsIContent> docContent =
|
|
|
|
do_QueryInterface(mFocusedWindow->GetFrameElementInternal());
|
|
|
|
if (docContent)
|
|
|
|
browseWithCaret = docContent->AttrValueIs(kNameSpaceID_None,
|
|
|
|
nsGkAtoms::showcaret,
|
|
|
|
NS_LITERAL_STRING("true"),
|
|
|
|
eCaseMatters);
|
|
|
|
}
|
|
|
|
|
|
|
|
SetCaretVisible(presShell, browseWithCaret, aContent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::MoveCaretToFocus(nsIPresShell* aPresShell, nsIContent* aContent)
|
|
|
|
{
|
|
|
|
// rangeDoc is a document interface we can create a range with
|
|
|
|
nsCOMPtr<nsIDOMDocumentRange> rangeDoc(do_QueryInterface(aPresShell->GetDocument()));
|
|
|
|
if (rangeDoc) {
|
|
|
|
nsCOMPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
|
|
|
|
nsCOMPtr<nsISelection> domSelection = frameSelection->
|
|
|
|
GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
|
|
if (domSelection) {
|
|
|
|
nsCOMPtr<nsIDOMNode> currentFocusNode(do_QueryInterface(aContent));
|
|
|
|
// First clear the selection. This way, if there is no currently focused
|
|
|
|
// content, the selection will just be cleared.
|
|
|
|
domSelection->RemoveAllRanges();
|
|
|
|
if (currentFocusNode) {
|
|
|
|
nsCOMPtr<nsIDOMRange> newRange;
|
|
|
|
nsresult rv = rangeDoc->CreateRange(getter_AddRefs(newRange));
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
// Set the range to the start of the currently focused node
|
|
|
|
// Make sure it's collapsed
|
|
|
|
newRange->SelectNodeContents(currentFocusNode);
|
|
|
|
nsCOMPtr<nsIDOMNode> firstChild;
|
|
|
|
currentFocusNode->GetFirstChild(getter_AddRefs(firstChild));
|
|
|
|
if (!firstChild ||
|
|
|
|
aContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) {
|
|
|
|
// If current focus node is a leaf, set range to before the
|
|
|
|
// node by using the parent as a container.
|
|
|
|
// This prevents it from appearing as selected.
|
|
|
|
newRange->SetStartBefore(currentFocusNode);
|
|
|
|
newRange->SetEndBefore(currentFocusNode);
|
|
|
|
}
|
|
|
|
domSelection->AddRange(newRange);
|
|
|
|
domSelection->CollapseToStart();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsFocusManager::SetCaretVisible(nsIPresShell* aPresShell,
|
|
|
|
PRBool aVisible,
|
|
|
|
nsIContent* aContent)
|
|
|
|
{
|
|
|
|
// When browsing with caret, make sure caret is visible after new focus
|
|
|
|
// Return early if there is no caret. This can happen for the testcase
|
|
|
|
// for bug 308025 where a window is closed in a blur handler.
|
|
|
|
nsRefPtr<nsCaret> caret;
|
|
|
|
aPresShell->GetCaret(getter_AddRefs(caret));
|
|
|
|
if (!caret)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
PRBool caretVisible = PR_FALSE;
|
|
|
|
caret->GetCaretVisible(&caretVisible);
|
|
|
|
if (!aVisible && !caretVisible)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsFrameSelection> frameSelection;
|
|
|
|
if (aContent) {
|
|
|
|
nsIFrame *focusFrame = aPresShell->GetPrimaryFrameFor(aContent);
|
|
|
|
if (focusFrame)
|
|
|
|
frameSelection = focusFrame->GetFrameSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
|
|
|
|
|
|
|
|
if (docFrameSelection && caret &&
|
|
|
|
(frameSelection == docFrameSelection || !aContent)) {
|
|
|
|
nsISelection* domSelection = docFrameSelection->
|
|
|
|
GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
|
|
if (domSelection) {
|
|
|
|
// First, tell the caret which selection to use
|
|
|
|
caret->SetCaretDOMSelection(domSelection);
|
|
|
|
|
|
|
|
// In content, we need to set the caret. The only special case is edit
|
|
|
|
// fields, which have a different frame selection from the document.
|
|
|
|
// They will take care of making the caret visible themselves.
|
|
|
|
|
|
|
|
nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
|
|
|
|
if (!selCon)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
selCon->SetCaretEnabled(aVisible);
|
|
|
|
caret->SetCaretVisible(aVisible);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
|
|
|
|
nsIPresShell* aPresShell,
|
|
|
|
nsIContent **aStartContent,
|
|
|
|
nsIContent **aEndContent)
|
|
|
|
{
|
|
|
|
*aStartContent = *aEndContent = nsnull;
|
|
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsPresContext* presContext = aPresShell->GetPresContext();
|
|
|
|
NS_ASSERTION(presContext, "mPresContent is null!!");
|
|
|
|
|
|
|
|
nsCOMPtr<nsFrameSelection> frameSelection;
|
|
|
|
frameSelection = aPresShell->FrameSelection();
|
|
|
|
|
|
|
|
nsCOMPtr<nsISelection> domSelection;
|
|
|
|
if (frameSelection) {
|
|
|
|
domSelection = frameSelection->
|
|
|
|
GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
|
|
|
PRBool isCollapsed = PR_FALSE;
|
|
|
|
nsCOMPtr<nsIContent> startContent, endContent;
|
|
|
|
PRInt32 startOffset = 0;
|
|
|
|
if (domSelection) {
|
|
|
|
domSelection->GetIsCollapsed(&isCollapsed);
|
|
|
|
nsCOMPtr<nsIDOMRange> domRange;
|
|
|
|
rv = domSelection->GetRangeAt(0, getter_AddRefs(domRange));
|
|
|
|
if (domRange) {
|
|
|
|
PRInt32 startOffset;
|
|
|
|
domRange->GetStartContainer(getter_AddRefs(startNode));
|
|
|
|
domRange->GetEndContainer(getter_AddRefs(endNode));
|
|
|
|
domRange->GetStartOffset(&startOffset);
|
|
|
|
|
|
|
|
nsIContent *childContent = nsnull;
|
|
|
|
|
|
|
|
startContent = do_QueryInterface(startNode);
|
|
|
|
if (startContent && startContent->IsNodeOfType(nsINode::eELEMENT)) {
|
|
|
|
NS_ASSERTION(startOffset >= 0, "Start offset cannot be negative");
|
|
|
|
childContent = startContent->GetChildAt(startOffset);
|
|
|
|
if (childContent) {
|
|
|
|
startContent = childContent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
endContent = do_QueryInterface(endNode);
|
|
|
|
if (endContent && endContent->IsNodeOfType(nsINode::eELEMENT)) {
|
|
|
|
PRInt32 endOffset = 0;
|
|
|
|
domRange->GetEndOffset(&endOffset);
|
|
|
|
NS_ASSERTION(endOffset >= 0, "End offset cannot be negative");
|
|
|
|
childContent = endContent->GetChildAt(endOffset);
|
|
|
|
if (childContent) {
|
|
|
|
endContent = childContent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rv = NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame *startFrame = nsnull;
|
|
|
|
if (startContent) {
|
|
|
|
startFrame = aPresShell->GetPrimaryFrameFor(startContent);
|
|
|
|
if (isCollapsed) {
|
|
|
|
// Next check to see if our caret is at the very end of a node
|
|
|
|
// If so, the caret is actually sitting in front of the next
|
|
|
|
// logical frame's primary node - so for this case we need to
|
|
|
|
// change caretContent to that node.
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(startContent));
|
|
|
|
PRUint16 nodeType;
|
|
|
|
domNode->GetNodeType(&nodeType);
|
|
|
|
|
|
|
|
if (nodeType == nsIDOMNode::TEXT_NODE) {
|
|
|
|
nsAutoString nodeValue;
|
|
|
|
domNode->GetNodeValue(nodeValue);
|
|
|
|
|
|
|
|
PRBool isFormControl =
|
|
|
|
startContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL);
|
|
|
|
|
|
|
|
if (nodeValue.Length() == (PRUint32)startOffset && !isFormControl &&
|
|
|
|
startContent != aDocument->GetRootContent()) {
|
|
|
|
// Yes, indeed we were at the end of the last node
|
|
|
|
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
|
|
|
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
|
|
|
presContext, startFrame,
|
|
|
|
eLeaf,
|
|
|
|
PR_FALSE, // aVisual
|
|
|
|
PR_FALSE, // aLockInScrollView
|
|
|
|
PR_TRUE // aFollowOOFs
|
|
|
|
);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsIFrame *newCaretFrame = nsnull;
|
|
|
|
nsCOMPtr<nsIContent> newCaretContent = startContent;
|
|
|
|
PRBool endOfSelectionInStartNode(startContent == endContent);
|
|
|
|
do {
|
|
|
|
// Continue getting the next frame until the primary content for the frame
|
|
|
|
// we are on changes - we don't want to be stuck in the same place
|
|
|
|
frameTraversal->Next();
|
|
|
|
nsIFrame* newCaretFrame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
|
|
|
if (nsnull == newCaretFrame)
|
|
|
|
break;
|
|
|
|
newCaretContent = newCaretFrame->GetContent();
|
|
|
|
} while (!newCaretContent || newCaretContent == startContent);
|
|
|
|
|
|
|
|
if (newCaretFrame && newCaretContent) {
|
|
|
|
// If the caret is exactly at the same position of the new frame,
|
|
|
|
// then we can use the newCaretFrame and newCaretContent for our position
|
|
|
|
nsRefPtr<nsCaret> caret;
|
|
|
|
aPresShell->GetCaret(getter_AddRefs(caret));
|
|
|
|
nsRect caretRect;
|
|
|
|
nsIView *caretView;
|
|
|
|
caret->GetCaretCoordinates(nsCaret::eClosestViewCoordinates,
|
|
|
|
domSelection, &caretRect,
|
|
|
|
&isCollapsed, &caretView);
|
|
|
|
nsPoint framePt;
|
|
|
|
nsIView *frameClosestView = newCaretFrame->GetClosestView(&framePt);
|
|
|
|
if (caretView == frameClosestView && caretRect.y == framePt.y &&
|
|
|
|
caretRect.x == framePt.x) {
|
|
|
|
// The caret is at the start of the new element.
|
|
|
|
startFrame = newCaretFrame;
|
|
|
|
startContent = newCaretContent;
|
|
|
|
if (endOfSelectionInStartNode) {
|
|
|
|
endContent = newCaretContent; // Ensure end of selection is not before start
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*aStartContent = startContent;
|
|
|
|
*aEndContent = endContent;
|
|
|
|
NS_IF_ADDREF(*aStartContent);
|
|
|
|
NS_IF_ADDREF(*aEndContent);
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
|
|
|
nsIContent* aStartContent,
|
|
|
|
PRInt32 aType,
|
|
|
|
nsIContent** aNextContent)
|
|
|
|
{
|
|
|
|
*aNextContent = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
|
|
|
if (!docShell)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> startContent = aStartContent;
|
|
|
|
if (!startContent && aType != MOVEFOCUS_CARET)
|
|
|
|
startContent = aWindow->GetFocusedNode();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> doc;
|
|
|
|
if (startContent)
|
|
|
|
doc = startContent->GetCurrentDoc();
|
|
|
|
else
|
|
|
|
doc = do_QueryInterface(aWindow->GetExtantDocument());
|
|
|
|
if (!doc)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsILookAndFeel> lookNFeel(do_GetService(kLookAndFeelCID));
|
|
|
|
lookNFeel->GetMetric(nsILookAndFeel::eMetric_TabFocusModel,
|
|
|
|
nsIContent::sTabFocusModel);
|
|
|
|
|
|
|
|
if (aType == MOVEFOCUS_ROOT) {
|
|
|
|
NS_IF_ADDREF(*aNextContent = GetRootForFocus(aWindow, doc, PR_FALSE, PR_FALSE));
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
if (aType == MOVEFOCUS_FORWARDDOC) {
|
|
|
|
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(PR_TRUE));
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
if (aType == MOVEFOCUS_BACKWARDDOC) {
|
|
|
|
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(PR_FALSE));
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent* rootContent = doc->GetRootContent();
|
|
|
|
NS_ENSURE_TRUE(rootContent, NS_OK);
|
|
|
|
|
|
|
|
nsIPresShell *presShell = doc->GetPrimaryShell();
|
|
|
|
NS_ENSURE_TRUE(presShell, NS_OK);
|
|
|
|
|
|
|
|
if (aType == MOVEFOCUS_FIRST) {
|
|
|
|
if (!aStartContent)
|
|
|
|
startContent = rootContent;
|
|
|
|
return GetNextTabbableContent(presShell, startContent,
|
|
|
|
nsnull, startContent,
|
|
|
|
PR_TRUE, 1, PR_FALSE, aNextContent);
|
|
|
|
}
|
|
|
|
if (aType == MOVEFOCUS_LAST) {
|
|
|
|
if (!aStartContent)
|
|
|
|
startContent = rootContent;
|
|
|
|
return GetNextTabbableContent(presShell, startContent,
|
|
|
|
nsnull, startContent,
|
|
|
|
PR_FALSE, 0, PR_FALSE, aNextContent);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool forward = (aType == MOVEFOCUS_FORWARD);
|
|
|
|
PRBool doNavigation = PR_TRUE;
|
|
|
|
PRBool ignoreTabIndex = PR_FALSE;
|
|
|
|
// when a popup is open, we want to ensure that tab navigation occurs only
|
|
|
|
// within the most recently opened panel. If a popup is open, its frame will
|
|
|
|
// be stored in popupFrame.
|
|
|
|
nsIFrame* popupFrame = nsnull;
|
|
|
|
|
|
|
|
PRInt32 tabIndex = forward ? 1 : 0;
|
|
|
|
if (startContent) {
|
|
|
|
nsIFrame* frame = presShell->GetPrimaryFrameFor(startContent);
|
|
|
|
if (startContent->Tag() == nsGkAtoms::area &&
|
|
|
|
startContent->IsNodeOfType(nsINode::eHTML))
|
|
|
|
startContent->IsFocusable(&tabIndex);
|
|
|
|
else if (frame)
|
|
|
|
frame->IsFocusable(&tabIndex, 0);
|
|
|
|
|
|
|
|
// if the current element isn't tabbable, ignore the tabindex and just
|
|
|
|
// look for the next element. The root content won't have a tabindex
|
|
|
|
// so just treat this as the beginning of the tab order.
|
|
|
|
if (tabIndex < 0) {
|
|
|
|
tabIndex = 1;
|
|
|
|
if (startContent != rootContent)
|
|
|
|
ignoreTabIndex = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if the focus is currently inside a popup. Elements such as the
|
|
|
|
// autocomplete widget use the noautofocus attribute to allow the focus to
|
|
|
|
// remain outside the popup when it is opened.
|
|
|
|
if (frame) {
|
|
|
|
popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
|
|
|
|
nsGkAtoms::menuPopupFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (popupFrame) {
|
|
|
|
// Don't navigate outside of a popup, so pretend that the
|
|
|
|
// root content is the popup itself
|
|
|
|
rootContent = popupFrame->GetContent();
|
|
|
|
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
|
|
|
|
}
|
|
|
|
else if (!forward && startContent == rootContent) {
|
|
|
|
doNavigation = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
// if there is no focus, yet a panel is open, focus the first item in
|
|
|
|
// the panel
|
|
|
|
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
|
|
|
if (pm)
|
|
|
|
popupFrame = pm->GetTopPopup(ePopupTypePanel);
|
|
|
|
#endif
|
|
|
|
if (popupFrame) {
|
|
|
|
rootContent = popupFrame->GetContent();
|
|
|
|
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
|
|
|
|
startContent = rootContent;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Otherwise, for content shells, start from the location of the caret.
|
|
|
|
PRInt32 itemType;
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> shellItem = do_QueryInterface(docShell);
|
|
|
|
shellItem->GetItemType(&itemType);
|
|
|
|
if (itemType != nsIDocShellTreeItem::typeChrome) {
|
|
|
|
nsCOMPtr<nsIContent> endSelectionContent;
|
|
|
|
GetSelectionLocation(doc, presShell,
|
|
|
|
getter_AddRefs(startContent),
|
|
|
|
getter_AddRefs(endSelectionContent));
|
|
|
|
// If the selection is on the rootContent, then there is no selection
|
|
|
|
if (startContent == rootContent) {
|
|
|
|
startContent = nsnull;
|
|
|
|
}
|
|
|
|
else if (startContent && startContent->HasFlag(NODE_IS_EDITABLE)) {
|
|
|
|
// Don't start from the selection if the selection is in a
|
|
|
|
// contentEditable region.
|
|
|
|
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
|
|
|
|
if (htmlDoc &&
|
|
|
|
htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable)
|
|
|
|
startContent = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (startContent) {
|
|
|
|
if (aType == MOVEFOCUS_CARET) {
|
|
|
|
GetFocusInSelection(aWindow, startContent,
|
|
|
|
endSelectionContent, aNextContent);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// when starting from a selection, we always want to find the next or
|
|
|
|
// previous element in the document. So the tabindex on elements
|
|
|
|
// should be ignored.
|
|
|
|
ignoreTabIndex = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!startContent) {
|
|
|
|
// otherwise, just use the root content as the starting point
|
|
|
|
startContent = rootContent;
|
|
|
|
NS_ENSURE_TRUE(startContent, NS_OK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(startContent, "starting content not set");
|
|
|
|
|
|
|
|
// keep a reference to the starting content. If we find that again, it means
|
|
|
|
// we've iterated around completely and we don't want to adjust the focus.
|
|
|
|
// The skipOriginalContentCheck will be set to true only for the first time
|
|
|
|
// GetNextTabbableContent is called. This ensures that we don't break out
|
|
|
|
// when nothing is focused to start with. Specifically,
|
|
|
|
// GetNextTabbableContent first checks the root content -- which happens to
|
|
|
|
// be the same as the start content -- when nothing is focused and tabbing
|
|
|
|
// forward. Without skipOriginalContentCheck set to true, we'd end up
|
|
|
|
// returning right away and focusing nothing. Luckily, GetNextTabbableContent
|
|
|
|
// will never wrap around on its own, and can only return the original
|
|
|
|
// content when it is called a second time or later.
|
|
|
|
PRBool skipOriginalContentCheck = PR_TRUE;
|
|
|
|
nsIContent* originalStartContent = startContent;
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS_NAVIGATION
|
|
|
|
PRINTTAGF("Focus Navigation Start Content %s\n", startContent);
|
|
|
|
printf("[Tabindex: %d Ignore: %d]", tabIndex, ignoreTabIndex);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (doc) {
|
|
|
|
if (doNavigation) {
|
|
|
|
nsCOMPtr<nsIContent> nextFocus;
|
|
|
|
nsresult rv = GetNextTabbableContent(presShell, rootContent,
|
|
|
|
skipOriginalContentCheck ? nsnull : originalStartContent,
|
|
|
|
startContent, forward,
|
|
|
|
tabIndex, ignoreTabIndex,
|
|
|
|
getter_AddRefs(nextFocus));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// found a content node to focus.
|
|
|
|
if (nextFocus) {
|
|
|
|
#ifdef DEBUG_FOCUS_NAVIGATION
|
|
|
|
PRINTTAGF("Next Content: %s\n", nextFocus);
|
|
|
|
#endif
|
|
|
|
// as long as the found node was not the same as the starting node,
|
|
|
|
// set it as the return value.
|
|
|
|
if (nextFocus != originalStartContent)
|
|
|
|
NS_ADDREF(*aNextContent = nextFocus);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (popupFrame) {
|
|
|
|
// 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
|
|
|
|
if (startContent != rootContent) {
|
|
|
|
startContent = rootContent;
|
|
|
|
tabIndex = forward ? 1 : 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
doNavigation = PR_TRUE;
|
|
|
|
skipOriginalContentCheck = PR_FALSE;
|
|
|
|
ignoreTabIndex = PR_FALSE;
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
// first, get the frame element this window is inside.
|
|
|
|
nsCOMPtr<nsPIDOMWindow> piWindow = do_GetInterface(docShell);
|
|
|
|
NS_ENSURE_TRUE(piWindow, 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 = do_GetInterface(docShellParent);
|
|
|
|
NS_ENSURE_TRUE(piParentWindow, NS_ERROR_FAILURE);
|
|
|
|
doc = do_QueryInterface(piParentWindow->GetExtantDocument());
|
|
|
|
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
presShell = doc->GetPrimaryShell();
|
|
|
|
|
|
|
|
rootContent = doc->GetRootContent();
|
|
|
|
startContent = do_QueryInterface(piWindow->GetFrameElementInternal());
|
|
|
|
if (startContent) {
|
|
|
|
nsIFrame* frame = presShell->GetPrimaryFrameFor(startContent);
|
|
|
|
if (!frame)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
frame->IsFocusable(&tabIndex, 0);
|
|
|
|
if (tabIndex < 0) {
|
|
|
|
tabIndex = 1;
|
|
|
|
ignoreTabIndex = PR_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.
|
|
|
|
popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
|
|
|
|
nsGkAtoms::menuPopupFrame);
|
|
|
|
if (popupFrame) {
|
|
|
|
rootContent = popupFrame->GetContent();
|
|
|
|
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.
|
|
|
|
PRBool tookFocus;
|
|
|
|
docShell->TabToTreeOwner(forward, &tookFocus);
|
|
|
|
// if the tree owner, took the focus, blur the current content
|
|
|
|
if (tookFocus) {
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(docShell);
|
|
|
|
if (window->GetFocusedNode() == mFocusedContent)
|
|
|
|
Blur(mFocusedWindow, nsnull, PR_TRUE);
|
|
|
|
else
|
|
|
|
window->SetFocusedNode(nsnull);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset the tab index and start again from the beginning or end
|
|
|
|
startContent = rootContent;
|
|
|
|
tabIndex = forward ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// wrapped all the way around and didn't find anything to move the focus
|
|
|
|
// to, so just break out
|
|
|
|
if (startContent == originalStartContent)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
|
|
|
nsIContent* aRootContent,
|
|
|
|
nsIContent* aOriginalStartContent,
|
|
|
|
nsIContent* aStartContent,
|
|
|
|
PRBool aForward,
|
|
|
|
PRInt32 aCurrentTabIndex,
|
|
|
|
PRBool aIgnoreTabIndex,
|
|
|
|
nsIContent** aResultContent)
|
|
|
|
{
|
|
|
|
*aResultContent = nsnull;
|
|
|
|
|
|
|
|
if (!aStartContent)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS_NAVIGATION
|
|
|
|
PRINTTAGF("GetNextTabbable: %s", aStartContent);
|
|
|
|
printf(" tabindex: %d\n", aCurrentTabIndex);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsPresContext* presContext = aPresShell->GetPresContext();
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
nsIFrame* aStartFrame = aPresShell->GetPrimaryFrameFor(aStartContent);
|
|
|
|
if (!aStartFrame) {
|
|
|
|
// if there is no frame, just get the root frame
|
|
|
|
aStartFrame = aPresShell->GetPrimaryFrameFor(aRootContent);
|
|
|
|
if (!aStartFrame)
|
|
|
|
return NS_OK;
|
|
|
|
aStartContent = aRootContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
|
|
|
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
|
|
|
presContext, aStartFrame,
|
|
|
|
ePreOrder,
|
|
|
|
PR_FALSE, // aVisual
|
|
|
|
PR_FALSE, // aLockInScrollView
|
|
|
|
PR_TRUE // aFollowOOFs
|
|
|
|
);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (aStartContent == aRootContent) {
|
|
|
|
if (!aForward)
|
|
|
|
frameTraversal->Last();
|
|
|
|
}
|
|
|
|
else if (!aStartContent || aStartContent->Tag() != nsGkAtoms::area ||
|
|
|
|
!aStartContent->IsNodeOfType(nsINode::eHTML)) {
|
|
|
|
// Need to do special check in case we're in an imagemap which has multiple
|
|
|
|
// content nodes per frame, so don't skip over the starting frame.
|
|
|
|
if (aForward)
|
|
|
|
frameTraversal->Next();
|
|
|
|
else
|
|
|
|
frameTraversal->Prev();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk frames to find something tabbable matching mCurrentTabIndex
|
|
|
|
nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
|
|
|
while (frame) {
|
|
|
|
// 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.
|
|
|
|
// The returned computed tabindex from IsFocusable() is as follows:
|
|
|
|
// < 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
|
|
|
|
|
|
|
|
PRInt32 tabIndex;
|
|
|
|
frame->IsFocusable(&tabIndex, 0);
|
|
|
|
|
|
|
|
#ifdef DEBUG_FOCUS_NAVIGATION
|
|
|
|
if (frame->GetContent()) {
|
|
|
|
PRINTTAGF("Next Tabbable %s:", frame->GetContent());
|
|
|
|
printf(" with tabindex: %d expected: %d\n", tabIndex, aCurrentTabIndex);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsIContent* currentContent = frame->GetContent();
|
|
|
|
if (tabIndex >= 0) {
|
|
|
|
NS_ASSERTION(currentContent, "IsFocusable set a tabindex for a frame with no content");
|
|
|
|
if (currentContent->Tag() == 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.
|
|
|
|
nsIContent *areaContent =
|
|
|
|
GetNextTabbableMapArea(aForward, aCurrentTabIndex,
|
|
|
|
currentContent, aStartContent);
|
|
|
|
if (areaContent) {
|
|
|
|
NS_ADDREF(*aResultContent = areaContent);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (aIgnoreTabIndex || aCurrentTabIndex == tabIndex) {
|
|
|
|
// break out if we've wrapped around to the start again.
|
|
|
|
if (aOriginalStartContent && currentContent == aOriginalStartContent) {
|
|
|
|
NS_ADDREF(*aResultContent = currentContent);
|
|
|
|
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->GetCurrentDoc();
|
|
|
|
NS_ASSERTION(doc, "content not in document");
|
|
|
|
nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
|
|
|
|
if (subdoc) {
|
|
|
|
if (!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) {
|
|
|
|
*aResultContent = GetRootForFocus(subframe, subdoc, PR_FALSE, PR_TRUE);
|
|
|
|
if (*aResultContent) {
|
|
|
|
NS_ADDREF(*aResultContent);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nsIContent* rootContent = subdoc->GetRootContent();
|
|
|
|
nsIPresShell* subShell = subdoc->GetPrimaryShell();
|
|
|
|
if (rootContent && subShell) {
|
|
|
|
rv = GetNextTabbableContent(subShell, rootContent,
|
|
|
|
aOriginalStartContent, rootContent,
|
|
|
|
aForward, (aForward ? 1 : 0),
|
|
|
|
PR_FALSE, 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.
|
2009-08-04 11:03:39 -07:00
|
|
|
// 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 != aStartContent &&
|
|
|
|
(aForward || !GetRedirectedFocus(currentContent)))) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
NS_ADDREF(*aResultContent = currentContent);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (aOriginalStartContent && currentContent == aOriginalStartContent) {
|
|
|
|
// not focusable, so return if we have wrapped around to the original
|
|
|
|
// content. This is necessary in case the original starting content was
|
|
|
|
// not focusable.
|
|
|
|
NS_ADDREF(*aResultContent = currentContent);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move to the next or previous frame, but ignore continuation frames
|
|
|
|
// since only the first frame should be involved in focusability.
|
|
|
|
// Otherwise, a loop will occur in the following example:
|
|
|
|
// <span tabindex="1">...<a/><a/>...</span>
|
|
|
|
// where the text wraps onto multiple lines. Tabbing from the second
|
|
|
|
// link can find one of the span's continuation frames between the link
|
|
|
|
// and the end of the span, and the span would end up getting focused
|
|
|
|
// again.
|
|
|
|
do {
|
|
|
|
if (aForward)
|
|
|
|
frameTraversal->Next();
|
|
|
|
else
|
|
|
|
frameTraversal->Prev();
|
|
|
|
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
|
|
|
} while (frame && frame->GetPrevContinuation());
|
|
|
|
}
|
|
|
|
|
|
|
|
// If already at lowest priority tab (0), end search completely.
|
|
|
|
// 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.
|
|
|
|
if (!aForward) {
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = GetCurrentWindow(aRootContent);
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
|
|
|
NS_IF_ADDREF(*aResultContent =
|
|
|
|
GetRootForFocus(window, aRootContent->GetCurrentDoc(), PR_FALSE, PR_TRUE));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// continue looking for next highest priority tabindex
|
|
|
|
aCurrentTabIndex = GetNextTabIndex(aRootContent, aCurrentTabIndex, aForward);
|
|
|
|
aStartContent = aRootContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent*
|
|
|
|
nsFocusManager::GetNextTabbableMapArea(PRBool aForward,
|
|
|
|
PRInt32 aCurrentTabIndex,
|
|
|
|
nsIContent* aImageContent,
|
|
|
|
nsIContent* aStartContent)
|
|
|
|
{
|
|
|
|
nsAutoString useMap;
|
|
|
|
aImageContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, useMap);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> doc = aImageContent->GetDocument();
|
|
|
|
if (doc) {
|
|
|
|
nsCOMPtr<nsIDOMHTMLMapElement> imageMap = nsImageMapUtils::FindImageMap(doc, useMap);
|
|
|
|
if (!imageMap)
|
|
|
|
return nsnull;
|
|
|
|
nsCOMPtr<nsIContent> mapContent = do_QueryInterface(imageMap);
|
|
|
|
PRUint32 count = mapContent->GetChildCount();
|
|
|
|
// First see if the the start content is in this map
|
|
|
|
|
|
|
|
PRInt32 index = mapContent->IndexOf(aStartContent);
|
|
|
|
PRInt32 tabIndex;
|
|
|
|
if (index < 0 || (aStartContent->IsFocusable(&tabIndex) &&
|
|
|
|
tabIndex != aCurrentTabIndex)) {
|
|
|
|
// If aStartContent is in this map we must start iterating past it.
|
|
|
|
// We skip the case where aStartContent has tabindex == aStartContent
|
|
|
|
// since the next tab ordered element might be before it
|
|
|
|
// (or after for backwards) in the child list.
|
|
|
|
index = aForward ? -1 : (PRInt32)count;
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetChildAt will return nsnull if our index < 0 or index >= count
|
|
|
|
nsCOMPtr<nsIContent> areaContent;
|
|
|
|
while ((areaContent = mapContent->GetChildAt(aForward ? ++index : --index)) != nsnull) {
|
|
|
|
if (areaContent->IsFocusable(&tabIndex) && tabIndex == aCurrentTabIndex) {
|
|
|
|
return areaContent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32
|
|
|
|
nsFocusManager::GetNextTabIndex(nsIContent* aParent,
|
|
|
|
PRInt32 aCurrentTabIndex,
|
|
|
|
PRBool aForward)
|
|
|
|
{
|
|
|
|
PRInt32 tabIndex, childTabIndex;
|
|
|
|
nsIContent *child;
|
|
|
|
|
|
|
|
PRUint32 count = aParent->GetChildCount();
|
|
|
|
|
|
|
|
if (aForward) {
|
|
|
|
tabIndex = 0;
|
|
|
|
for (PRUint32 index = 0; index < count; index++) {
|
|
|
|
child = aParent->GetChildAt(index);
|
|
|
|
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
|
|
|
if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
|
|
|
|
tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoString tabIndexStr;
|
|
|
|
child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
|
|
|
|
PRInt32 ec, val = tabIndexStr.ToInteger(&ec);
|
|
|
|
if (NS_SUCCEEDED (ec) && val > aCurrentTabIndex && val != tabIndex) {
|
|
|
|
tabIndex = (tabIndex == 0 || val < tabIndex) ? val : tabIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { /* !aForward */
|
|
|
|
tabIndex = 1;
|
|
|
|
for (PRUint32 index = 0; index < count; index++) {
|
|
|
|
child = aParent->GetChildAt(index);
|
|
|
|
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
|
|
|
if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
|
|
|
|
(childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
|
|
|
|
tabIndex = childTabIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoString tabIndexStr;
|
|
|
|
child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
|
|
|
|
PRInt32 ec, val = tabIndexStr.ToInteger(&ec);
|
|
|
|
if (NS_SUCCEEDED (ec)) {
|
|
|
|
if ((aCurrentTabIndex == 0 && val > tabIndex) ||
|
|
|
|
(val < aCurrentTabIndex && val > tabIndex) ) {
|
|
|
|
tabIndex = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tabIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent*
|
|
|
|
nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
|
|
|
|
nsIDocument* aDocument,
|
|
|
|
PRBool aIsForDocNavigation,
|
|
|
|
PRBool 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<nsIContent> docContent =
|
|
|
|
do_QueryInterface(aWindow->GetFrameElementInternal());
|
|
|
|
if (docContent && docContent->Tag() == nsGkAtoms::iframe)
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
PRInt32 itemType;
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> shellItem = do_QueryInterface(aWindow->GetDocShell());
|
|
|
|
shellItem->GetItemType(&itemType);
|
|
|
|
|
|
|
|
if (itemType == nsIDocShellTreeItem::typeChrome)
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aCheckVisibility && !IsWindowVisible(aWindow))
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsIContent *rootContent = aDocument->GetRootContent();
|
|
|
|
if (rootContent) {
|
|
|
|
if (aCheckVisibility) {
|
|
|
|
nsIPresShell* presShell = aDocument->GetPrimaryShell();
|
|
|
|
if (!presShell || !presShell->GetPrimaryFrameFor(rootContent))
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, check if this is a frameset
|
|
|
|
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
|
|
|
|
if (htmlDoc) {
|
|
|
|
PRUint32 childCount = rootContent->GetChildCount();
|
|
|
|
for (PRUint32 i = 0; i < childCount; ++i) {
|
|
|
|
nsIContent *childContent = rootContent->GetChildAt(i);
|
|
|
|
nsINodeInfo *ni = childContent->NodeInfo();
|
|
|
|
if (childContent->IsNodeOfType(nsINode::eHTML) &&
|
|
|
|
ni->Equals(nsGkAtoms::frameset))
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rootContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::GetLastDocShell(nsIDocShellTreeItem* aItem,
|
|
|
|
nsIDocShellTreeItem** aResult)
|
|
|
|
{
|
|
|
|
*aResult = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
|
|
|
|
while (curItem) {
|
|
|
|
PRInt32 childCount = 0;
|
|
|
|
curItem->GetChildCount(&childCount);
|
|
|
|
if (!childCount) {
|
|
|
|
*aResult = curItem;
|
|
|
|
NS_ADDREF(*aResult);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
curItem->GetChildAt(childCount - 1, getter_AddRefs(curItem));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::GetNextDocShell(nsIDocShellTreeItem* aItem,
|
|
|
|
nsIDocShellTreeItem** aResult)
|
|
|
|
{
|
|
|
|
*aResult = nsnull;
|
|
|
|
|
|
|
|
PRInt32 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 (PRInt32 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 = nsnull;
|
|
|
|
|
|
|
|
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.
|
|
|
|
PRInt32 childCount = 0;
|
|
|
|
parentItem->GetChildCount(&childCount);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> prevItem, iterItem;
|
|
|
|
for (PRInt32 index = 0; index < childCount; ++index) {
|
|
|
|
parentItem->GetChildAt(index, getter_AddRefs(iterItem));
|
|
|
|
if (iterItem == aItem)
|
|
|
|
break;
|
|
|
|
prevItem = iterItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prevItem)
|
|
|
|
GetLastDocShell(prevItem, aResult);
|
|
|
|
else
|
|
|
|
NS_ADDREF(*aResult = parentItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent*
|
|
|
|
nsFocusManager::GetNextTabbableDocument(PRBool aForward)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> startItem;
|
|
|
|
if (mFocusedWindow) {
|
|
|
|
startItem = do_QueryInterface(mFocusedWindow->GetDocShell());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mActiveWindow);
|
|
|
|
startItem = do_QueryInterface(webnav);
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
curItem = nextItem;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> nextFrame = do_GetInterface(nextItem);
|
|
|
|
if (!nextFrame)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(nextFrame->GetExtantDocument());
|
|
|
|
if (doc && !doc->EventHandlingSuppressed()) {
|
|
|
|
content = GetRootForFocus(nextFrame, doc, PR_TRUE, PR_TRUE);
|
|
|
|
if (content && !GetRootForFocus(nextFrame, doc, PR_FALSE, PR_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;
|
|
|
|
nsIContent* rootContent = doc->GetRootContent();
|
|
|
|
nsIPresShell* presShell = doc->GetPrimaryShell();
|
|
|
|
if (presShell) {
|
|
|
|
nsresult rv = GetNextTabbableContent(presShell, rootContent,
|
|
|
|
nsnull, rootContent,
|
|
|
|
PR_TRUE, 1, PR_FALSE,
|
|
|
|
getter_AddRefs(nextFocus));
|
|
|
|
return NS_SUCCEEDED(rv) ? nextFocus.get() : nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (!content);
|
|
|
|
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsFocusManager::GetFocusInSelection(nsPIDOMWindow* aWindow,
|
|
|
|
nsIContent* aStartSelection,
|
|
|
|
nsIContent* aEndSelection,
|
|
|
|
nsIContent** aFocusedContent)
|
|
|
|
{
|
|
|
|
*aFocusedContent = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> testContent = aStartSelection;
|
|
|
|
nsCOMPtr<nsIContent> nextTestContent = aEndSelection;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> currentFocus = aWindow->GetFocusedNode();
|
|
|
|
|
|
|
|
// We now have the correct start node in selectionContent!
|
|
|
|
// Search for focusable elements, starting with selectionContent
|
|
|
|
|
|
|
|
// Method #1: Keep going up while we look - an ancestor might be focusable
|
|
|
|
// We could end the loop earlier, such as when we're no longer
|
|
|
|
// in the same frame, by comparing getPrimaryFrameFor(selectionContent)
|
|
|
|
// with a variable holding the starting selectionContent
|
|
|
|
while (testContent) {
|
|
|
|
// Keep testing while selectionContent is equal to something,
|
|
|
|
// eventually we'll run out of ancestors
|
|
|
|
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
if (testContent == currentFocus ||
|
|
|
|
testContent->IsLink(getter_AddRefs(uri))) {
|
|
|
|
NS_ADDREF(*aFocusedContent = testContent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the parent
|
|
|
|
testContent = testContent->GetParent();
|
|
|
|
|
|
|
|
if (!testContent) {
|
|
|
|
// We run this loop again, checking the ancestor chain of the selection's end point
|
|
|
|
testContent = nextTestContent;
|
|
|
|
nextTestContent = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We couldn't find an anchor that was an ancestor of the selection start
|
|
|
|
// Method #2: look for anchor in selection's primary range (depth first search)
|
|
|
|
|
|
|
|
// Turn into nodes so that we can use GetNextSibling() and GetFirstChild()
|
|
|
|
nsCOMPtr<nsIDOMNode> selectionNode(do_QueryInterface(aStartSelection));
|
|
|
|
nsCOMPtr<nsIDOMNode> endSelectionNode(do_QueryInterface(aEndSelection));
|
|
|
|
nsCOMPtr<nsIDOMNode> testNode;
|
|
|
|
|
|
|
|
do {
|
|
|
|
testContent = do_QueryInterface(selectionNode);
|
|
|
|
|
|
|
|
// We're looking for any focusable link that could be part of the
|
|
|
|
// main document's selection.
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
if (testContent == currentFocus ||
|
|
|
|
testContent->IsLink(getter_AddRefs(uri))) {
|
|
|
|
NS_ADDREF(*aFocusedContent = testContent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
selectionNode->GetFirstChild(getter_AddRefs(testNode));
|
|
|
|
if (testNode) {
|
|
|
|
selectionNode = testNode;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectionNode == endSelectionNode)
|
|
|
|
break;
|
|
|
|
selectionNode->GetNextSibling(getter_AddRefs(testNode));
|
|
|
|
if (testNode) {
|
|
|
|
selectionNode = testNode;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
selectionNode->GetParentNode(getter_AddRefs(testNode));
|
|
|
|
if (!testNode || testNode == endSelectionNode) {
|
|
|
|
selectionNode = nsnull;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
testNode->GetNextSibling(getter_AddRefs(selectionNode));
|
|
|
|
if (selectionNode)
|
|
|
|
break;
|
|
|
|
selectionNode = testNode;
|
|
|
|
} while (PR_TRUE);
|
|
|
|
}
|
|
|
|
while (selectionNode && selectionNode != endSelectionNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
NS_NewFocusManager(nsIFocusManager** aResult)
|
|
|
|
{
|
|
|
|
NS_IF_ADDREF(*aResult = nsFocusManager::GetFocusManager());
|
|
|
|
return NS_OK;
|
|
|
|
}
|