Back to index

lightning-sunbird  0.9+nobinonly
nsViewportFrame.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 #include "nsCOMPtr.h"
00038 #include "nsViewportFrame.h"
00039 #include "nsHTMLParts.h"
00040 #include "nsLayoutAtoms.h"
00041 #include "nsIViewManager.h"
00042 #include "nsIScrollableFrame.h"
00043 #include "nsIDeviceContext.h"
00044 #include "nsPresContext.h"
00045 #include "nsReflowPath.h"
00046 #include "nsIPresShell.h"
00047 
00048 nsresult
00049 NS_NewViewportFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00050 {
00051   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00052   if (nsnull == aNewFrame) {
00053     return NS_ERROR_NULL_POINTER;
00054   }
00055   ViewportFrame* it = new (aPresShell) ViewportFrame;
00056   if (nsnull == it) {
00057     return NS_ERROR_OUT_OF_MEMORY;
00058   }
00059   *aNewFrame = it;
00060   return NS_OK;
00061 }
00062 
00063 NS_IMETHODIMP
00064 ViewportFrame::Destroy(nsPresContext* aPresContext)
00065 {
00066   mFixedContainer.DestroyFrames(this, aPresContext);
00067   return nsContainerFrame::Destroy(aPresContext);
00068 }
00069 
00070 NS_IMETHODIMP
00071 ViewportFrame::SetInitialChildList(nsPresContext* aPresContext,
00072                                    nsIAtom*        aListName,
00073                                    nsIFrame*       aChildList)
00074 {
00075   nsresult rv = NS_OK;
00076 
00077   // See which child list to add the frames to
00078 #ifdef NS_DEBUG
00079   nsFrame::VerifyDirtyBitSet(aChildList);
00080 #endif
00081   if (mFixedContainer.GetChildListName() == aListName) {
00082     rv = mFixedContainer.SetInitialChildList(this, aPresContext, aListName, aChildList);
00083   } 
00084   else {
00085     rv = nsContainerFrame::SetInitialChildList(aPresContext, aListName, aChildList);
00086   }
00087 
00088   return rv;
00089 }
00090 
00091 NS_IMETHODIMP
00092 ViewportFrame::GetFrameForPoint(const nsPoint& aPoint, 
00093                                    nsFramePaintLayer aWhichLayer,
00094                                    nsIFrame**     aFrame)
00095 {
00096   // this should act like a block, so we need to override
00097   return GetFrameForPointUsing(aPoint, nsnull, aWhichLayer, 
00098                                (aWhichLayer == NS_FRAME_PAINT_LAYER_BACKGROUND), aFrame);
00099 }
00100 
00101 NS_IMETHODIMP
00102 ViewportFrame::AppendFrames(nsIAtom*        aListName,
00103                             nsIFrame*       aFrameList)
00104 {
00105   nsresult rv = NS_OK;
00106 
00107   if (mFixedContainer.GetChildListName() == aListName) {
00108     rv = mFixedContainer.AppendFrames(this, aListName, aFrameList);
00109   }
00110   else {
00111     // We only expect incremental changes for our fixed frames
00112     NS_ERROR("unexpected child list");
00113     rv = NS_ERROR_INVALID_ARG;
00114   }
00115 
00116   return rv;
00117 }
00118 
00119 NS_IMETHODIMP
00120 ViewportFrame::InsertFrames(nsIAtom*        aListName,
00121                             nsIFrame*       aPrevFrame,
00122                             nsIFrame*       aFrameList)
00123 {
00124   nsresult rv = NS_OK;
00125 
00126   if (mFixedContainer.GetChildListName() == aListName) {
00127     rv = mFixedContainer.InsertFrames(this, aListName, aPrevFrame, aFrameList);
00128   }
00129   else {
00130     // We only expect incremental changes for our fixed frames
00131     NS_ERROR("unexpected child list");
00132     rv = NS_ERROR_INVALID_ARG;
00133   }
00134 
00135   return rv;
00136 }
00137 
00138 NS_IMETHODIMP
00139 ViewportFrame::RemoveFrame(nsIAtom*        aListName,
00140                            nsIFrame*       aOldFrame)
00141 {
00142   nsresult rv = NS_OK;
00143 
00144   if (mFixedContainer.GetChildListName() == aListName) {
00145     rv = mFixedContainer.RemoveFrame(this, aListName, aOldFrame);
00146   }
00147   else {
00148     // We only expect incremental changes for our fixed frames
00149     NS_ERROR("unexpected child list");
00150     rv = NS_ERROR_INVALID_ARG;
00151   }
00152 
00153   return rv;
00154 }
00155 
00156 nsIAtom*
00157 ViewportFrame::GetAdditionalChildListName(PRInt32 aIndex) const
00158 {
00159   NS_PRECONDITION(aIndex >= 0, "illegal index");
00160 
00161   if (0 == aIndex) {
00162     return mFixedContainer.GetChildListName();
00163   }
00164 
00165   return nsnull;
00166 }
00167 
00168 nsIFrame*
00169 ViewportFrame::GetFirstChild(nsIAtom* aListName) const
00170 {
00171   if (mFixedContainer.GetChildListName() == aListName) {
00172     nsIFrame* result = nsnull;
00173     mFixedContainer.FirstChild(this, aListName, &result);
00174     return result;
00175   }
00176 
00177   return nsContainerFrame::GetFirstChild(aListName);
00178 }
00179 
00180 nsPoint
00181  ViewportFrame::AdjustReflowStateForScrollbars(nsHTMLReflowState* aReflowState) const
00182 {
00183   // Calculate how much room is available for fixed frames. That means
00184   // determining if the viewport is scrollable and whether the vertical and/or
00185   // horizontal scrollbars are visible
00186 
00187   // Get our prinicpal child frame and see if we're scrollable
00188   nsIFrame* kidFrame = mFrames.FirstChild();
00189   nsCOMPtr<nsIScrollableFrame> scrollingFrame(do_QueryInterface(kidFrame));
00190 
00191   if (scrollingFrame) {
00192     nsMargin scrollbars = scrollingFrame->GetActualScrollbarSizes();
00193     aReflowState->mComputedWidth -= scrollbars.left + scrollbars.right;
00194     aReflowState->availableWidth -= scrollbars.left + scrollbars.right;
00195     aReflowState->mComputedHeight -= scrollbars.top + scrollbars.bottom;
00196     // XXX why don't we also adjust "aReflowState->availableHeight"?
00197     return nsPoint(scrollbars.left, scrollbars.top);
00198   }
00199   return nsPoint(0, 0);
00200 }
00201 
00202 NS_IMETHODIMP
00203 ViewportFrame::Reflow(nsPresContext*          aPresContext,
00204                       nsHTMLReflowMetrics&     aDesiredSize,
00205                       const nsHTMLReflowState& aReflowState,
00206                       nsReflowStatus&          aStatus)
00207 {
00208   DO_GLOBAL_REFLOW_COUNT("ViewportFrame", aReflowState.reason);
00209   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
00210   NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow");
00211   NS_PRECONDITION(!aDesiredSize.mComputeMEW, "unexpected request");
00212 
00213   // Initialize OUT parameters
00214   aStatus = NS_FRAME_COMPLETE;
00215 
00216   // Reflow the main content first so that the placeholders of the
00217   // fixed-position frames will be in the right places on an initial
00218   // reflow.
00219   nsRect kidRect(0,0,aReflowState.availableWidth,aReflowState.availableHeight);
00220 
00221   nsresult rv = NS_OK;
00222   
00223   if (mFrames.NotEmpty()) {
00224     // Deal with a non-incremental reflow or an incremental reflow
00225     // targeted at our one-and-only principal child frame.
00226     if (eReflowReason_Incremental != aReflowState.reason ||
00227         aReflowState.path->HasChild(mFrames.FirstChild())) {
00228       // Reflow our one-and-only principal child frame
00229       nsIFrame*           kidFrame = mFrames.FirstChild();
00230       nsHTMLReflowMetrics kidDesiredSize(nsnull);
00231       nsSize              availableSpace(aReflowState.availableWidth,
00232                                          aReflowState.availableHeight);
00233       nsHTMLReflowState   kidReflowState(aPresContext, aReflowState,
00234                                          kidFrame, availableSpace);
00235 
00236       // Reflow the frame
00237       kidReflowState.mComputedHeight = aReflowState.availableHeight;
00238       rv = ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState,
00239                        0, 0, 0, aStatus);
00240       kidRect.width = kidDesiredSize.width;
00241       kidRect.height = kidDesiredSize.height;
00242 
00243       FinishReflowChild(kidFrame, aPresContext, nsnull, kidDesiredSize, 0, 0, 0);
00244     }
00245   }
00246 
00247   // If we were flowed initially at both an unconstrained width and height, 
00248   // this is a hint that we should return our child's intrinsic size.
00249   if ((eReflowReason_Initial == aReflowState.reason ||
00250        eReflowReason_Resize == aReflowState.reason) &&
00251       aReflowState.availableWidth == NS_UNCONSTRAINEDSIZE &&
00252       aReflowState.availableHeight == NS_UNCONSTRAINEDSIZE) {
00253     aDesiredSize.width = kidRect.width;
00254     aDesiredSize.height = kidRect.height;
00255     aDesiredSize.ascent = kidRect.height;
00256     aDesiredSize.descent = 0;
00257   }
00258   else {
00259     // Return the max size as our desired size
00260     aDesiredSize.width = aReflowState.availableWidth;
00261     aDesiredSize.height = aReflowState.availableHeight;
00262     aDesiredSize.ascent = aReflowState.availableHeight;
00263     aDesiredSize.descent = 0;
00264   }
00265 
00266   // Make a copy of the reflow state and change the computed width and height
00267   // to reflect the available space for the fixed items
00268   nsHTMLReflowState reflowState(aReflowState);
00269   nsPoint offset = AdjustReflowStateForScrollbars(&reflowState);
00270   
00271 #ifdef DEBUG
00272   nsIFrame* f;
00273   mFixedContainer.FirstChild(this, nsLayoutAtoms::fixedList, &f);
00274   NS_ASSERTION(!f || (offset.x == 0 && offset.y == 0),
00275                "We don't handle correct positioning of fixed frames with "
00276                "scrollbars in odd positions");
00277 #endif
00278 
00279   nsReflowType reflowType = eReflowType_ContentChanged;
00280   if (aReflowState.path) {
00281     // XXXwaterson this is more restrictive than the previous code
00282     // was: it insists that the UserDefined reflow be targeted at
00283     // _this_ frame.
00284     nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand;
00285     if (command)
00286       command->GetType(reflowType);
00287   }
00288 
00289   if (reflowType != eReflowType_UserDefined &&
00290       aReflowState.reason == eReflowReason_Incremental) {
00291     // Incremental reflow
00292      mFixedContainer.IncrementalReflow(this, aPresContext, reflowState,
00293                                        reflowState.mComputedWidth,
00294                                        reflowState.mComputedHeight);
00295   }
00296 
00297   // Just reflow all the fixed-pos frames.
00298   rv = mFixedContainer.Reflow(this, aPresContext, reflowState,
00299                               reflowState.mComputedWidth, 
00300                               reflowState.mComputedHeight);
00301 
00302   // If this is an initial reflow, resize reflow, or style change reflow
00303   // then do a repaint
00304   if ((eReflowReason_Initial == aReflowState.reason) ||
00305       (eReflowReason_Resize == aReflowState.reason) ||
00306       (eReflowReason_StyleChange == aReflowState.reason)) {
00307     nsRect damageRect(0, 0, aDesiredSize.width, aDesiredSize.height);
00308     Invalidate(damageRect, PR_FALSE);
00309   }
00310 
00311   NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus);
00312   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
00313   return rv; 
00314 }
00315 
00316 nsIAtom*
00317 ViewportFrame::GetType() const
00318 {
00319   return nsLayoutAtoms::viewportFrame;
00320 }
00321 
00322 /* virtual */ PRBool
00323 ViewportFrame::IsContainingBlock() const
00324 {
00325   return PR_TRUE;
00326 }
00327 
00328 #ifdef DEBUG
00329 NS_IMETHODIMP
00330 ViewportFrame::GetFrameName(nsAString& aResult) const
00331 {
00332   return MakeFrameName(NS_LITERAL_STRING("Viewport"), aResult);
00333 }
00334 #endif