867 lines
25 KiB
C++
Executable File
867 lines
25 KiB
C++
Executable File
/* ***** 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 Spatial Navigation
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Douglas F. Turner II <dougt@meer.net>
|
|
* Portions created by the Initial Developer are Copyright (C) 2004-2005
|
|
* 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 "nsSpatialNavigationPrivate.h"
|
|
|
|
PRInt32 gRectFudge = 20;
|
|
PRInt32 gDirectionalBias = 1;
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsSpatialNavigation)
|
|
NS_INTERFACE_MAP_ENTRY(nsISpatialNavigation)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMKeyListener)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_ADDREF(nsSpatialNavigation)
|
|
NS_IMPL_RELEASE(nsSpatialNavigation)
|
|
|
|
|
|
nsSpatialNavigation::nsSpatialNavigation(nsSpatialNavigationService* aService)
|
|
{
|
|
NS_ASSERTION(aService, "Should not create this object without a valid service");
|
|
|
|
mService = aService; // back pointer -- no reference
|
|
|
|
mNavigationFramesState = PR_FALSE;
|
|
}
|
|
|
|
nsSpatialNavigation::~nsSpatialNavigation()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::HandleEvent(nsIDOMEvent* aEvent)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::KeyUp(nsIDOMEvent* aEvent)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::KeyPress(nsIDOMEvent* aEvent)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::KeyDown(nsIDOMEvent* aEvent)
|
|
{
|
|
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
PRBool enabled;
|
|
prefBranch->GetBoolPref("snav.enabled", &enabled);
|
|
if (!enabled) // this doesn't work. wtf? if (!mService->mEnabled)
|
|
return NS_OK;
|
|
|
|
|
|
nsCOMPtr<nsIDOMNSUIEvent> uiEvent(do_QueryInterface(aEvent));
|
|
if (uiEvent)
|
|
{
|
|
// If a web page wants to use the keys mapped to our
|
|
// move, they have to use evt.preventDefault() after
|
|
// they get the key
|
|
|
|
PRBool preventDefault;
|
|
uiEvent->GetPreventDefault(&preventDefault);
|
|
if (preventDefault)
|
|
return NS_OK;
|
|
}
|
|
|
|
PRInt32 formControlType = -1;
|
|
// check to see if we are in a text field.
|
|
// based on nsTypeAheadFind.
|
|
|
|
//nsEvent should be renamed.
|
|
nsCOMPtr<nsIDOMNSEvent> nsEvent = do_QueryInterface(aEvent);
|
|
if (!nsEvent)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIDOMEventTarget> domEventTarget;
|
|
nsEvent->GetOriginalTarget(getter_AddRefs(domEventTarget));
|
|
|
|
nsCOMPtr<nsIContent> targetContent = do_QueryInterface(domEventTarget);
|
|
|
|
if (targetContent->IsNodeOfType(nsINode::eXUL))
|
|
return NS_OK;
|
|
|
|
if (targetContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL))
|
|
{
|
|
nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(targetContent));
|
|
formControlType = formControl->GetType();
|
|
|
|
if (mService->mIgnoreTextFields)
|
|
{
|
|
if (formControlType == NS_FORM_TEXTAREA ||
|
|
formControlType == NS_FORM_INPUT_TEXT ||
|
|
formControlType == NS_FORM_INPUT_PASSWORD ||
|
|
formControlType == NS_FORM_INPUT_FILE)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
else if (mService->mIgnoreTextFields && targetContent->IsNodeOfType(nsINode::eHTML))
|
|
{
|
|
// Test for isindex, a deprecated kind of text field. We're using a string
|
|
// compare because <isindex> is not considered a form control, so it does
|
|
// not support nsIFormControl or eHTML_FORM_CONTROL, and it's not worth
|
|
// having a table of atoms just for it.
|
|
|
|
if (isContentOfType(targetContent, "isindex"))
|
|
return NS_OK;
|
|
}
|
|
|
|
PRUint32 keyCode;
|
|
PRBool isModifier;
|
|
nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
|
|
|
|
if (!keyEvent)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
if (NS_FAILED(keyEvent->GetKeyCode(&keyCode)))
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// figure out what modifier to use
|
|
|
|
/************************************************
|
|
Value of the keyCodeModifier is
|
|
|
|
SHIFT = 0x00100000
|
|
CONTROL = 0x00001100
|
|
ALT = 0x00000012
|
|
*************************************************/
|
|
|
|
|
|
if (mService->mKeyCodeModifier & 0x00100000)
|
|
{
|
|
if (NS_FAILED(keyEvent->GetShiftKey(&isModifier)))
|
|
return NS_ERROR_FAILURE;
|
|
if (!isModifier)
|
|
return NS_OK;
|
|
}
|
|
|
|
if (mService->mKeyCodeModifier & 0x00001100)
|
|
{
|
|
if (NS_FAILED(keyEvent->GetCtrlKey(&isModifier)))
|
|
return NS_ERROR_FAILURE;
|
|
if (!isModifier)
|
|
return NS_OK;
|
|
}
|
|
|
|
if (mService->mKeyCodeModifier & 0x00000012)
|
|
{
|
|
if (NS_FAILED(keyEvent->GetAltKey(&isModifier)))
|
|
return NS_ERROR_FAILURE;
|
|
if (!isModifier)
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
if (keyCode == mService->mKeyCodeLeft)
|
|
{
|
|
|
|
// ************************************************************************************
|
|
// NS_FORM_TEXTAREA cases:
|
|
|
|
// ************************************************************************************
|
|
// NS_FORM_INPUT_TEXT | NS_FORM_INPUT_PASSWORD | NS_FORM_INPUT_FILE cases
|
|
|
|
|
|
if (formControlType == NS_FORM_INPUT_TEXT ||
|
|
formControlType == NS_FORM_INPUT_PASSWORD)
|
|
{
|
|
PRInt32 selectionStart, textLength;
|
|
nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(targetContent);
|
|
if (input) {
|
|
input->GetSelectionStart (&selectionStart);
|
|
input->GetTextLength (&textLength);
|
|
} else {
|
|
nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea = do_QueryInterface(targetContent);
|
|
if (textArea) {
|
|
textArea->GetSelectionStart (&selectionStart);
|
|
textArea->GetTextLength (&textLength);
|
|
}
|
|
}
|
|
|
|
if (textLength != 0 && selectionStart != 0)
|
|
return NS_OK;
|
|
}
|
|
|
|
// We're using this key, no one else should
|
|
aEvent->StopPropagation();
|
|
aEvent->PreventDefault();
|
|
|
|
return Left();
|
|
}
|
|
|
|
if (keyCode == mService->mKeyCodeRight)
|
|
{
|
|
// ************************************************************************************
|
|
// NS_FORM_TEXTAREA cases:
|
|
|
|
// ************************************************************************************
|
|
// NS_FORM_INPUT_TEXT | NS_FORM_INPUT_PASSWORD | NS_FORM_INPUT_FILE cases
|
|
|
|
if (formControlType == NS_FORM_INPUT_TEXT ||
|
|
formControlType == NS_FORM_INPUT_PASSWORD)
|
|
{
|
|
PRInt32 selectionEnd, textLength;
|
|
nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(targetContent);
|
|
if (input) {
|
|
input->GetSelectionEnd (&selectionEnd);
|
|
input->GetTextLength (&textLength);
|
|
} else {
|
|
nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea = do_QueryInterface(targetContent);
|
|
if (textArea) {
|
|
textArea->GetSelectionEnd (&selectionEnd);
|
|
textArea->GetTextLength (&textLength);
|
|
}
|
|
}
|
|
|
|
// going down.
|
|
|
|
if (textLength != selectionEnd)
|
|
return NS_OK;
|
|
}
|
|
|
|
aEvent->StopPropagation();
|
|
aEvent->PreventDefault();
|
|
return Right();
|
|
}
|
|
|
|
if (keyCode == mService->mKeyCodeUp)
|
|
{
|
|
|
|
// If we are going up or down, in a select, lets not
|
|
// navigate.
|
|
//
|
|
// FIX: What we really want to do is determine if we are
|
|
// at the start or the end fo the form element, and
|
|
// based on the selected position we decide to nav. or
|
|
// not.
|
|
|
|
// ************************************************************************************
|
|
// NS_FORM_SELECT cases:
|
|
// * if it is a select form of 'size' attr != than '1' then we do as above.
|
|
|
|
// * if it is a select form of 'size' attr == than '1', snav can take care of it.
|
|
// if (formControlType == NS_FORM_SELECT)
|
|
// return NS_OK;
|
|
|
|
aEvent->StopPropagation();
|
|
aEvent->PreventDefault();
|
|
return Up();
|
|
}
|
|
|
|
if (keyCode == mService->mKeyCodeDown)
|
|
{
|
|
// If we are going up or down, in a select, lets not
|
|
// navigate.
|
|
//
|
|
// FIX: What we really want to do is determine if we are
|
|
// at the start or the end fo the form element, and
|
|
// based on the selected position we decide to nav. or
|
|
// not.
|
|
|
|
// ************************************************************************************
|
|
// NS_FORM_SELECT cases:
|
|
// * if it is a select form of 'size' attr != than '1' then we do as above.
|
|
|
|
// * if it is a select form of 'size' attr == than '1', snav can take care of it.
|
|
// if (formControlType == NS_FORM_SELECT)
|
|
// return NS_OK;
|
|
|
|
aEvent->StopPropagation(); // We're using this key, no one else should
|
|
aEvent->PreventDefault();
|
|
return Down();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::Init(nsIDOMWindow *aWindow)
|
|
{
|
|
mTopWindow = aWindow;
|
|
|
|
nsCOMPtr<nsIDOM3EventTarget> target;
|
|
nsCOMPtr<nsIDOMEventGroup> systemGroup;
|
|
nsresult rv = getEventTargetFromWindow(aWindow, getter_AddRefs(target), getter_AddRefs(systemGroup));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
target->AddGroupedEventListener(NS_LITERAL_STRING("keypress"),
|
|
static_cast<nsIDOMKeyListener*>(this),
|
|
PR_FALSE,
|
|
systemGroup);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::Shutdown()
|
|
{
|
|
nsCOMPtr<nsIDOM3EventTarget> target;
|
|
nsCOMPtr<nsIDOMEventGroup> systemGroup;
|
|
nsresult rv = getEventTargetFromWindow(mTopWindow, getter_AddRefs(target), getter_AddRefs(systemGroup));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
target->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"),
|
|
static_cast<nsIDOMKeyListener*>(this),
|
|
PR_FALSE,
|
|
systemGroup);
|
|
mTopWindow = nsnull;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::Up()
|
|
{
|
|
return handleMove(eNavUp);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::Down()
|
|
{
|
|
return handleMove(eNavDown);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::Left()
|
|
{
|
|
return handleMove(eNavLeft);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::Right()
|
|
{
|
|
return handleMove(eNavRight);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSpatialNavigation::GetAttachedWindow(nsIDOMWindow * *aAttachedWindow)
|
|
{
|
|
NS_IF_ADDREF(*aAttachedWindow = mTopWindow);
|
|
return NS_OK;
|
|
}
|
|
|
|
void DoTraversal(int aDirection,
|
|
nsIBidirectionalEnumerator* aFrameTraversal,
|
|
nsIFrame* aFocusedFrame,
|
|
nsRect& aFocusedRect,
|
|
PRBool isAREA,
|
|
PRBool focusDocuments,
|
|
nsPresContext* aPresContext,
|
|
PRInt64* aDSoFar,
|
|
nsIContent** aCurrentContent)
|
|
{
|
|
|
|
*aCurrentContent = nsnull;
|
|
|
|
// There are some rects with zero width or height.
|
|
// calculating distance from a non point is troublesome.
|
|
// we will fix this up here by setting such a point to at
|
|
// least 1 px.
|
|
|
|
if (aFocusedRect.width == 0) {
|
|
aFocusedRect.width = 1;
|
|
}
|
|
|
|
if (aFocusedRect.height == 0) {
|
|
aFocusedRect.height = 1;
|
|
}
|
|
|
|
nsIFrame* frame;
|
|
nsRect frameRect;
|
|
|
|
PRInt64 d;
|
|
|
|
while (1)
|
|
{
|
|
aFrameTraversal->Next();
|
|
|
|
nsISupports* currentItem;
|
|
aFrameTraversal->CurrentItem(¤tItem);
|
|
frame = static_cast<nsIFrame*>(currentItem);
|
|
|
|
if (!frame)
|
|
break;
|
|
#ifdef DEBUG_outputframes
|
|
printf("got frame %x\n", frame);
|
|
#endif
|
|
// So, here we want to make sure that the frame that we
|
|
// nav to isn't part of the flow of the currently
|
|
// focused frame
|
|
|
|
if (!isAREA)
|
|
{
|
|
nsIFrame* flowFrame = aFocusedFrame;
|
|
PRBool currentFrameIsFocusedFrame = PR_FALSE;
|
|
while (flowFrame)
|
|
{
|
|
if (flowFrame == frame)
|
|
{
|
|
currentFrameIsFocusedFrame = PR_TRUE;
|
|
break;
|
|
}
|
|
flowFrame = flowFrame->GetNextInFlow();
|
|
}
|
|
if (currentFrameIsFocusedFrame)
|
|
continue;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// a special case for area's which are not enumerated by the nsIFrameTraversal
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
if (isMap(frame))
|
|
{
|
|
nsIContent* c = frame->GetContent();
|
|
nsCOMPtr<nsIDOMHTMLMapElement> element = do_QueryInterface(c);
|
|
if (!element)
|
|
continue;
|
|
|
|
nsCOMPtr<nsIDOMHTMLCollection> mapAreas;
|
|
element->GetAreas(getter_AddRefs(mapAreas));
|
|
if (!mapAreas)
|
|
continue;
|
|
|
|
PRUint32 length;
|
|
mapAreas->GetLength(&length);
|
|
|
|
for (PRUint32 i = 0; i < length; i++)
|
|
{
|
|
nsCOMPtr<nsIDOMNode> domNode;
|
|
mapAreas->Item(i,getter_AddRefs(domNode));
|
|
|
|
nsCOMPtr<nsIDOMHTMLAreaElement> e = do_QueryInterface(domNode);
|
|
nsCOMPtr<nsIContent> content= do_QueryInterface(domNode);
|
|
getFrameForContent(content, &frame);
|
|
|
|
getRectOfAreaElement(frame, e, &frameRect);
|
|
|
|
if (!isRectInDirection(aDirection, aFocusedRect, frameRect))
|
|
continue;
|
|
|
|
d = spatialDistance(aDirection, aFocusedRect, frameRect);
|
|
|
|
if ((*aDSoFar) > d)
|
|
{
|
|
(*aDSoFar) = d;
|
|
NS_IF_RELEASE(*aCurrentContent);
|
|
NS_ADDREF(*aCurrentContent = content);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// we don't want to worry about the same frame if we
|
|
// aren't an area
|
|
if (frame == aFocusedFrame ||
|
|
(aFocusedFrame && (frame->GetContent() == aFocusedFrame->GetContent())))
|
|
continue;
|
|
|
|
// make sure the frame is targetable for focus
|
|
if (!isTargetable(focusDocuments, frame))
|
|
continue;
|
|
|
|
// RECT !!
|
|
frameRect = makeRectRelativeToGlobalView(frame);
|
|
|
|
// no frames without size and be navigated to
|
|
// successfully.
|
|
if (frameRect.width == 0 || frameRect.height == 0)
|
|
continue;
|
|
|
|
// deflate the rect to avoid overlapping with other
|
|
// rects.
|
|
frameRect.Deflate(gRectFudge, gRectFudge);
|
|
|
|
if (!isRectInDirection(aDirection, aFocusedRect, frameRect))
|
|
continue;
|
|
|
|
d = spatialDistance(aDirection, aFocusedRect, frameRect);
|
|
|
|
if ((*aDSoFar) >= d)
|
|
{
|
|
#ifdef DEBUG_dougt
|
|
if (d == 0)
|
|
{
|
|
printf("there is overlapping content;\n");
|
|
}
|
|
#endif
|
|
(*aDSoFar) = d;
|
|
NS_IF_RELEASE(*aCurrentContent);
|
|
NS_ADDREF(*aCurrentContent = frame->GetContent());
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void centerRect(int aDirection, nsRect& aRect)
|
|
{
|
|
if (aDirection == eNavLeft)
|
|
aRect.x = 1000000;
|
|
else if (aDirection == eNavRight)
|
|
aRect.x = 0;
|
|
else if (aDirection == eNavUp)
|
|
aRect.y = 1000000;
|
|
else
|
|
aRect.y = 0;
|
|
|
|
aRect.height = 1;
|
|
aRect.width = 1;
|
|
}
|
|
|
|
nsresult
|
|
nsSpatialNavigation::getContentInDirection(int aDirection,
|
|
nsPresContext* aPresContext,
|
|
nsRect& aFocusedRect,
|
|
nsIFrame* aFocusedFrame,
|
|
PRBool aIsAREA,
|
|
PRBool aFocusDocuments,
|
|
nsIContent** aContent)
|
|
{
|
|
|
|
// Check to see if we should decend into subdoc
|
|
nsIContent* subFrameContent = aFocusedFrame->GetContent();
|
|
nsCOMPtr<nsIDOMHTMLHtmlElement> hhElement = do_QueryInterface(subFrameContent);
|
|
nsCOMPtr<nsIDOMHTMLIFrameElement> iFrameElement = do_QueryInterface(subFrameContent);
|
|
|
|
if ( (hhElement || iFrameElement) && mNavigationFramesState)
|
|
{
|
|
aPresContext = getPresContext(subFrameContent);
|
|
centerRect(aDirection, aFocusedRect);
|
|
}
|
|
|
|
|
|
nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
|
|
nsresult result = createFrameTraversal(aPresContext,
|
|
ePreOrder,
|
|
PR_FALSE, // aVisual
|
|
PR_FALSE, // aLockInScrollView
|
|
PR_TRUE, // aFollowOOFs
|
|
getter_AddRefs(frameTraversal));
|
|
if (NS_FAILED(result))
|
|
return result;
|
|
|
|
nsCOMPtr<nsIContent> currentContent;
|
|
PRInt64 currentDistance = LL_MaxInt();
|
|
|
|
DoTraversal(aDirection,
|
|
frameTraversal,
|
|
aFocusedFrame,
|
|
aFocusedRect,
|
|
aIsAREA,
|
|
aFocusDocuments,
|
|
aPresContext,
|
|
¤tDistance,
|
|
aContent);
|
|
|
|
|
|
if ( (hhElement || iFrameElement) && mNavigationFramesState)
|
|
{
|
|
mNavigationFramesState = PR_FALSE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsSpatialNavigation::handleMove(int direction)
|
|
{
|
|
nsCOMPtr<nsIContent> focusedContent;
|
|
getFocusedContent(direction, getter_AddRefs(focusedContent));
|
|
|
|
// there are some websites which have no focusable elements,
|
|
// only text, for example. In these cases, scrolling have to be
|
|
// performed by snav.
|
|
if (!focusedContent) {
|
|
ScrollWindow(direction, getContentWindow());
|
|
return NS_OK;
|
|
}
|
|
nsPresContext* presContext = getPresContext(focusedContent);
|
|
if(!presContext)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsIFrame* focusedFrame;
|
|
getFrameForContent(focusedContent, &focusedFrame);
|
|
|
|
nsRect focusedRect;
|
|
PRBool isAREA = isArea(focusedContent);
|
|
if (!isAREA)
|
|
{
|
|
// RECT !!
|
|
focusedRect = makeRectRelativeToGlobalView(focusedFrame);
|
|
|
|
// deflate the rect to avoid overlapping with other
|
|
// rects.
|
|
focusedRect.Deflate(gRectFudge, gRectFudge);
|
|
}
|
|
else
|
|
{
|
|
nsCOMPtr<nsIDOMHTMLAreaElement> e = do_QueryInterface(focusedContent);
|
|
getRectOfAreaElement(focusedFrame, e, &focusedRect);
|
|
}
|
|
|
|
nsCOMPtr<nsIContent> c;
|
|
getContentInDirection(direction, presContext, focusedRect, focusedFrame, PR_FALSE, isAREA, getter_AddRefs(c));
|
|
|
|
if (c) {
|
|
|
|
nsIDocument* doc = c->GetDocument();
|
|
if (!doc)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
/* nsIPresShell *presShell = doc->GetPrimaryShell();
|
|
|
|
nsIFrame* cframe = presShell->GetPrimaryFrameFor(c);
|
|
|
|
PRBool b = IsPartiallyVisible(presShell, cframe);
|
|
|
|
if (b)
|
|
setFocusedContent(c);
|
|
else
|
|
ScrollWindow(direction, getContentWindow());*/
|
|
|
|
setFocusedContent(c);
|
|
return NS_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// do it all again at the parent document
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
{
|
|
nsCOMPtr<nsIDOMWindow> contentWindow = getContentWindow();
|
|
if (!contentWindow)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
|
contentWindow->GetDocument(getter_AddRefs(domDoc));
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
|
|
|
nsIPresShell *shell = doc->GetPrimaryShell();
|
|
if (!shell) return NS_OK;
|
|
|
|
presContext = shell->GetPresContext();
|
|
|
|
nsIFrame* parentFrame = shell->GetRootFrame();
|
|
|
|
nsCOMPtr<nsIDocument> subdoc = focusedContent->GetDocument();
|
|
if (!subdoc) return NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMDocument> subdomdoc = do_QueryInterface(subdoc);
|
|
|
|
nsCOMPtr<nsIDOMWindowInternal> domWindowInternal;
|
|
GetWindowFromDocument(subdomdoc, getter_AddRefs(domWindowInternal));
|
|
if (!domWindowInternal) return NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMWindowInternal> domWindowInternal2 = domWindowInternal;
|
|
domWindowInternal2->GetOpener(getter_AddRefs(domWindowInternal));
|
|
if (!domWindowInternal)
|
|
domWindowInternal = domWindowInternal2;
|
|
|
|
nsCOMPtr<nsIDOMWindow> subdocWindow = do_QueryInterface(domWindowInternal);
|
|
if (!subdocWindow) return NS_OK;
|
|
|
|
subdocWindow->GetDocument(getter_AddRefs(subdomdoc));
|
|
if (!subdoc) return NS_OK;
|
|
|
|
nsIPresShell *subdocShell = subdoc->GetPrimaryShell();
|
|
if (!subdocShell) return NS_OK;
|
|
|
|
nsPresContext *subdocPresContext = subdocShell->GetPresContext();
|
|
|
|
nsIFrame* subdocFrame = subdocShell->GetRootFrame();
|
|
|
|
nsRect subdocRect = subdocFrame->GetRect();
|
|
|
|
nsPoint frame_offset = subdocFrame->GetOffsetToExternal(parentFrame);
|
|
|
|
subdocRect.x = frame_offset.x;
|
|
subdocRect.y = frame_offset.y;
|
|
|
|
getContentInDirection(direction, presContext, subdocRect, subdocFrame, PR_TRUE, PR_FALSE, getter_AddRefs(c));
|
|
}
|
|
|
|
if (c) {
|
|
nsCOMPtr<nsIContent> subdocContent;
|
|
getContentFromFrame(c, getter_AddRefs(subdocContent));
|
|
|
|
if (subdocContent) {
|
|
mNavigationFramesState = PR_TRUE;
|
|
setFocusedContent(c);
|
|
return NS_OK;
|
|
}
|
|
|
|
setFocusedContent(c);
|
|
return NS_OK;
|
|
}
|
|
|
|
// if everything fails, default is to move the focus just as if the user hit tab.
|
|
// presContext->EventStateManager()->ShiftFocus(PR_TRUE, focusedContent);
|
|
|
|
// how about this, if we find anything, we just scroll the
|
|
// page in the direction of the navigation??
|
|
ScrollWindow(direction, getContentWindow());
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
void
|
|
nsSpatialNavigation::getFocusedContent(int direction, nsIContent** aContent)
|
|
{
|
|
*aContent = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMWindow> contentWindow = getContentWindow();
|
|
if (!contentWindow)
|
|
return;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> privateWindow = do_QueryInterface(contentWindow);
|
|
nsIFocusController *focusController = privateWindow->GetRootFocusController();
|
|
|
|
if (!focusController)
|
|
return;
|
|
|
|
nsCOMPtr<nsIDOMElement> element;
|
|
focusController->GetFocusedElement(getter_AddRefs(element));
|
|
|
|
if (element)
|
|
{
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(element);
|
|
NS_IF_ADDREF(*aContent = content);
|
|
return;
|
|
}
|
|
|
|
//xxxx should/can we prevent it from going into chrome???
|
|
if (direction == eNavLeft || direction == eNavUp)
|
|
focusController->MoveFocus(PR_FALSE, nsnull);
|
|
else
|
|
focusController->MoveFocus(PR_TRUE, nsnull);
|
|
|
|
// so there is no focused content -- lets make some up, hightlight it and return.
|
|
focusController->GetFocusedElement(getter_AddRefs(element));
|
|
}
|
|
|
|
void
|
|
nsSpatialNavigation::setFocusedContent(nsIContent* c)
|
|
{
|
|
if (!c)
|
|
return;
|
|
|
|
nsCOMPtr<nsIContent> subdocContent;
|
|
getContentFromFrame(c, getter_AddRefs(subdocContent));
|
|
|
|
if (subdocContent) {
|
|
c = subdocContent;
|
|
}
|
|
|
|
nsIContent* currentContent = c;
|
|
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(currentContent);
|
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(element));
|
|
|
|
nsCOMPtr<nsIDOMWindow> contentWindow;
|
|
if (mService->mDisableJSWhenFocusing)
|
|
contentWindow = getContentWindow();
|
|
|
|
// We do not want to have JS disable at anytime - see bug 51075
|
|
// DisableJSScope foopy (contentWindow);
|
|
|
|
//#ifdef OLDER_LAYOUT
|
|
nsPresContext* presContext = getPresContext(c);
|
|
nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
|
|
presContext->EventStateManager()->SetContentState(c, NS_EVENT_STATE_FOCUS);
|
|
presShell->ScrollContentIntoView(c,
|
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
|
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
|
|
presContext->EventStateManager()->MoveCaretToFocus();
|
|
|
|
//#else
|
|
nsCOMPtr<nsIDOMNSHTMLElement> nsElement = do_QueryInterface(element);
|
|
if (nsElement)
|
|
nsElement->Focus();
|
|
//#endif
|
|
|
|
}
|
|
|
|
|
|
nsIDOMWindow*
|
|
nsSpatialNavigation::getContentWindow()
|
|
{
|
|
|
|
nsIDOMWindow* resultWindow = nsnull;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTopWindow);
|
|
nsIDOMWindowInternal *rootWindow = window->GetPrivateRoot();
|
|
|
|
nsCOMPtr<nsIDOMWindow> windowContent;
|
|
rootWindow->GetContent(getter_AddRefs(windowContent));
|
|
|
|
if (!windowContent)
|
|
return nsnull;
|
|
|
|
NS_ADDREF(resultWindow = windowContent);
|
|
return resultWindow;
|
|
}
|
|
|
|
|
|
nsPresContext*
|
|
nsSpatialNavigation::getPresContext(nsIContent* content)
|
|
{
|
|
if (!content) return nsnull;
|
|
|
|
nsCOMPtr<nsIDocument> doc = content->GetDocument();
|
|
if (!doc) return nsnull;
|
|
|
|
// the only case where there could be more shells in printpreview
|
|
nsIPresShell *shell = doc->GetPrimaryShell();
|
|
if (!shell) return nsnull;
|
|
|
|
nsPresContext *presContext = shell->GetPresContext();
|
|
return presContext;
|
|
}
|
|
|
|
|