Back to index

lightning-sunbird  0.9+nobinonly
nsLayoutUtils.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsLayoutUtils.h"
00039 #include "nsIFrame.h"
00040 #include "nsPresContext.h"
00041 #include "nsIContent.h"
00042 #include "nsFrameList.h"
00043 #include "nsLayoutAtoms.h"
00044 #include "nsIAtom.h"
00045 #include "nsCSSPseudoElements.h"
00046 #include "nsIView.h"
00047 #include "nsIScrollableView.h"
00048 #include "nsPlaceholderFrame.h"
00049 #include "nsIScrollableFrame.h"
00050 #include "nsCSSFrameConstructor.h"
00051 #include "nsIPrivateDOMEvent.h"
00052 #include "nsIDOMEvent.h"
00053 #include "nsGUIEvent.h"
00054 
00066 static nsIFrame*
00067 GetFirstChildFrame(nsIFrame*       aFrame,
00068                    nsIContent*     aContent)
00069 {
00070   NS_PRECONDITION(aFrame, "NULL frame pointer");
00071 
00072   // Get the first child frame
00073   nsIFrame* childFrame = aFrame->GetFirstChild(nsnull);
00074 
00075   // If the child frame is a pseudo-frame, then return its first child.
00076   // Note that the frame we create for the generated content is also a
00077   // pseudo-frame and so don't drill down in that case
00078   if (childFrame &&
00079       childFrame->IsPseudoFrame(aContent) &&
00080       !childFrame->IsGeneratedContentFrame()) {
00081     return GetFirstChildFrame(childFrame, aContent);
00082   }
00083 
00084   return childFrame;
00085 }
00086 
00094 static nsIFrame*
00095 GetLastChildFrame(nsIFrame*       aFrame,
00096                   nsIContent*     aContent)
00097 {
00098   NS_PRECONDITION(aFrame, "NULL frame pointer");
00099 
00100   // Get the last in flow frame
00101   nsIFrame* lastInFlow = aFrame->GetLastInFlow();
00102 
00103   // Get the last child frame
00104   nsIFrame* firstChildFrame = lastInFlow->GetFirstChild(nsnull);
00105   if (firstChildFrame) {
00106     nsFrameList frameList(firstChildFrame);
00107     nsIFrame*   lastChildFrame = frameList.LastChild();
00108 
00109     NS_ASSERTION(lastChildFrame, "unexpected error");
00110 
00111     // Get the frame's first-in-flow. This matters in case the frame has
00112     // been continuted across multiple lines
00113     lastChildFrame = lastChildFrame->GetFirstInFlow();
00114     
00115     // If the last child frame is a pseudo-frame, then return its last child.
00116     // Note that the frame we create for the generated content is also a
00117     // pseudo-frame and so don't drill down in that case
00118     if (lastChildFrame &&
00119         lastChildFrame->IsPseudoFrame(aContent) &&
00120         !lastChildFrame->IsGeneratedContentFrame()) {
00121       return GetLastChildFrame(lastChildFrame, aContent);
00122     }
00123 
00124     return lastChildFrame;
00125   }
00126 
00127   return nsnull;
00128 }
00129 
00130 // static
00131 nsIFrame*
00132 nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame)
00133 {
00134   NS_PRECONDITION(aFrame, "NULL frame pointer");
00135   NS_ASSERTION(!aFrame->GetPrevInFlow(), "aFrame must be first-in-flow");
00136   
00137   nsIFrame* firstFrame = GetFirstChildFrame(aFrame, aFrame->GetContent());
00138 
00139   if (firstFrame && IsGeneratedContentFor(nsnull, firstFrame,
00140                                           nsCSSPseudoElements::before)) {
00141     return firstFrame;
00142   }
00143 
00144   return nsnull;
00145 }
00146 
00147 // static
00148 nsIFrame*
00149 nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame)
00150 {
00151   NS_PRECONDITION(aFrame, "NULL frame pointer");
00152 
00153   nsIFrame* lastFrame = GetLastChildFrame(aFrame, aFrame->GetContent());
00154 
00155   if (lastFrame && IsGeneratedContentFor(nsnull, lastFrame,
00156                                          nsCSSPseudoElements::after)) {
00157     return lastFrame;
00158   }
00159 
00160   return nsnull;
00161 }
00162 
00163 // static
00164 nsIFrame*
00165 nsLayoutUtils::GetPageFrame(nsIFrame* aFrame)
00166 {
00167   for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
00168     if (frame->GetType() == nsLayoutAtoms::pageFrame) {
00169       return frame;
00170     }
00171   }
00172   return nsnull;
00173 }
00174 
00175 nsIFrame*
00176 nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
00177   if (nsLayoutAtoms::placeholderFrame != aFrame->GetType()) {
00178     return nsnull;
00179   }
00180 
00181   nsIFrame *outOfFlowFrame =
00182     nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
00183   if (outOfFlowFrame->GetStyleDisplay()->IsFloating()) {
00184     return outOfFlowFrame;
00185   }
00186 
00187   return nsnull;
00188 }
00189 
00190 // static
00191 PRBool
00192 nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent,
00193                                      nsIFrame* aFrame,
00194                                      nsIAtom* aPseudoElement)
00195 {
00196   NS_PRECONDITION(aFrame, "Must have a frame");
00197   NS_PRECONDITION(aPseudoElement, "Must have a pseudo name");
00198 
00199   if (!aFrame->IsGeneratedContentFrame()) {
00200     return PR_FALSE;
00201   }
00202   
00203   if (aContent && aFrame->GetContent() != aContent) {
00204     return PR_FALSE;
00205   }
00206 
00207   return aFrame->GetStyleContext()->GetPseudoType() == aPseudoElement;
00208 }
00209 
00210 // static
00211 PRBool
00212 nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
00213                                      nsIFrame* aCommonAncestor)
00214 {
00215   if (aFrame == aCommonAncestor) {
00216     return PR_FALSE;
00217   }
00218   
00219   nsIFrame* parentFrame = aFrame->GetParent();
00220 
00221   while (parentFrame != aCommonAncestor) {
00222     if (parentFrame == aAncestorFrame) {
00223       return PR_TRUE;
00224     }
00225 
00226     parentFrame = parentFrame->GetParent();
00227   }
00228 
00229   return PR_FALSE;
00230 }
00231 
00232 // static
00233 PRInt32
00234 nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1,
00235                                      nsIContent* aContent2,
00236                                      PRInt32 aIf1Ancestor,
00237                                      PRInt32 aIf2Ancestor,
00238                                      nsIContent* aCommonAncestor)
00239 {
00240   NS_PRECONDITION(aContent1, "aContent1 must not be null");
00241   NS_PRECONDITION(aContent2, "aContent2 must not be null");
00242 
00243   nsAutoVoidArray content1Ancestors;
00244   nsIContent* c1;
00245   for (c1 = aContent1; c1 && c1 != aCommonAncestor; c1 = c1->GetParent()) {
00246     content1Ancestors.AppendElement(c1);
00247   }
00248   if (!c1 && aCommonAncestor) {
00249     // So, it turns out aCommonAncestor was not an ancestor of c1. Oops.
00250     // Never mind. We can continue as if aCommonAncestor was null.
00251     aCommonAncestor = nsnull;
00252   }
00253 
00254   nsAutoVoidArray content2Ancestors;
00255   nsIContent* c2;
00256   for (c2 = aContent2; c2 && c2 != aCommonAncestor; c2 = c2->GetParent()) {
00257     content2Ancestors.AppendElement(c2);
00258   }
00259   if (!c2 && aCommonAncestor) {
00260     // So, it turns out aCommonAncestor was not an ancestor of c2.
00261     // We need to retry with no common ancestor hint.
00262     return DoCompareTreePosition(aContent1, aContent2,
00263                                  aIf1Ancestor, aIf2Ancestor, nsnull);
00264   }
00265   
00266   int last1 = content1Ancestors.Count() - 1;
00267   int last2 = content2Ancestors.Count() - 1;
00268   nsIContent* content1Ancestor = nsnull;
00269   nsIContent* content2Ancestor = nsnull;
00270   while (last1 >= 0 && last2 >= 0
00271          && ((content1Ancestor = NS_STATIC_CAST(nsIContent*, content1Ancestors.ElementAt(last1)))
00272              == (content2Ancestor = NS_STATIC_CAST(nsIContent*, content2Ancestors.ElementAt(last2))))) {
00273     last1--;
00274     last2--;
00275   }
00276 
00277   if (last1 < 0) {
00278     if (last2 < 0) {
00279       NS_ASSERTION(aContent1 == aContent2, "internal error?");
00280       return 0;
00281     } else {
00282       // aContent1 is an ancestor of aContent2
00283       return aIf1Ancestor;
00284     }
00285   } else {
00286     if (last2 < 0) {
00287       // aContent2 is an ancestor of aContent1
00288       return aIf2Ancestor;
00289     } else {
00290       // content1Ancestor != content2Ancestor, so they must be siblings with the same parent
00291       nsIContent* parent = content1Ancestor->GetParent();
00292       NS_ASSERTION(parent, "no common ancestor at all???");
00293       if (!parent) { // different documents??
00294         return 0;
00295       }
00296 
00297       PRInt32 index1 = parent->IndexOf(content1Ancestor);
00298       PRInt32 index2 = parent->IndexOf(content2Ancestor);
00299       if (index1 < 0 || index2 < 0) {
00300         // one of them must be anonymous; we can't determine the order
00301         return 0;
00302       }
00303 
00304       return index1 - index2;
00305     }
00306   }
00307 }
00308 
00309 // static
00310 nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) {
00311   if (!aFrame) {
00312     return nsnull;
00313   }
00314 
00315   nsIFrame* next;
00316   while ((next = aFrame->GetNextSibling()) != nsnull) {
00317     aFrame = next;
00318   }
00319   return aFrame;
00320 }
00321 
00322 // static
00323 nsIView*
00324 nsLayoutUtils::FindSiblingViewFor(nsIView* aParentView, nsIFrame* aFrame) {
00325   nsIFrame* parentViewFrame = NS_STATIC_CAST(nsIFrame*, aParentView->GetClientData());
00326   nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nsnull;
00327   for (nsIView* insertBefore = aParentView->GetFirstChild(); insertBefore;
00328        insertBefore = insertBefore->GetNextSibling()) {
00329     nsIFrame* f = NS_STATIC_CAST(nsIFrame*, insertBefore->GetClientData());
00330     if (!f) {
00331       // this view could be some anonymous view attached to a meaningful parent
00332       for (nsIView* searchView = insertBefore->GetParent(); searchView;
00333            searchView = searchView->GetParent()) {
00334         f = NS_STATIC_CAST(nsIFrame*, searchView->GetClientData());
00335         if (f) {
00336           break;
00337         }
00338       }
00339       NS_ASSERTION(f, "Can't find a frame anywhere!");
00340     }
00341     if (!f || !aFrame->GetContent() || !f->GetContent() ||
00342         CompareTreePosition(aFrame->GetContent(), f->GetContent(), parentViewContent) > 0) {
00343       // aFrame's content is after f's content (or we just don't know),
00344       // so put our view before f's view
00345       return insertBefore;
00346     }
00347   }
00348   return nsnull;
00349 }
00350 
00351 //static
00352 nsIScrollableFrame*
00353 nsLayoutUtils::GetScrollableFrameFor(nsIFrame *aScrolledFrame)
00354 {
00355   nsIFrame *frame = aScrolledFrame->GetParent();
00356   if (!frame) {
00357     return nsnull;
00358   }
00359   nsIScrollableFrame *sf;
00360   CallQueryInterface(frame, &sf);
00361   return sf;
00362 }
00363 
00364 //static
00365 nsIScrollableFrame*
00366 nsLayoutUtils::GetScrollableFrameFor(nsIScrollableView *aScrollableView)
00367 {
00368   nsIFrame *frame = GetFrameFor(aScrollableView->View()->GetParent());
00369   if (frame) {
00370     nsIScrollableFrame *sf;
00371     CallQueryInterface(frame, &sf);
00372     return sf;
00373   }
00374   return nsnull;
00375 }
00376 
00377 //static
00378 nsPresContext::ScrollbarStyles
00379 nsLayoutUtils::ScrollbarStylesOfView(nsIScrollableView *aScrollableView)
00380 {
00381   nsIScrollableFrame *sf = GetScrollableFrameFor(aScrollableView);
00382   return sf ? sf->GetScrollbarStyles() :
00383               nsPresContext::ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN,
00384                                              NS_STYLE_OVERFLOW_HIDDEN);
00385 }
00386 
00387 // static
00388 nsIScrollableView*
00389 nsLayoutUtils::GetNearestScrollingView(nsIView* aView, Direction aDirection)
00390 {
00391   // If aDirection is eEither, find first view with a scrolllable frame.
00392   // Otherwise, find the first view that has a scrollable frame whose
00393   // ScrollbarStyles is not NS_STYLE_OVERFLOW_HIDDEN in aDirection
00394   // and where there is something currently not visible
00395   // that can be scrolled to in aDirection.
00396   NS_ASSERTION(aView, "GetNearestScrollingView expects a non-null view");
00397   nsIScrollableView* scrollableView = nsnull;
00398   for (; aView; aView = aView->GetParent()) {
00399     scrollableView = aView->ToScrollableView();
00400     if (scrollableView) {
00401       nsPresContext::ScrollbarStyles ss =
00402         nsLayoutUtils::ScrollbarStylesOfView(scrollableView);
00403       nsIScrollableFrame *scrollableFrame = GetScrollableFrameFor(scrollableView);
00404       NS_ASSERTION(scrollableFrame, "Must have scrollable frame for view!");
00405       nsMargin margin = scrollableFrame->GetActualScrollbarSizes();
00406       // Get size of total scrollable area
00407       nscoord totalWidth, totalHeight;
00408       scrollableView->GetContainerSize(&totalWidth, &totalHeight);
00409       // Get size of currently visible area
00410       nsSize visibleSize = aView->GetBounds().Size();
00411       // aDirection can be eHorizontal, eVertical, or eEither
00412       // If scrolling in a specific direction, require visible scrollbars or
00413       // something to scroll to in that direction.
00414       if (aDirection != eHorizontal &&
00415           ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN &&
00416           (aDirection == eEither || totalHeight > visibleSize.height || margin.right))
00417         break;
00418       if (aDirection != eVertical &&
00419           ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN &&
00420           (aDirection == eEither || totalWidth > visibleSize.width || margin.bottom))
00421         break;
00422     }
00423   }
00424   return scrollableView;
00425 }
00426 
00427 nsPoint
00428 nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(nsIDOMEvent* aDOMEvent, nsIFrame* aFrame)
00429 {
00430   nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aDOMEvent));
00431   NS_ASSERTION(privateEvent, "bad implementation");
00432   if (!privateEvent)
00433     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
00434   nsEvent* event;
00435   nsresult rv = privateEvent->GetInternalNSEvent(&event);
00436   if (NS_FAILED(rv))
00437     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
00438   if (!event || event->eventStructType != NS_MOUSE_EVENT)
00439     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
00440   
00441   nsGUIEvent* GUIEvent = NS_STATIC_CAST(nsGUIEvent*, event);
00442   if (!GUIEvent->widget)
00443     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
00444   nsIView* view = nsIView::GetViewFor(GUIEvent->widget);
00445   if (!view)
00446     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
00447 
00448   nsPoint widgetToView;
00449   view->GetNearestWidget(&widgetToView);
00450   nsPoint viewToFrame;
00451   nsIView* frameView = aFrame->GetClosestView(&viewToFrame);
00452 
00453   float p2t = aFrame->GetPresContext()->PixelsToTwips();
00454   nsPoint mousePt(NSIntPixelsToTwips(GUIEvent->refPoint.x, p2t),
00455                   NSIntPixelsToTwips(GUIEvent->refPoint.y, p2t));
00456   return mousePt + widgetToView + (-frameView->GetOffsetTo(view)) +  viewToFrame;
00457 }
00458 
00459 // Combine aNewBreakType with aOrigBreakType, but limit the break types
00460 // to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT.
00461 PRUint8
00462 nsLayoutUtils::CombineBreakType(PRUint8 aOrigBreakType,
00463                                 PRUint8 aNewBreakType)
00464 {
00465   PRUint8 breakType = aOrigBreakType;
00466   switch(breakType) {
00467   case NS_STYLE_CLEAR_LEFT:
00468     if ((NS_STYLE_CLEAR_RIGHT          == aNewBreakType) ||
00469         (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
00470       breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
00471     }
00472     break;
00473   case NS_STYLE_CLEAR_RIGHT:
00474     if ((NS_STYLE_CLEAR_LEFT           == aNewBreakType) ||
00475         (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
00476       breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
00477     }
00478     break;
00479   case NS_STYLE_CLEAR_NONE:
00480     if ((NS_STYLE_CLEAR_LEFT           == aNewBreakType) ||
00481         (NS_STYLE_CLEAR_RIGHT          == aNewBreakType) ||
00482         (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
00483       breakType = aNewBreakType;
00484     }
00485   }
00486   return breakType;
00487 }
00488 
00489 PRBool
00490 nsLayoutUtils::IsInitialContainingBlock(nsIFrame* aFrame)
00491 {
00492   return aFrame ==
00493     aFrame->GetPresContext()->PresShell()->FrameConstructor()->GetInitialContainingBlock();
00494 }
00495 
00496 nsPoint
00497 nsLayoutUtils::GetEventCoordinatesForNearestView(nsEvent* aEvent,
00498                                                  nsIFrame* aFrame,
00499                                                  nsIView** aView)
00500 {
00501   if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT && 
00502                   aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT))
00503     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
00504 
00505   nsGUIEvent* GUIEvent = NS_STATIC_CAST(nsGUIEvent*, aEvent);
00506   if (!GUIEvent->widget)
00507     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
00508 
00509   nsPoint viewToFrame;
00510   nsIView* frameView;
00511   aFrame->GetOffsetFromView(viewToFrame, &frameView);
00512   if (aView)
00513     *aView = frameView;
00514 
00515   return TranslateWidgetToView(aFrame->GetPresContext(), GUIEvent->widget,
00516                                GUIEvent->refPoint, frameView);
00517 }
00518 
00519 nsPoint
00520 nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext, 
00521                                      nsIWidget* aWidget, nsIntPoint aPt,
00522                                      nsIView* aView)
00523 {
00524   nsIView* baseView = nsIView::GetViewFor(aWidget);
00525   if (!baseView)
00526     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
00527   nsPoint viewToWidget;
00528   nsIWidget* wid = baseView->GetNearestWidget(&viewToWidget);
00529   NS_ASSERTION(aWidget == wid, "Clashing widgets");
00530   float pixelsToTwips = aPresContext->PixelsToTwips();
00531   nsPoint refPointTwips(NSIntPixelsToTwips(aPt.x, pixelsToTwips),
00532                         NSIntPixelsToTwips(aPt.y, pixelsToTwips));
00533   return refPointTwips - viewToWidget - aView->GetOffsetTo(baseView);
00534 }