Back to index

lightning-sunbird  0.9+nobinonly
nsSpatialNavigation.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Spatial Navigation
00015  *
00016  * The Initial Developer of the Original Code is 
00017  * Douglas F. Turner II  <dougt@meer.net>
00018  * Portions created by the Initial Developer are Copyright (C) 2004-2005
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "nsSpatialNavigationPrivate.h"
00038 
00039 PRInt32 gRectFudge = 20;
00040 PRInt32 gDirectionalBias = 1;
00041 
00042 NS_INTERFACE_MAP_BEGIN(nsSpatialNavigation)
00043   NS_INTERFACE_MAP_ENTRY(nsISpatialNavigation)
00044   NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
00045   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMKeyListener)
00046 NS_INTERFACE_MAP_END
00047 
00048 NS_IMPL_ADDREF(nsSpatialNavigation)
00049 NS_IMPL_RELEASE(nsSpatialNavigation)
00050 
00051 
00052 nsSpatialNavigation::nsSpatialNavigation(nsSpatialNavigationService* aService)
00053 {
00054   NS_ASSERTION(aService, "Should not create this object without a valid service");
00055 
00056   mService = aService; // back pointer -- no reference
00057 
00058   mNavigationFramesState = PR_FALSE;
00059 }
00060 
00061 nsSpatialNavigation::~nsSpatialNavigation()
00062 {
00063 }
00064 
00065 NS_IMETHODIMP
00066 nsSpatialNavigation::HandleEvent(nsIDOMEvent* aEvent)
00067 {
00068   return NS_OK;
00069 }
00070 
00071 NS_IMETHODIMP
00072 nsSpatialNavigation::KeyUp(nsIDOMEvent* aEvent)
00073 {
00074   return NS_OK;
00075 }
00076 
00077 NS_IMETHODIMP
00078 nsSpatialNavigation::KeyPress(nsIDOMEvent* aEvent)
00079 {
00080   return NS_OK;
00081 }
00082 
00083 NS_IMETHODIMP
00084 nsSpatialNavigation::KeyDown(nsIDOMEvent* aEvent)
00085 {
00086   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
00087   PRBool enabled;
00088   prefBranch->GetBoolPref("snav.enabled", &enabled);
00089   if (!enabled) //  this doesn't work.  wtf? if (!mService->mEnabled)
00090     return NS_OK;
00091  
00092 
00093   nsCOMPtr<nsIDOMNSUIEvent> uiEvent(do_QueryInterface(aEvent));
00094   if (uiEvent)
00095   {
00096     // If a web page wants to use the keys mapped to our
00097     // move, they have to use evt.preventDefault() after
00098     // they get the key
00099 
00100     PRBool preventDefault;
00101     uiEvent->GetPreventDefault(&preventDefault);
00102     if (preventDefault)
00103       return NS_OK;
00104   }
00105 
00106   PRInt32 formControlType = -1;
00107   // check to see if we are in a text field.
00108   // based on nsTypeAheadFind.
00109     
00110   //nsEvent should be renamed.
00111   nsCOMPtr<nsIDOMNSEvent> nsEvent = do_QueryInterface(aEvent);
00112   if (!nsEvent)
00113     return NS_ERROR_FAILURE;
00114   
00115   nsCOMPtr<nsIDOMEventTarget> domEventTarget;
00116   nsEvent->GetOriginalTarget(getter_AddRefs(domEventTarget));
00117   
00118   nsCOMPtr<nsIContent> targetContent = do_QueryInterface(domEventTarget);
00119 
00120   if (targetContent->IsContentOfType(nsIContent::eXUL))
00121     return NS_OK;
00122   
00123   if (targetContent->IsContentOfType(nsIContent::eHTML_FORM_CONTROL)) 
00124   {
00125       nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(targetContent));
00126       formControlType = formControl->GetType();
00127       
00128       if (mService->mIgnoreTextFields)
00129       {
00130         if (formControlType == NS_FORM_TEXTAREA ||
00131             formControlType == NS_FORM_INPUT_TEXT ||
00132             formControlType == NS_FORM_INPUT_PASSWORD ||
00133             formControlType == NS_FORM_INPUT_FILE) 
00134         {
00135           return NS_OK;
00136         }
00137       }
00138   }
00139   else if (mService->mIgnoreTextFields && targetContent->IsContentOfType(nsIContent::eHTML)) 
00140   {
00141     // Test for isindex, a deprecated kind of text field. We're using a string 
00142     // compare because <isindex> is not considered a form control, so it does 
00143     // not support nsIFormControl or eHTML_FORM_CONTROL, and it's not worth 
00144     // having a table of atoms just for it. 
00145     
00146       if (isContentOfType(targetContent, "isindex"))
00147         return NS_OK;
00148   }
00149 
00150   PRUint32 keyCode;
00151   PRBool isModifier;
00152   nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
00153   
00154   if (!keyEvent)
00155     return NS_ERROR_FAILURE;
00156 
00157   if (NS_FAILED(keyEvent->GetKeyCode(&keyCode)))
00158        return NS_ERROR_FAILURE;
00159   
00160   // figure out what modifier to use  
00161   
00162   /************************************************
00163     Value of the keyCodeModifier is
00164   
00165     SHIFT          = 0x00100000
00166     CONTROL        = 0x00001100
00167     ALT            = 0x00000012
00168   *************************************************/
00169   
00170   
00171   if (mService->mKeyCodeModifier & 0x00100000)
00172   {
00173     if (NS_FAILED(keyEvent->GetShiftKey(&isModifier)))
00174       return NS_ERROR_FAILURE;
00175     if (!isModifier)
00176       return NS_OK;  
00177   }
00178   
00179   if (mService->mKeyCodeModifier & 0x00001100)
00180   {
00181     if (NS_FAILED(keyEvent->GetCtrlKey(&isModifier)))
00182       return NS_ERROR_FAILURE;
00183     if (!isModifier)
00184       return NS_OK;  
00185   }
00186   
00187   if (mService->mKeyCodeModifier & 0x00000012)
00188   {
00189     if (NS_FAILED(keyEvent->GetAltKey(&isModifier)))
00190       return NS_ERROR_FAILURE;
00191     if (!isModifier)
00192       return NS_OK;  
00193   }
00194 
00195   if (keyCode == mService->mKeyCodeLeft)
00196   {
00197     //************************************************************************************
00198     // NS_FORM_TEXTAREA & (NS_FORM_INPUT_TEXT | NS_FORM_INPUT_PASSWORD | NS_FORM_INPUT_FILE) cases
00199 
00200     PRInt32 selectionStart = 0, textLength = 0;
00201     if (formControlType == NS_FORM_INPUT_TEXT || 
00202         formControlType == NS_FORM_INPUT_PASSWORD ||
00203         formControlType == NS_FORM_INPUT_FILE)
00204     {
00205       nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(targetContent);
00206       if (input) {
00207         input->GetSelectionStart (&selectionStart);
00208         input->GetTextLength (&textLength);
00209       }
00210     } else if (formControlType == NS_FORM_TEXTAREA) {
00211 
00212       nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea = do_QueryInterface(targetContent);
00213       if (textArea) {
00214         textArea->GetSelectionStart (&selectionStart);
00215         textArea->GetTextLength (&textLength);
00216       }
00217     }
00218 
00219     if (textLength != 0 && selectionStart != 0)
00220       return NS_OK;
00221 
00222     // We're using this key, no one else should
00223     aEvent->StopPropagation();
00224     aEvent->PreventDefault();
00225     return Left();
00226   }
00227 
00228   if (keyCode == mService->mKeyCodeRight)
00229   {
00230     //************************************************************************************
00231     // NS_FORM_TEXTAREA & (NS_FORM_INPUT_TEXT | NS_FORM_INPUT_PASSWORD | NS_FORM_INPUT_FILE) cases
00232 
00233     PRInt32 selectionEnd = 0, textLength = 0;
00234 
00235     if (formControlType == NS_FORM_INPUT_TEXT || 
00236         formControlType == NS_FORM_INPUT_PASSWORD ||
00237         formControlType == NS_FORM_INPUT_FILE)
00238     {
00239       nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(targetContent);
00240       if (input) {
00241         input->GetSelectionEnd (&selectionEnd);
00242         input->GetTextLength (&textLength);
00243       }
00244     } else if (formControlType == NS_FORM_TEXTAREA) {
00245 
00246       nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea = do_QueryInterface(targetContent);
00247       if (textArea) {
00248         textArea->GetSelectionEnd (&selectionEnd);
00249         textArea->GetTextLength (&textLength);
00250       }
00251     }
00252 
00253     if (textLength  != selectionEnd)
00254       return NS_OK;
00255 
00256     aEvent->StopPropagation();
00257     aEvent->PreventDefault();
00258     return Right();
00259   }
00260 
00261   if (keyCode == mService->mKeyCodeUp)
00262   {
00263 
00264     // If we are going up or down, in a select, lets not
00265     // navigate.
00266     //
00267     // FIX: What we really want to do is determine if we are
00268     // at the start or the end fo the form element, and
00269     // based on the selected position we decide to nav. or
00270     // not.
00271 
00272     // ************************************************************************************
00273     // NS_FORM_SELECT cases:
00274     // * if it is a select form of 'size' attr != than '1' then we do as above.
00275 
00276     // * if it is a select form of 'size' attr == than '1', snav can take care of it.
00277     // if (formControlType == NS_FORM_SELECT)
00278     //   return NS_OK;
00279 
00280     //************************************************************************************
00281     // NS_FORM_TEXTAREA & (NS_FORM_INPUT_TEXT | NS_FORM_INPUT_PASSWORD | NS_FORM_INPUT_FILE) cases
00282 
00283     if (formControlType == NS_FORM_TEXTAREA) {
00284 
00285       PRInt32 selectionStart = 0, textLength = 0;
00286       nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea = do_QueryInterface(targetContent);
00287       if (textArea) {
00288         textArea->GetSelectionStart (&selectionStart);
00289         textArea->GetTextLength (&textLength);
00290       }
00291 
00292       if (textLength != 0 && selectionStart != 0)
00293         return NS_OK;
00294     }
00295 
00296     // We're using this key, no one else should
00297     aEvent->StopPropagation();
00298     aEvent->PreventDefault();
00299     return Up();
00300   }
00301 
00302   if (keyCode == mService->mKeyCodeDown)
00303   {
00304     // If we are going up or down, in a select, lets not
00305     // navigate.
00306     //
00307     // FIX: What we really want to do is determine if we are
00308     // at the start or the end fo the form element, and
00309     // based on the selected position we decide to nav. or
00310     // not.
00311 
00312     // ************************************************************************************
00313     // NS_FORM_SELECT cases:
00314     // * if it is a select form of 'size' attr != than '1' then we do as above.
00315 
00316     // * if it is a select form of 'size' attr == than '1', snav can take care of it.
00317     // if (formControlType == NS_FORM_SELECT)
00318     //   return NS_OK;
00319 
00320     if (formControlType == NS_FORM_TEXTAREA) {
00321 
00322       PRInt32 selectionEnd = 0, textLength = 0;
00323       nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea = do_QueryInterface(targetContent);
00324       if (textArea) {
00325         textArea->GetSelectionEnd (&selectionEnd);
00326         textArea->GetTextLength (&textLength);
00327       }
00328 
00329       if (textLength  != selectionEnd)
00330         return NS_OK;
00331     }
00332 
00333     aEvent->StopPropagation();  // We're using this key, no one else should
00334     aEvent->PreventDefault();
00335     return Down();
00336   }
00337   
00338   return NS_OK;
00339 }
00340 
00341 
00342 NS_IMETHODIMP 
00343 nsSpatialNavigation::Init(nsIDOMWindow *aWindow)
00344 {
00345   mTopWindow = aWindow;
00346 
00347   nsCOMPtr<nsIDOM3EventTarget> target;
00348   nsCOMPtr<nsIDOMEventGroup> systemGroup;
00349   nsresult rv = getEventTargetFromWindow(aWindow, getter_AddRefs(target), getter_AddRefs(systemGroup));
00350   if (NS_FAILED(rv))
00351     return rv;  
00352   
00353   target->AddGroupedEventListener(NS_LITERAL_STRING("keypress"),
00354                                   NS_STATIC_CAST(nsIDOMKeyListener*, this),
00355                                   PR_FALSE, 
00356                                   systemGroup);
00357   
00358   return NS_OK;
00359 }
00360 
00361 NS_IMETHODIMP 
00362 nsSpatialNavigation::Shutdown()
00363 {
00364   nsCOMPtr<nsIDOM3EventTarget> target;
00365   nsCOMPtr<nsIDOMEventGroup> systemGroup;
00366   nsresult rv = getEventTargetFromWindow(mTopWindow, getter_AddRefs(target), getter_AddRefs(systemGroup));
00367   if (NS_FAILED(rv))
00368     return rv;
00369   
00370   target->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"),
00371                                      NS_STATIC_CAST(nsIDOMKeyListener*, this),
00372                                      PR_FALSE, 
00373                                      systemGroup);
00374   mTopWindow = nsnull;
00375 
00376   return NS_OK;
00377 }
00378 
00379 NS_IMETHODIMP 
00380 nsSpatialNavigation::Up()
00381 {
00382   return handleMove(eNavUp);
00383 }
00384 
00385 NS_IMETHODIMP 
00386 nsSpatialNavigation::Down()
00387 {
00388   return handleMove(eNavDown);
00389 }
00390 
00391 NS_IMETHODIMP 
00392 nsSpatialNavigation::Left()
00393 {
00394   return handleMove(eNavLeft);
00395 }
00396 
00397 NS_IMETHODIMP 
00398 nsSpatialNavigation::Right()
00399 {
00400   return handleMove(eNavRight);
00401 }
00402 
00403 NS_IMETHODIMP 
00404 nsSpatialNavigation::GetAttachedWindow(nsIDOMWindow * *aAttachedWindow)
00405 {
00406   NS_IF_ADDREF(*aAttachedWindow = mTopWindow);
00407   return NS_OK;
00408 }
00409 
00410 void DoTraversal(int aDirection,
00411                  nsIBidirectionalEnumerator* aFrameTraversal, 
00412                  nsIFrame* aFocusedFrame,
00413                  nsRect& aFocusedRect,
00414                  PRBool isAREA,
00415                  PRBool focusDocuments,
00416                  nsPresContext* aPresContext, 
00417                  PRInt64* aDSoFar,                 
00418                  nsIContent** aCurrentContent)
00419 {
00420 
00421   *aCurrentContent = nsnull;
00422 
00423   // There are some rects with zero width or height.
00424   // calculating distance from a non point is troublesome.
00425   // we will fix this up here by setting such a point to at
00426   // least 1 px.
00427 
00428   if (aFocusedRect.width == 0) {
00429     aFocusedRect.width = 1;
00430   }
00431 
00432   if (aFocusedRect.height == 0) {
00433     aFocusedRect.height = 1;
00434   }
00435 
00436   nsIFrame* frame;
00437   nsRect frameRect;
00438   
00439   PRInt64 d;
00440   
00441   while (1) 
00442   {
00443     aFrameTraversal->Next();
00444     
00445     nsISupports* currentItem;
00446     aFrameTraversal->CurrentItem(&currentItem);
00447     frame = NS_STATIC_CAST(nsIFrame*, currentItem);
00448 
00449     if (!frame)
00450       break;
00451 #ifdef DEBUG_outputframes
00452     printf("got frame %x\n", frame);
00453 #endif
00454     // So, here we want to make sure that the frame that we
00455     // nav to isn't part of the flow of the currently
00456     // focused frame
00457 
00458        if (!isAREA) 
00459     {
00460       nsIFrame* flowFrame = aFocusedFrame;
00461       PRBool currentFrameIsFocusedFrame = PR_FALSE;
00462       while (flowFrame) 
00463       {
00464         if (flowFrame == frame) 
00465         {
00466           currentFrameIsFocusedFrame = PR_TRUE;
00467           break;
00468         }
00469         flowFrame = flowFrame->GetNextInFlow();
00470       }
00471       if (currentFrameIsFocusedFrame)
00472         continue;
00473        }
00474 
00476     // a special case for area's which are not enumerated by the nsIFrameTraversal
00478     if (isMap(frame)) 
00479     {
00480       nsIContent* c = frame->GetContent();
00481       nsCOMPtr<nsIDOMHTMLMapElement> element = do_QueryInterface(c);
00482       if (!element)
00483         continue;
00484 
00485       nsCOMPtr<nsIDOMHTMLCollection> mapAreas;
00486       element->GetAreas(getter_AddRefs(mapAreas));
00487       if (!mapAreas)
00488         continue;
00489       
00490       PRUint32 length;
00491       mapAreas->GetLength(&length);
00492       
00493       for (PRUint32 i = 0; i < length; i++) 
00494       {
00495         nsCOMPtr<nsIDOMNode> domNode;
00496         mapAreas->Item(i,getter_AddRefs(domNode));
00497         
00498         nsCOMPtr<nsIDOMHTMLAreaElement> e = do_QueryInterface(domNode);        
00499               nsCOMPtr<nsIContent> content= do_QueryInterface(domNode);
00500               getFrameForContent(content, &frame);
00501         
00502         getRectOfAreaElement(frame, e, &frameRect);
00503         
00504         if (!isRectInDirection(aDirection, aFocusedRect, frameRect))
00505           continue;
00506         
00507         d = spatialDistance(aDirection, aFocusedRect, frameRect);
00508         
00509         if ((*aDSoFar) > d) 
00510         {
00511           (*aDSoFar) = d;
00512           NS_IF_RELEASE(*aCurrentContent);
00513           NS_ADDREF(*aCurrentContent = content);
00514         }      
00515       }
00516       continue;
00517     }
00519     
00520     // we don't want to worry about the same frame if we
00521     // aren't an area
00522     if (frame == aFocusedFrame ||
00523         (aFocusedFrame && (frame->GetContent() == aFocusedFrame->GetContent())))
00524       continue;
00525 
00526        // make sure the frame is targetable for focus
00527        if (!isTargetable(focusDocuments, frame))
00528       continue;
00529        
00530     // RECT !!
00531     frameRect = makeRectRelativeToGlobalView(frame);
00532 
00533     // no frames without size and be navigated to
00534     // successfully.
00535     if (frameRect.width == 0 || frameRect.height == 0)
00536       continue;
00537     
00538     // deflate the rect to avoid overlapping with other
00539     // rects.
00540     frameRect.Deflate(gRectFudge, gRectFudge);
00541 
00542     if (!isRectInDirection(aDirection, aFocusedRect, frameRect))
00543       continue;
00544     
00545     d = spatialDistance(aDirection, aFocusedRect, frameRect);
00546     
00547     if ((*aDSoFar) >= d) 
00548     {
00549 #ifdef DEBUG_dougt
00550       if (d == 0)
00551       {
00552         printf("there is overlapping content;\n");
00553       }
00554 #endif
00555       (*aDSoFar) = d;
00556       NS_IF_RELEASE(*aCurrentContent);
00557       NS_ADDREF(*aCurrentContent = frame->GetContent());
00558     }
00559   }
00560 }
00561 
00562 inline void centerRect(int aDirection, nsRect& aRect)
00563 {
00564   if (aDirection == eNavLeft)
00565     aRect.x = 1000000;
00566   else if (aDirection == eNavRight)
00567     aRect.x = 0;
00568   else if (aDirection == eNavUp)
00569     aRect.y = 1000000;
00570   else
00571     aRect.y = 0;
00572 
00573   aRect.height = 1;
00574   aRect.width = 1;
00575 }
00576 
00577 nsresult
00578 nsSpatialNavigation::getContentInDirection(int aDirection, 
00579                                            nsPresContext* aPresContext, 
00580                                            nsRect& aFocusedRect, 
00581                                            nsIFrame* aFocusedFrame, 
00582                                            PRBool aIsAREA,
00583                                            PRBool aFocusDocuments,
00584                                            nsIContent** aContent)
00585 {  
00586 
00587   // Check to see if we should decend into subdoc
00588   nsIContent* subFrameContent = aFocusedFrame->GetContent();
00589   nsCOMPtr<nsIDOMHTMLHtmlElement> hhElement = do_QueryInterface(subFrameContent);
00590   nsCOMPtr<nsIDOMHTMLIFrameElement> iFrameElement = do_QueryInterface(subFrameContent);
00591 
00592   if ( (hhElement || iFrameElement) && mNavigationFramesState)
00593   {
00594     aPresContext = getPresContext(subFrameContent);
00595     centerRect(aDirection, aFocusedRect);
00596   }
00597 
00598   
00599   nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
00600   nsresult result = createFrameTraversal(FOCUS, aPresContext, getter_AddRefs(frameTraversal));  
00601   if (NS_FAILED(result))
00602     return result;
00603   
00604   nsCOMPtr<nsIContent> currentContent;
00605   PRInt64 currentDistance = LL_MaxInt();
00606 
00607   DoTraversal(aDirection,
00608               frameTraversal, 
00609               aFocusedFrame,
00610               aFocusedRect,
00611               aIsAREA,
00612               aFocusDocuments,
00613               aPresContext, 
00614               &currentDistance,
00615               aContent);
00616 
00617 
00618   if ( (hhElement || iFrameElement) && mNavigationFramesState)
00619   {
00620     mNavigationFramesState = PR_FALSE;
00621   }
00622 
00623   return NS_OK;
00624 }
00625 
00626 
00627 nsresult
00628 nsSpatialNavigation::handleMove(int direction)
00629 {
00630   PRUint32 type = FOCUS;
00631 
00632   nsCOMPtr<nsIContent> focusedContent;
00633   getFocusedContent(direction, getter_AddRefs(focusedContent));
00634 
00635   // there are some websites which have no focusable elements,
00636   // only text, for example. In these cases, scrolling have to be
00637   // performed by snav.
00638   if (!focusedContent) {
00639      ScrollWindow(direction, getContentWindow());
00640      return NS_OK;
00641   }
00642   nsPresContext* presContext = getPresContext(focusedContent);
00643   if(!presContext)
00644     return NS_ERROR_NULL_POINTER;
00645 
00646   nsIFrame* focusedFrame;
00647   getFrameForContent(focusedContent, &focusedFrame);
00648 
00649   nsRect focusedRect;
00650   PRBool isAREA = isArea(focusedContent);
00651   if (!isAREA) 
00652   {
00653     // RECT !!
00654     focusedRect = makeRectRelativeToGlobalView(focusedFrame);
00655 
00656     // deflate the rect to avoid overlapping with other
00657     // rects.
00658     focusedRect.Deflate(gRectFudge, gRectFudge);
00659   }
00660   else
00661   {
00662     nsCOMPtr<nsIDOMHTMLAreaElement> e = do_QueryInterface(focusedContent);
00663     getRectOfAreaElement(focusedFrame, e, &focusedRect);
00664   }
00665 
00666   nsCOMPtr<nsIContent> c;
00667   getContentInDirection(direction, presContext, focusedRect, focusedFrame, PR_FALSE, isAREA, getter_AddRefs(c));
00668   
00669   if (c) {
00670    
00671     nsIDocument* doc = c->GetDocument();
00672     if (!doc)
00673       return NS_ERROR_FAILURE;
00674     
00675 /*    nsIPresShell *presShell = doc->GetShellAt(0);
00676 
00677     nsIFrame* cframe;
00678     presShell->GetPrimaryFrameFor(c, &cframe);
00679     
00680     PRBool b = IsPartiallyVisible(presShell, cframe); 
00681     
00682     if (b)
00683       setFocusedContent(c);
00684     else
00685       ScrollWindow(direction, getContentWindow());*/
00686 
00687     setFocusedContent(c);
00688     return NS_OK;
00689   }
00690 
00692   // do it all again at the parent document
00694 
00695   {
00696     nsCOMPtr<nsIDOMWindow> contentWindow = getContentWindow();
00697     if (!contentWindow)
00698       return NS_OK;
00699 
00700     nsCOMPtr<nsIDOMDocument> domDoc;
00701     contentWindow->GetDocument(getter_AddRefs(domDoc));
00702     nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
00703 
00704     nsIPresShell *shell = doc->GetShellAt(0);
00705     if (!shell) return NS_OK;
00706   
00707     presContext = shell->GetPresContext();
00708 
00709        nsIFrame* parentFrame = shell->GetRootFrame();
00710 
00711     nsCOMPtr<nsIDocument> subdoc = focusedContent->GetDocument();
00712     if (!subdoc) return NS_OK;
00713     
00714     nsCOMPtr<nsIDOMDocument> subdomdoc = do_QueryInterface(subdoc);
00715 
00716     nsCOMPtr<nsIDOMWindowInternal> domWindowInternal;
00717     GetWindowFromDocument(subdomdoc, getter_AddRefs(domWindowInternal));
00718     if (!domWindowInternal) return NS_OK;
00719 
00720     nsCOMPtr<nsIDOMWindowInternal> domWindowInternal2 = domWindowInternal;
00721        domWindowInternal2->GetOpener(getter_AddRefs(domWindowInternal));
00722     if (!domWindowInternal) 
00723       domWindowInternal = domWindowInternal2;
00724 
00725     nsCOMPtr<nsIDOMWindow> subdocWindow = do_QueryInterface(domWindowInternal);
00726     if (!subdocWindow) return NS_OK;
00727 
00728     subdocWindow->GetDocument(getter_AddRefs(subdomdoc));
00729     if (!subdoc) return NS_OK;
00730 
00731     nsIPresShell *subdocShell = subdoc->GetShellAt(0);
00732     if (!subdocShell) return NS_OK;
00733   
00734     nsPresContext *subdocPresContext = subdocShell->GetPresContext();
00735 
00736     nsIFrame* subdocFrame = subdocShell->GetRootFrame();
00737 
00738     nsRect subdocRect = subdocFrame->GetRect();
00739 
00740     nsPoint frame_offset = subdocFrame->GetOffsetToExternal(parentFrame);
00741        
00742        subdocRect.x = frame_offset.x;
00743        subdocRect.y = frame_offset.y;
00744 
00745     getContentInDirection(direction, presContext, subdocRect, subdocFrame, PR_TRUE, PR_FALSE, getter_AddRefs(c));
00746   }
00747 
00748   if (c) {
00749     nsCOMPtr<nsIContent> subdocContent;
00750     getContentFromFrame(c, getter_AddRefs(subdocContent));
00751 
00752     if (subdocContent) {
00753       mNavigationFramesState = PR_TRUE;
00754       setFocusedContent(c);
00755       return NS_OK;
00756     }
00757 
00758     setFocusedContent(c);
00759     return NS_OK;
00760   }
00761   
00762   // if everything fails, default is to move the focus just as if the user hit tab.
00763   //  presContext->EventStateManager()->ShiftFocus(PR_TRUE, focusedContent);
00764 
00765   // how about this, if we find anything, we just scroll the
00766   // page in the direction of the navigation??
00767   ScrollWindow(direction, getContentWindow());
00768 
00770 
00771   return NS_OK;
00772 
00773 }
00774 
00775 void
00776 nsSpatialNavigation::getFocusedContent(int direction, nsIContent** aContent)
00777 {
00778   *aContent = nsnull;
00779   
00780   nsCOMPtr<nsIDOMWindow> contentWindow = getContentWindow();
00781   if (!contentWindow)
00782     return;
00783   
00784   nsCOMPtr<nsPIDOMWindow> privateWindow = do_QueryInterface(contentWindow);
00785   nsIFocusController *focusController = privateWindow->GetRootFocusController();
00786   
00787   if (!focusController)
00788     return;
00789   
00790   nsCOMPtr<nsIDOMElement> element;
00791   focusController->GetFocusedElement(getter_AddRefs(element));
00792   
00793   if (element)
00794   {
00795     nsCOMPtr<nsIContent> content = do_QueryInterface(element);
00796     NS_IF_ADDREF(*aContent = content);
00797     return;
00798   }
00799 
00800   //xxxx should/can we prevent it from going into chrome???
00801   if (direction == eNavLeft || direction == eNavUp)
00802     focusController->MoveFocus(PR_FALSE, nsnull);
00803   else
00804     focusController->MoveFocus(PR_TRUE, nsnull);
00805   
00806   // so there is no focused content -- lets make some up, hightlight it and return.  
00807   focusController->GetFocusedElement(getter_AddRefs(element));
00808 }
00809 
00810 void 
00811 nsSpatialNavigation::setFocusedContent(nsIContent* c)
00812 {
00813   if (!c)
00814     return;
00815 
00816   nsCOMPtr<nsIContent> subdocContent;
00817   getContentFromFrame(c, getter_AddRefs(subdocContent));
00818 
00819   if (subdocContent) {
00820     c = subdocContent;
00821   }
00822 
00823   nsIContent* currentContent = c;
00824   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(currentContent);
00825   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(element));
00826 
00827   nsCOMPtr<nsIDOMWindow> contentWindow;
00828   if (mService->mDisableJSWhenFocusing)
00829     contentWindow = getContentWindow();
00830 
00831   // We do not want to have JS disable at anytime - see bug 51075
00832   // DisableJSScope foopy (contentWindow);
00833 
00834   //#ifdef OLDER_LAYOUT  
00835   nsPresContext* presContext = getPresContext(c);
00836   
00837   nsIPresShell *presShell = presContext->PresShell();
00838   nsIFrame* frame;
00839   presShell->GetPrimaryFrameFor(c, &frame);
00840   
00841   if (frame) {
00842     presContext->EventStateManager()->SetContentState(c, NS_EVENT_STATE_FOCUS);
00843     
00844     presShell->ScrollFrameIntoView(frame, 
00845                                    NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
00846                                    NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
00847     
00848     presContext->EventStateManager()->MoveCaretToFocus();
00849   }
00850 
00851   //#else
00852   nsCOMPtr<nsIDOMNSHTMLElement> nsElement = do_QueryInterface(element);
00853   if (nsElement) 
00854     nsElement->Focus();
00855   //#endif
00856 
00857 }
00858 
00859 
00860 nsIDOMWindow*
00861 nsSpatialNavigation::getContentWindow()
00862 {
00863 
00864   nsIDOMWindow* resultWindow = nsnull;
00865 
00866   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTopWindow);
00867   nsIDOMWindowInternal *rootWindow = window->GetPrivateRoot();
00868   
00869   nsCOMPtr<nsIDOMWindow> windowContent;
00870   rootWindow->GetContent(getter_AddRefs(windowContent));
00871   
00872   if (!windowContent)
00873        return nsnull;
00874 
00875   NS_ADDREF(resultWindow = windowContent);
00876   return resultWindow;
00877 }
00878 
00879 
00880 nsPresContext* 
00881 nsSpatialNavigation::getPresContext(nsIContent* content)
00882 {
00883   if (!content) return nsnull;
00884   
00885   nsCOMPtr<nsIDocument> doc = content->GetDocument();
00886   if (!doc) return nsnull;
00887   
00888   // the only case where there could be more shells in printpreview
00889   nsIPresShell *shell = doc->GetShellAt(0);
00890   if (!shell) return nsnull;
00891   
00892   nsPresContext *presContext = shell->GetPresContext();
00893   return presContext;
00894 }
00895 
00896