1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Netscape Public License
6
* Version 1.1 (the "License"); you may not use this file except in
7
* compliance with the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/NPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is mozilla.org code.
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998
20
* the Initial Developer. All Rights Reserved.
24
* Alternatively, the contents of this file may be used under the terms of
25
* either the GNU General Public License Version 2 or later (the "GPL"), or
26
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
* in which case the provisions of the GPL or the LGPL are applicable instead
28
* of those above. If you wish to allow use of your version of this file only
29
* under the terms of either the GPL or the LGPL, and not to allow others to
30
* use your version of this file under the terms of the NPL, indicate your
31
* decision by deleting the provisions above and replace them with the notice
32
* and other provisions required by the GPL or the LGPL. If you do not delete
33
* the provisions above, a recipient may use your version of this file under
34
* the terms of any one of the NPL, the GPL or the LGPL.
36
* ***** END LICENSE BLOCK ***** */
38
#include "nsHTMLParts.h"
39
#include "nsIPresContext.h"
40
#include "nsHTMLReflowCommand.h"
41
#include "nsIDeviceContext.h"
42
#include "nsPageFrame.h"
43
#include "nsViewsCID.h"
44
#include "nsIServiceManager.h"
46
#include "nsIViewManager.h"
47
#include "nsHTMLContainerFrame.h"
48
#include "nsIScrollableView.h"
49
#include "nsWidgetsCID.h"
50
#include "nsGfxScrollFrame.h"
51
#include "nsScrollBoxFrame.h"
52
#include "nsLayoutAtoms.h"
53
#include "nsXULAtoms.h"
54
#include "nsHTMLAtoms.h"
55
#include "nsINameSpaceManager.h"
56
#include "nsISupportsArray.h"
57
#include "nsIDocument.h"
58
#include "nsIFontMetrics.h"
59
#include "nsIDocumentObserver.h"
60
#include "nsIDocument.h"
61
#include "nsIScrollPositionListener.h"
62
//#include "nsBoxFrame.h"
63
#include "nsIElementFactory.h"
64
#include "nsBoxLayoutState.h"
65
#include "nsINodeInfo.h"
66
#include "nsIScrollbarFrame.h"
67
#include "nsIScrollbarMediator.h"
68
#include "nsITextControlFrame.h"
69
#include "nsIDOMHTMLTextAreaElement.h"
71
#include "nsIPrintPreviewContext.h"
73
#include "nsGUIEvent.h"
74
//----------------------------------------------------------------------
76
class nsGfxScrollFrameInner : public nsIScrollPositionListener {
82
nsGfxScrollFrameInner(nsGfxScrollFrame* aOuter);
83
virtual ~nsGfxScrollFrameInner();
85
// nsIScrollPositionListener
87
NS_IMETHOD ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY);
88
NS_IMETHOD ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY);
90
// This gets called when the 'curpos' attribute on one of the scrollbars changes
91
nsresult CurPosAttributeChanged(nsIPresContext* aPresContext,
95
PRBool SetAttribute(nsIBox* aBox, nsIAtom* aAtom, nscoord aSize, PRBool aReflow=PR_TRUE);
96
PRInt32 GetIntegerAttribute(nsIBox* aFrame, nsIAtom* atom, PRInt32 defaultValue);
98
nsresult Layout(nsBoxLayoutState& aState);
99
nsresult LayoutBox(nsBoxLayoutState& aState, nsIBox* aBox, const nsRect& aRect);
101
// Like ScrollPositionDidChange, but initiated by this frame rather than from the
103
void InternalScrollPositionDidChange(nscoord aX, nscoord aY);
105
PRBool AddRemoveScrollbar (PRBool& aHasScrollbar,
109
PRBool aOnRightOrBottom,
112
PRBool AddRemoveScrollbar(nsBoxLayoutState& aState,
113
nsRect& aScrollAreaSize,
118
PRBool AddHorizontalScrollbar (nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnBottom);
119
PRBool AddVerticalScrollbar (nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight);
120
void RemoveHorizontalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnBottom);
121
void RemoveVerticalScrollbar (nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight);
123
nsIScrollableView* GetScrollableView(nsIPresContext* aPresContext);
125
void ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY, PRUint32 aFlags);
127
void SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisible);
129
NS_IMETHOD GetScrolledSize(nsIPresContext* aPresContext,
131
nscoord *aHeight) const;
132
void AdjustReflowStateForPrintPreview(nsBoxLayoutState& aState, PRBool& aSetBack);
133
void AdjustReflowStateBack(nsBoxLayoutState& aState, PRBool aSetBack);
135
nsIBox* mHScrollbarBox;
136
nsIBox* mVScrollbarBox;
137
nsIBox* mScrollAreaBox;
138
nsIBox* mScrollCornerBox;
140
nsGfxScrollFrame* mOuter;
141
nscoord mMaxElementWidth;
143
// The last dir value we saw in AddHorizontalScrollbar. Use PRInt16
144
// so we can fit all the possible values of a PRUint8 and have a -1
145
// value that indicates "not set")
148
PRPackedBool mNeverHasVerticalScrollbar;
149
PRPackedBool mNeverHasHorizontalScrollbar;
151
PRPackedBool mHasVerticalScrollbar;
152
PRPackedBool mHasHorizontalScrollbar;
153
PRPackedBool mFirstPass;
154
PRPackedBool mIsRoot;
155
PRPackedBool mNeverReflowed;
156
PRPackedBool mViewInitiatedScroll;
157
PRPackedBool mFrameInitiatedScroll;
160
NS_IMPL_ISUPPORTS1(nsGfxScrollFrameInner, nsIScrollPositionListener)
163
NS_NewGfxScrollFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, nsIDocument* aDocument, PRBool aIsRoot)
165
NS_PRECONDITION(aNewFrame, "null OUT ptr");
166
if (nsnull == aNewFrame) {
167
return NS_ERROR_NULL_POINTER;
169
nsGfxScrollFrame* it = new (aPresShell) nsGfxScrollFrame(aPresShell, aDocument, aIsRoot);
171
return NS_ERROR_OUT_OF_MEMORY;
177
nsGfxScrollFrame::nsGfxScrollFrame(nsIPresShell* aShell, nsIDocument* aDocument, PRBool aIsRoot):nsBoxFrame(aShell, aIsRoot)
179
mInner = new nsGfxScrollFrameInner(this);
181
mPresContext = nsnull;
182
mInner->mIsRoot = PR_FALSE;
183
mInner->mNeverReflowed = PR_TRUE;
184
mInner->mViewInitiatedScroll = PR_FALSE;
185
mInner->mFrameInitiatedScroll = PR_FALSE;
186
SetLayoutManager(nsnull);
189
nsGfxScrollFrame::~nsGfxScrollFrame()
191
mInner->mOuter = nsnull;
193
mPresContext = nsnull;
197
* Set the view that we are scrolling within the scrolling view.
200
nsGfxScrollFrame::SetScrolledFrame(nsIPresContext* aPresContext, nsIFrame *aScrolledFrame)
202
NS_ERROR("Not implemented!");
204
mFrames.DestroyFrame(aPresContext, mInner->mScrollAreaBox);
205
mInner->mScrollAreaBox = aScrolledFrame;
206
mFrames.InsertFrame(nsnull, nsnull, mInner->mScrollAreaBox);
212
* Get the view that we are scrolling within the scrolling view.
216
nsGfxScrollFrame::GetScrolledFrame(nsIPresContext* aPresContext, nsIFrame *&aScrolledFrame) const
218
nsIBox* child = nsnull;
219
mInner->mScrollAreaBox->GetChildBox(&child);
220
child->GetFrame(&aScrolledFrame);
225
* Get information about whether the vertical and horizontal scrollbars
226
* are currently visible
229
nsGfxScrollFrame::GetScrollbarVisibility(nsIPresContext* aPresContext,
230
PRBool *aVerticalVisible,
231
PRBool *aHorizontalVisible) const
233
*aVerticalVisible = mInner->mHasVerticalScrollbar;
234
*aHorizontalVisible = mInner->mHasHorizontalScrollbar;
239
nsGfxScrollFrame::GetScrollableView(nsIPresContext* aContext, nsIScrollableView** aResult)
241
*aResult = mInner->GetScrollableView(aContext);
246
nsGfxScrollFrame::GetScrollPosition(nsIPresContext* aContext, nscoord &aX, nscoord& aY) const
248
nsIScrollableView* s = mInner->GetScrollableView(aContext);
249
return s->GetScrollPosition(aX, aY);
253
nsGfxScrollFrame::ScrollTo(nsIPresContext* aContext, nscoord aX, nscoord aY, PRUint32 aFlags)
255
nsIScrollableView* s = mInner->GetScrollableView(aContext);
256
return s->ScrollTo(aX, aY, aFlags);
260
* Query whether scroll bars should be displayed all the time, never or
261
* only when necessary.
262
* @return current scrollbar selection
263
* XXX roc only 'Auto' is really tested for. This API should be simplified or
267
nsGfxScrollFrame::GetScrollPreference(nsIPresContext* aPresContext, nsScrollPref* aScrollPreference) const
269
ScrollbarStyles styles = GetScrollbarStyles();
271
if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL &&
272
styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
273
*aScrollPreference = AlwaysScroll;
274
} else if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
275
*aScrollPreference = AlwaysScrollHorizontal;
276
} else if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
277
*aScrollPreference = AlwaysScrollVertical;
278
} else if (styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO ||
279
styles.mVertical == NS_STYLE_OVERFLOW_AUTO) {
280
*aScrollPreference = Auto;
282
*aScrollPreference = NeverScroll;
288
void nsGfxScrollFrame::ScrollToRestoredPosition() {
289
NS_STATIC_CAST(nsScrollBoxFrame*, mInner->mScrollAreaBox)
290
->ScrollToRestoredPosition();
293
nsMargin nsGfxScrollFrame::GetActualScrollbarSizes() const {
294
nsRect bounds = GetRect();
296
mInner->mScrollAreaBox->GetBounds(scrollArea);
298
return nsMargin(scrollArea.x, scrollArea.y,
299
bounds.width - scrollArea.XMost(),
300
bounds.height - scrollArea.YMost());
303
nsMargin nsGfxScrollFrame::GetDesiredScrollbarSizes(nsBoxLayoutState* aState) {
304
nsMargin result(0, 0, 0, 0);
306
if (mInner->mHScrollbarBox) {
308
mInner->mHScrollbarBox->GetPrefSize(*aState, size);
310
if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
311
result.left = size.width;
314
result.right = size.width;
317
if (mInner->mVScrollbarBox) {
319
mInner->mVScrollbarBox->GetPrefSize(*aState, size);
320
// We don't currently support any scripts that would require a scrollbar
321
// at the top. (Are there any?)
322
result.bottom = size.height;
329
nsGfxScrollFrame::SetScrollbarVisibility(nsIPresContext* aPresContext,
330
PRBool aVerticalVisible,
331
PRBool aHorizontalVisible)
333
mInner->mNeverHasVerticalScrollbar = !aVerticalVisible;
334
mInner->mNeverHasHorizontalScrollbar = !aHorizontalVisible;
339
nsGfxScrollFrame::GetScrollbarBox(PRBool aVertical, nsIBox** aResult)
341
*aResult = aVertical ? mInner->mVScrollbarBox : mInner->mHScrollbarBox;
346
nsGfxScrollFrame::CreateAnonymousContent(nsIPresContext* aPresContext,
347
nsISupportsArray& aAnonymousChildren)
349
// Don't create scrollbars if we're printing/print previewing
350
// Get rid of this code when printing moves to its own presentation
351
if (aPresContext->IsPaginated()) {
352
// allow scrollbars if this is the child of the viewport, because
353
// we must be the scrollbars for the print preview window
354
nsIFrame* parent = GetParent();
355
if (!parent || parent->GetType() != nsLayoutAtoms::viewportFrame) {
356
SetScrollbarVisibility(aPresContext, PR_FALSE, PR_FALSE);
361
nsIPresShell *shell = aPresContext->GetPresShell();
362
nsCOMPtr<nsIDocument> document;
364
shell->GetDocument(getter_AddRefs(document));
366
// The anonymous <div> used by <inputs> never gets scrollbars.
367
nsCOMPtr<nsITextControlFrame> textFrame(do_QueryInterface(mParent));
369
// Make sure we are not a text area.
370
nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement(do_QueryInterface(mParent->GetContent()));
371
if (!textAreaElement) {
372
SetScrollbarVisibility(aPresContext, PR_FALSE, PR_FALSE);
377
// create horzontal scrollbar
379
nsCOMPtr<nsIElementFactory> elementFactory =
380
do_GetService(NS_ELEMENT_FACTORY_CONTRACTID_PREFIX "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", &rv);
382
return NS_ERROR_FAILURE;
384
nsINodeInfoManager *nodeInfoManager = nsnull;
386
nodeInfoManager = document->GetNodeInfoManager();
387
NS_ENSURE_TRUE(nodeInfoManager, NS_ERROR_FAILURE);
389
nsCOMPtr<nsINodeInfo> nodeInfo;
390
nodeInfoManager->GetNodeInfo(nsXULAtoms::scrollbar, nsnull,
391
kNameSpaceID_XUL, getter_AddRefs(nodeInfo));
393
ScrollbarStyles styles = GetScrollbarStyles();
394
PRBool canHaveHorizontal = styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO
395
|| styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL;
396
if (canHaveHorizontal) {
397
nsCOMPtr<nsIContent> content;
398
elementFactory->CreateInstanceByTag(nodeInfo, getter_AddRefs(content));
399
content->SetAttr(kNameSpaceID_None, nsXULAtoms::orient,
400
NS_LITERAL_STRING("horizontal"), PR_FALSE);
401
aAnonymousChildren.AppendElement(content);
404
PRBool canHaveVertical = styles.mVertical == NS_STYLE_OVERFLOW_AUTO
405
|| styles.mVertical == NS_STYLE_OVERFLOW_SCROLL;
406
if (canHaveVertical) {
407
nsCOMPtr<nsIContent> content;
408
elementFactory->CreateInstanceByTag(nodeInfo, getter_AddRefs(content));
409
content->SetAttr(kNameSpaceID_None, nsXULAtoms::orient,
410
NS_LITERAL_STRING("vertical"), PR_FALSE);
411
aAnonymousChildren.AppendElement(content);
414
if (canHaveHorizontal && canHaveVertical) {
415
nodeInfoManager->GetNodeInfo(nsXULAtoms::scrollcorner, nsnull,
416
kNameSpaceID_XUL, getter_AddRefs(nodeInfo));
417
nsCOMPtr<nsIContent> content;
418
elementFactory->CreateInstanceByTag(nodeInfo, getter_AddRefs(content));
419
aAnonymousChildren.AppendElement(content);
426
nsGfxScrollFrame::Destroy(nsIPresContext* aPresContext)
429
nsIScrollableView *view = mInner->GetScrollableView(aPresContext);
430
NS_ASSERTION(view, "unexpected null pointer");
432
view->RemoveScrollPositionListener(mInner);
433
return nsBoxFrame::Destroy(aPresContext);
437
nsGfxScrollFrame::Init(nsIPresContext* aPresContext,
438
nsIContent* aContent,
440
nsStyleContext* aStyleContext,
441
nsIFrame* aPrevInFlow)
443
mPresContext = aPresContext;
444
nsresult rv = nsBoxFrame::Init(aPresContext, aContent, aParent, aStyleContext,
449
void nsGfxScrollFrame::ReloadChildFrames(nsIPresContext* aPresContext)
451
mInner->mScrollAreaBox = nsnull;
452
mInner->mHScrollbarBox = nsnull;
453
mInner->mVScrollbarBox = nsnull;
454
mInner->mScrollCornerBox = nsnull;
456
nsIFrame* frame = GetFirstChild(nsnull);
458
PRBool understood = PR_FALSE;
460
nsIBox* box = nsnull;
461
frame->QueryInterface(NS_GET_IID(nsIBox), (void**)&box);
463
if (frame->GetType() == nsLayoutAtoms::scrollFrame) {
464
NS_ASSERTION(!mInner->mScrollAreaBox, "Found multiple scroll areas?");
465
mInner->mScrollAreaBox = box;
466
understood = PR_TRUE;
468
nsIContent* content = frame->GetContent();
471
if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttr(kNameSpaceID_None,
472
nsXULAtoms::orient, value)) {
473
// probably a scrollbar then
474
if (value.EqualsIgnoreCase("horizontal")) {
475
NS_ASSERTION(!mInner->mHScrollbarBox, "Found multiple horizontal scrollbars?");
476
mInner->mHScrollbarBox = box;
478
NS_ASSERTION(!mInner->mVScrollbarBox, "Found multiple vertical scrollbars?");
479
mInner->mVScrollbarBox = box;
481
understood = PR_TRUE;
483
// probably a scrollcorner
484
NS_ASSERTION(!mInner->mScrollCornerBox, "Found multiple scrollcorners");
485
mInner->mScrollCornerBox = box;
486
understood = PR_TRUE;
492
NS_ASSERTION(understood, "What is this frame doing here?");
494
frame = frame->GetNextSibling();
499
nsGfxScrollFrame::SetInitialChildList(nsIPresContext* aPresContext,
501
nsIFrame* aChildList)
503
nsresult rv = nsBoxFrame::SetInitialChildList(aPresContext, aListName,
506
ReloadChildFrames(aPresContext);
508
// listen for scroll events.
509
mInner->GetScrollableView(aPresContext)->AddScrollPositionListener(mInner);
516
nsGfxScrollFrame::AppendFrames(nsIPresContext* aPresContext,
517
nsIPresShell& aPresShell,
519
nsIFrame* aFrameList)
521
nsresult rv = nsBoxFrame::AppendFrames(aPresContext,
525
ReloadChildFrames(aPresContext);
530
nsGfxScrollFrame::InsertFrames(nsIPresContext* aPresContext,
531
nsIPresShell& aPresShell,
533
nsIFrame* aPrevFrame,
534
nsIFrame* aFrameList)
536
nsresult rv = nsBoxFrame::InsertFrames(aPresContext,
541
ReloadChildFrames(aPresContext);
546
nsGfxScrollFrame::RemoveFrame(nsIPresContext* aPresContext,
547
nsIPresShell& aPresShell,
551
nsresult rv = nsBoxFrame::RemoveFrame(aPresContext,
555
ReloadChildFrames(aPresContext);
561
nsGfxScrollFrame::ReplaceFrame(nsIPresContext* aPresContext,
562
nsIPresShell& aPresShell,
567
nsresult rv = nsBoxFrame::ReplaceFrame(aPresContext,
572
ReloadChildFrames(aPresContext);
580
nsGfxScrollFrame::GetPadding(nsMargin& aMargin)
582
aMargin.SizeTo(0,0,0,0);
587
nsGfxScrollFrame::Paint(nsIPresContext* aPresContext,
588
nsIRenderingContext& aRenderingContext,
589
const nsRect& aDirtyRect,
590
nsFramePaintLayer aWhichLayer,
596
// Paint our children
597
result = nsBoxFrame::Paint(aPresContext, aRenderingContext, aDirtyRect,aWhichLayer);
603
nsGfxScrollFrame::GetContentAndOffsetsFromPoint(nsIPresContext* aCX,
604
const nsPoint& aPoint,
605
nsIContent ** aNewContent,
606
PRInt32& aContentOffset,
607
PRInt32& aContentOffsetEnd,
608
PRBool& aBeginFrameContent)
611
return NS_ERROR_NULL_POINTER;
613
nsIFrame* frame = nsnull;
614
mInner->mScrollAreaBox->GetFrame(&frame);
615
nsPoint point(aPoint);
616
//we need to translate the coordinates to the inner
617
nsIView *view = GetClosestView();
619
return NS_ERROR_FAILURE;
621
nsIView *innerView = GetClosestView();
622
while (view != innerView && innerView)
624
point -= innerView->GetPosition();
625
innerView = innerView->GetParent();
628
return frame->GetContentAndOffsetsFromPoint(aCX, point, aNewContent, aContentOffset, aContentOffsetEnd, aBeginFrameContent);
632
nsGfxScrollFrame::GetSkipSides() const
638
nsGfxScrollFrame::GetType() const
640
return nsLayoutAtoms::scrollFrame;
644
nsGfxScrollFrame::GetAscent(nsBoxLayoutState& aState, nscoord& aAscent)
647
nsresult rv = mInner->mScrollAreaBox->GetAscent(aState, aAscent);
649
GetBorderAndPadding(m);
659
nsGfxScrollFrame::ScrollbarStyles
660
nsGfxScrollFrame::GetScrollbarStyles() const
663
if (GetParent() && GetParent()->GetType() == nsLayoutAtoms::viewportFrame &&
664
// Make sure we're actually the root scrollframe
665
GetParent()->GetFirstChild(nsnull) ==
666
NS_STATIC_CAST(const nsIFrame*, this)) {
667
overflow = GetPresContext()->GetViewportOverflowOverride();
669
overflow = GetStyleDisplay()->mOverflow;
673
case NS_STYLE_OVERFLOW_SCROLL:
674
case NS_STYLE_OVERFLOW_HIDDEN:
675
case NS_STYLE_OVERFLOW_VISIBLE:
676
case NS_STYLE_OVERFLOW_AUTO:
677
return ScrollbarStyles(overflow, overflow);
678
case NS_STYLE_OVERFLOW_SCROLLBARS_NONE:
679
// This isn't quite right. The scrollframe will still be scrollable using keys.
680
// This can happen when HTML or BODY has propagated this style to the viewport.
681
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN);
682
case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
683
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_SCROLL);
684
case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
685
return ScrollbarStyles(NS_STYLE_OVERFLOW_SCROLL, NS_STYLE_OVERFLOW_HIDDEN);
687
NS_NOTREACHED("invalid overflow value");
688
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN);
693
nsGfxScrollFrame::GetPrefSize(nsBoxLayoutState& aState, nsSize& aSize)
695
PropagateDebug(aState);
697
ScrollbarStyles styles = GetScrollbarStyles();
700
if (mInner->mVScrollbarBox &&
701
styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
702
mInner->mVScrollbarBox->GetPrefSize(aState, vSize);
703
nsBox::AddMargin(mInner->mVScrollbarBox, vSize);
707
if (mInner->mHScrollbarBox &&
708
styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
709
mInner->mHScrollbarBox->GetPrefSize(aState, hSize);
710
nsBox::AddMargin(mInner->mHScrollbarBox, hSize);
713
// If one of the width and height is constrained,
714
// do smarter preferred size checking in case the scrolled frame is a block.
716
// Details: We're going to pass our width (or height) constraint
717
// down to nsBoxToBlockAdaptor. Then when we call
718
// mScrollAreaBox->GetPrefSize below, it will reflow the scrolled
719
// block with this width (or height) constraint, and report the resulting
720
// height (or width) of the block. So if possible we'll be sized exactly to the
721
// height (or width) of the block, which is what we want because 'overflow'
722
// should not affect sizing...
724
// Push current constraint. We'll restore it when we're done.
725
nsSize oldConstrainedSize;
726
aState.GetScrolledBlockSizeConstraint(oldConstrainedSize);
728
const nsHTMLReflowState* HTMLState = aState.GetReflowState();
729
// This stores the computed width and height, if available.
730
nsSize computedSize(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
731
// This stores the maximum width and height we can be, if available.
732
// This is what we use to constrain the block reflow.
733
nsSize computedMax(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
734
if (HTMLState != nsnull) {
735
computedSize = nsSize(HTMLState->mComputedWidth, HTMLState->mComputedHeight);
736
// If we know the computed size, then that's the effective maximum
737
computedMax = computedSize;
738
// One could imagine using other constraints in computedMax, but it doesn't
739
// really work. See bug 237622.
741
// Pass a constraint to the block if we have at least one constraint
742
// and our size is not fixed
743
if (((computedSize.width == NS_INTRINSICSIZE)
744
|| (computedSize.height == NS_INTRINSICSIZE))
745
&& (computedMax.width != NS_INTRINSICSIZE
746
|| computedMax.height != NS_INTRINSICSIZE)) {
747
// adjust constraints in case we have scrollbars
748
if (computedMax.width != NS_INTRINSICSIZE) {
749
computedMax.width = PR_MAX(0, computedMax.width - vSize.width);
751
if (computedMax.height != NS_INTRINSICSIZE) {
752
computedMax.height = PR_MAX(0, computedMax.height - hSize.height);
754
// For now, constrained height seems to just confuse things because
755
// various places in the block code assume constrained height => printing.
756
// Disable height constraint.
757
aState.SetScrolledBlockSizeConstraint(nsSize(computedMax.width, NS_INTRINSICSIZE));
759
aState.SetScrolledBlockSizeConstraint(nsSize(-1,-1));
762
aState.SetScrolledBlockSizeConstraint(nsSize(-1,-1));
765
nsresult rv = mInner->mScrollAreaBox->GetPrefSize(aState, aSize);
767
// Restore old constraint.
768
aState.SetScrolledBlockSizeConstraint(oldConstrainedSize);
770
// If our height is not constrained, and we will need a horizontal
771
// scrollbar, then add space for the scrollbar to our desired height.
772
if (computedSize.height == NS_INTRINSICSIZE
773
&& computedMax.width != NS_INTRINSICSIZE
774
&& aSize.width > computedMax.width
775
&& mInner->mHScrollbarBox
776
&& styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO) {
777
// Add height of horizontal scrollbar which will be needed
778
mInner->mHScrollbarBox->GetPrefSize(aState, hSize);
779
nsBox::AddMargin(mInner->mHScrollbarBox, hSize);
782
// If our width is not constrained, and we will need a vertical
783
// scrollbar, then add space for the scrollbar to our desired width.
784
if (computedSize.width == NS_INTRINSICSIZE
785
&& computedMax.height != NS_INTRINSICSIZE
786
&& aSize.height > computedMax.height
787
&& mInner->mVScrollbarBox
788
&& styles.mVertical == NS_STYLE_OVERFLOW_AUTO) {
789
// Add width of vertical scrollbar which will be needed
790
mInner->mVScrollbarBox->GetPrefSize(aState, vSize);
791
nsBox::AddMargin(mInner->mVScrollbarBox, vSize);
794
nsBox::AddMargin(mInner->mScrollAreaBox, aSize);
796
aSize.width += vSize.width;
797
aSize.height += hSize.height;
799
AddBorderAndPadding(aSize);
801
nsIBox::AddCSSPrefSize(aState, this, aSize);
807
nsGfxScrollFrame::GetMinSize(nsBoxLayoutState& aState, nsSize& aSize)
809
PropagateDebug(aState);
811
nsresult rv = mInner->mScrollAreaBox->GetMinSize(aState, aSize);
813
ScrollbarStyles styles = GetScrollbarStyles();
815
if (mInner->mVScrollbarBox &&
816
styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
818
mInner->mVScrollbarBox->GetMinSize(aState, vSize);
819
AddMargin(mInner->mVScrollbarBox, vSize);
820
aSize.width += vSize.width;
821
if (aSize.height < vSize.height)
822
aSize.height = vSize.height;
825
if (mInner->mHScrollbarBox &&
826
styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
828
mInner->mHScrollbarBox->GetMinSize(aState, hSize);
829
AddMargin(mInner->mHScrollbarBox, hSize);
830
aSize.height += hSize.height;
831
if (aSize.width < hSize.width)
832
aSize.width = hSize.width;
835
AddBorderAndPadding(aSize);
837
nsIBox::AddCSSMinSize(aState, this, aSize);
842
nsGfxScrollFrame::GetMaxSize(nsBoxLayoutState& aState, nsSize& aSize)
844
PropagateDebug(aState);
846
aSize.width = NS_INTRINSICSIZE;
847
aSize.height = NS_INTRINSICSIZE;
849
AddBorderAndPadding(aSize);
851
nsIBox::AddCSSMaxSize(aState, this, aSize);
856
nsGfxScrollFrame::Reflow(nsIPresContext* aPresContext,
857
nsHTMLReflowMetrics& aDesiredSize,
858
const nsHTMLReflowState& aReflowState,
859
nsReflowStatus& aStatus)
861
DO_GLOBAL_REFLOW_COUNT("nsGfxScrollFrame", aReflowState.reason);
862
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
864
// if there is a max element request then set it to -1 so we can see if it gets set
865
if (aDesiredSize.mComputeMEW)
867
aDesiredSize.mMaxElementWidth = -1;
870
nsresult rv = nsBoxFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
872
// if it was set then cache it. Otherwise set it.
873
if (aDesiredSize.mComputeMEW)
875
// if not set then use the cached size. If set then set it.
876
if (aDesiredSize.mMaxElementWidth == -1)
877
aDesiredSize.mMaxElementWidth = mInner->mMaxElementWidth;
879
mInner->mMaxElementWidth = aDesiredSize.mMaxElementWidth;
882
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
887
nsGfxScrollFrame::GetScrollFrameForPort(nsIFrame* aPort) {
888
NS_ASSERTION(aPort, "no port");
889
nsIFrame* parent = aPort->GetParent();
892
nsIScrollableFrame* scFrame =
893
(nsCOMPtr<nsIScrollableFrame>(do_QueryInterface(parent)));
894
return NS_STATIC_CAST(nsGfxScrollFrame*, scFrame);
897
NS_IMETHODIMP_(nsrefcnt)
898
nsGfxScrollFrame::AddRef(void)
903
NS_IMETHODIMP_(nsrefcnt)
904
nsGfxScrollFrame::Release(void)
911
nsGfxScrollFrame::GetFrameName(nsAString& aResult) const
913
return MakeFrameName(NS_LITERAL_STRING("GfxScroll"), aResult);
917
NS_INTERFACE_MAP_BEGIN(nsGfxScrollFrame)
918
NS_INTERFACE_MAP_ENTRY(nsIAnonymousContentCreator)
920
NS_INTERFACE_MAP_ENTRY(nsIFrameDebug)
922
NS_INTERFACE_MAP_ENTRY(nsIScrollableFrame)
923
NS_INTERFACE_MAP_ENTRY(nsIScrollableViewProvider)
924
NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
928
//-------------------- Inner ----------------------
930
nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsGfxScrollFrame* aOuter)
931
: mHScrollbarBox(nsnull),
932
mVScrollbarBox(nsnull),
933
mScrollAreaBox(nsnull),
934
mScrollCornerBox(nsnull),
939
mNeverHasVerticalScrollbar(PR_FALSE),
940
mNeverHasHorizontalScrollbar(PR_FALSE),
941
mHasVerticalScrollbar(PR_FALSE),
942
mHasHorizontalScrollbar(PR_FALSE),
948
nsGfxScrollFrameInner::ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY)
955
* Called when someone (external or this frame) moves the scroll area.
958
nsGfxScrollFrameInner::InternalScrollPositionDidChange(nscoord aX, nscoord aY)
961
SetAttribute(mVScrollbarBox, nsXULAtoms::curpos, aY);
964
SetAttribute(mHScrollbarBox, nsXULAtoms::curpos, aX);
968
* Called if something externally moves the scroll area
969
* This can happen if the user pages up down or uses arrow keys
970
* So what we need to do up adjust the scrollbars to match.
973
nsGfxScrollFrameInner::ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY)
975
NS_ASSERTION(!mViewInitiatedScroll, "Cannot reenter ScrollPositionDidChange");
977
mViewInitiatedScroll = PR_TRUE;
979
InternalScrollPositionDidChange(aX, aY);
981
mViewInitiatedScroll = PR_FALSE;
987
nsGfxScrollFrame::CurPosAttributeChanged(nsIPresContext* aPresContext,
991
return mInner->CurPosAttributeChanged(aPresContext, aChild, aModType);
995
nsGfxScrollFrameInner::CurPosAttributeChanged(nsIPresContext* aPresContext,
996
nsIContent* aContent,
999
NS_ASSERTION(aContent, "aContent must not be null");
1001
// Attribute changes on the scrollbars happen in one of three ways:
1002
// 1) The scrollbar changed the attribute in response to some user event
1003
// 2) We changed the attribute in response to a ScrollPositionDidChange
1004
// callback from the scrolling view
1005
// 3) We changed the attribute to adjust the scrollbars for the start
1006
// of a smooth scroll operation
1008
// In case 2), we don't need to scroll the view because the scrolling
1009
// has already happened. In case 3) we don't need to scroll because
1010
// we're just adjusting the scrollbars back to the correct setting
1013
// We used to detect this case implicitly because we'd compare the
1014
// scrollbar attributes with the view's current scroll position and
1015
// bail out if they were equal. But that approach is fragile; it can
1016
// fail when, for example, the view scrolls horizontally and
1017
// vertically simultaneously; we'll get here when only the vertical
1018
// attribute has been set, so the attributes and the view scroll
1019
// position don't yet agree, and we'd tell the view to scroll to the
1020
// new vertical position and the old horizontal position! Even worse
1021
// things could happen when smooth scrolling got involved ... crashes
1022
// and other terrors.
1023
if (mViewInitiatedScroll || mFrameInitiatedScroll) return NS_OK;
1025
nsIFrame* hframe = nsnull;
1027
mHScrollbarBox->GetFrame(&hframe);
1029
nsIFrame* vframe = nsnull;
1031
mVScrollbarBox->GetFrame(&vframe);
1033
nsIContent* vcontent = vframe ? vframe->GetContent() : nsnull;
1034
nsIContent* hcontent = hframe ? hframe->GetContent() : nsnull;
1036
if (hcontent == aContent || vcontent == aContent)
1042
if (hcontent && NS_CONTENT_ATTR_HAS_VALUE == hcontent->GetAttr(kNameSpaceID_None, nsXULAtoms::curpos, value))
1046
// convert it to an integer
1047
x = value.ToInteger(&error);
1050
if (vcontent && NS_CONTENT_ATTR_HAS_VALUE == vcontent->GetAttr(kNameSpaceID_None, nsXULAtoms::curpos, value))
1054
// convert it to an integer
1055
y = value.ToInteger(&error);
1058
// Make sure the scrollbars indeed moved before firing the event.
1059
// I think it is OK to prevent the call to ScrollbarChanged()
1060
// if we didn't actually move. The following check is the first
1061
// thing ScrollbarChanged() does anyway, before deciding to move
1063
nscoord curPosX=0, curPosY=0;
1064
nsIScrollableView* s = GetScrollableView(mOuter->mPresContext);
1066
s->GetScrollPosition(curPosX, curPosY);
1067
if ((x*mOnePixel) == curPosX && (y*mOnePixel) == curPosY)
1070
PRBool isSmooth = aContent->HasAttr(kNameSpaceID_None, nsXULAtoms::smooth);
1073
// Make sure an attribute-setting callback occurs even if the view didn't actually move yet
1074
// We need to make sure other listeners see that the scroll position is not (yet)
1075
// what they thought it was.
1076
s->GetScrollPosition(curPosX, curPosY);
1078
NS_ASSERTION(!mFrameInitiatedScroll, "Unexpected reentry");
1079
// Make sure we don't do anything in when the view calls us back for this
1080
// scroll operation.
1081
mFrameInitiatedScroll = PR_TRUE;
1082
InternalScrollPositionDidChange(curPosX, curPosY);
1083
mFrameInitiatedScroll = PR_FALSE;
1085
ScrollbarChanged(mOuter->mPresContext, x*mOnePixel, y*mOnePixel, isSmooth ? NS_VMREFRESH_SMOOTHSCROLL : 0);
1087
// Fire the onScroll event now that we have scrolled
1088
nsIPresShell *presShell = mOuter->mPresContext->GetPresShell();
1090
nsScrollbarEvent event(NS_SCROLL_EVENT);
1091
nsEventStatus status = nsEventStatus_eIgnore;
1092
// note if hcontent is non-null then hframe must be non-null.
1093
// likewise for vcontent and vframe. Thus targetFrame will always
1094
// be non-null in here.
1095
nsIFrame* targetFrame =
1096
hcontent == aContent ? hframe : vframe;
1097
presShell->HandleEventWithTarget(&event, targetFrame,
1099
NS_EVENT_FLAG_INIT, &status);
1108
nsGfxScrollFrameInner::GetScrollableView(nsIPresContext* aPresContext)
1110
nsIFrame* frame = nsnull;
1111
mScrollAreaBox->GetFrame(&frame);
1112
nsIView* view = frame->GetView();
1113
if (!view) return nsnull;
1115
nsIScrollableView* scrollingView;
1119
CallQueryInterface(view, &scrollingView);
1120
NS_ASSERTION(NS_SUCCEEDED(result),
1121
"assertion gfx scrollframe does not contain a scrollframe");
1122
return scrollingView;
1126
nsGfxScrollFrameInner::AddHorizontalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnTop)
1128
if (!mHScrollbarBox)
1132
const nsStyleVisibility* vis = mOuter->GetStyleVisibility();
1134
// Scroll the view horizontally if:
1135
// 1) We are creating the scrollbar for the first time and the
1136
// horizontal scroll position of the view is 0 or
1137
// 2) The display direction is changed
1139
if (mLastDir == -1) {
1140
// Creating the scrollbar the first time
1141
nscoord curPosX = 0, curPosY = 0;
1142
nsIScrollableView* s = GetScrollableView(mOuter->mPresContext);
1144
s->GetScrollPosition(curPosX, curPosY);
1146
needScroll = (curPosX == 0);
1148
needScroll = (mLastDir != vis->mDirection);
1151
SetAttribute(mHScrollbarBox, nsXULAtoms::curpos,
1152
(NS_STYLE_DIRECTION_LTR == vis->mDirection) ? 0 : 0x7FFFFFFF);
1154
mLastDir = vis->mDirection;
1157
return AddRemoveScrollbar(aState, aScrollAreaSize, aOnTop, PR_TRUE, PR_TRUE);
1161
nsGfxScrollFrameInner::AddVerticalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight)
1163
if (!mVScrollbarBox)
1166
return AddRemoveScrollbar(aState, aScrollAreaSize, aOnRight, PR_FALSE, PR_TRUE);
1170
nsGfxScrollFrameInner::RemoveHorizontalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnTop)
1172
// removing a scrollbar should always fit
1176
AddRemoveScrollbar(aState, aScrollAreaSize, aOnTop, PR_TRUE, PR_FALSE);
1177
NS_ASSERTION(result, "Removing horizontal scrollbar failed to fit??");
1181
nsGfxScrollFrameInner::RemoveVerticalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight)
1183
// removing a scrollbar should always fit
1187
AddRemoveScrollbar(aState, aScrollAreaSize, aOnRight, PR_FALSE, PR_FALSE);
1188
NS_ASSERTION(result, "Removing vertical scrollbar failed to fit??");
1192
nsGfxScrollFrameInner::AddRemoveScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnTop, PRBool aHorizontal, PRBool aAdd)
1195
if (mNeverHasHorizontalScrollbar || !mHScrollbarBox)
1199
mHScrollbarBox->GetPrefSize(aState, hSize);
1200
nsBox::AddMargin(mHScrollbarBox, hSize);
1202
SetScrollbarVisibility(mHScrollbarBox, aAdd);
1204
PRBool hasHorizontalScrollbar;
1205
PRBool fit = AddRemoveScrollbar(hasHorizontalScrollbar, aScrollAreaSize.y, aScrollAreaSize.height, hSize.height, aOnTop, aAdd);
1206
mHasHorizontalScrollbar = hasHorizontalScrollbar; // because mHasHorizontalScrollbar is a PRPackedBool
1208
SetScrollbarVisibility(mHScrollbarBox, !aAdd);
1212
if (mNeverHasVerticalScrollbar || !mVScrollbarBox)
1216
mVScrollbarBox->GetPrefSize(aState, vSize);
1217
nsBox::AddMargin(mVScrollbarBox, vSize);
1219
SetScrollbarVisibility(mVScrollbarBox, aAdd);
1221
PRBool hasVerticalScrollbar;
1222
PRBool fit = AddRemoveScrollbar(hasVerticalScrollbar, aScrollAreaSize.x, aScrollAreaSize.width, vSize.width, aOnTop, aAdd);
1223
mHasVerticalScrollbar = hasVerticalScrollbar; // because mHasVerticalScrollbar is a PRPackedBool
1225
SetScrollbarVisibility(mVScrollbarBox, !aAdd);
1232
nsGfxScrollFrameInner::AddRemoveScrollbar(PRBool& aHasScrollbar, nscoord& aXY, nscoord& aSize, nscoord aSbSize, PRBool aRightOrBottom, PRBool aAdd)
1234
nscoord size = aSize;
1237
if (size != NS_INTRINSICSIZE) {
1240
if (!aRightOrBottom && size >= 0)
1244
if (!aRightOrBottom)
1249
// not enough room? Yes? Return true.
1251
aHasScrollbar = aAdd;
1257
aHasScrollbar = PR_FALSE;
1262
nsGfxScrollFrameInner::LayoutBox(nsBoxLayoutState& aState, nsIBox* aBox, const nsRect& aRect)
1264
return mOuter->LayoutChildAt(aState, aBox, aRect);
1268
nsGfxScrollFrame::DoLayout(nsBoxLayoutState& aState)
1271
aState.GetLayoutFlags(flags);
1272
nsresult rv = mInner->Layout(aState);
1273
aState.SetLayoutFlags(flags);
1275
nsBox::DoLayout(aState);
1280
* When reflowing a HTML document where the content model is being created
1281
* The nsGfxScrollFrame will get an Initial reflow when the body is opened by the content sink.
1282
* But there isn't enough content to really reflow very much of the document
1283
* so it never needs to do layout for the scrollbars
1285
* So later other reflows happen and these are Incremental reflows, and then the scrollbars
1286
* get reflowed. The important point here is that when they reflowed the ReflowState inside the
1287
* BoxLayoutState contains an "Incremental" reason and never a "Initial" reason.
1289
* When it reflows for Print Preview, the content model is already full constructed and it lays
1290
* out the entire document at that time. When it returns back here it discovers it needs scrollbars
1291
* and this is a problem because the ReflowState inside the BoxLayoutState still has a "Initial"
1292
* reason and if it does a Layout it is essentially asking everything to reflow yet again with
1293
* an "Initial" reason. This causes a lot of problems especially for tables.
1295
* The solution for this is to change the ReflowState's reason from Initial to Resize and let
1296
* all the frames do what is necessary for a resize refow. Now, we only need to do this when
1297
* it is doing PrintPreview and we need only do it for HTML documents and NOT chrome.
1301
nsGfxScrollFrameInner::AdjustReflowStateForPrintPreview(nsBoxLayoutState& aState, PRBool& aSetBack)
1303
aSetBack = PR_FALSE;
1305
PRBool isInitialPP = nsBoxFrame::IsInitialReflowForPrintPreview(aState, isChrome);
1306
if (isInitialPP && !isChrome) {
1307
// I know you shouldn't, but we cast away the "const" here
1308
nsHTMLReflowState* reflowState = (nsHTMLReflowState*)aState.GetReflowState();
1309
reflowState->reason = eReflowReason_Resize;
1315
* Sets reflow state back to Initial when we are done.
1318
nsGfxScrollFrameInner::AdjustReflowStateBack(nsBoxLayoutState& aState, PRBool aSetBack)
1320
// I know you shouldn't, but we cast away the "const" here
1321
nsHTMLReflowState* reflowState = (nsHTMLReflowState*)aState.GetReflowState();
1322
if (aSetBack && reflowState->reason == eReflowReason_Resize) {
1323
reflowState->reason = eReflowReason_Initial;
1328
* Reflow the scroll area if it needs it and return its size. Also determine if the reflow will
1329
* cause any of the scrollbars to need to be reflowed.
1332
nsGfxScrollFrameInner::Layout(nsBoxLayoutState& aState)
1334
//TODO make bidi code set these from preferences
1336
// if true places the vertical scrollbar on the right false puts it on the left.
1337
PRBool scrollBarRight = PR_TRUE;
1339
// if true places the horizontal scrollbar on the bottom false puts it on the top.
1340
PRBool scrollBarBottom = PR_TRUE;
1343
const nsStyleVisibility* vis = mOuter->GetStyleVisibility();
1346
// Direction Style from this->GetStyleData()
1347
// now in (vis->mDirection)
1348
// ------------------
1349
// NS_STYLE_DIRECTION_LTR : LTR or Default
1350
// NS_STYLE_DIRECTION_RTL
1351
// NS_STYLE_DIRECTION_INHERIT
1354
if (vis->mDirection == NS_STYLE_DIRECTION_RTL){
1355
// if true places the vertical scrollbar on the right false puts it on the left.
1356
scrollBarRight = PR_FALSE;
1358
// if true places the horizontal scrollbar on the bottom false puts it on the top.
1359
scrollBarBottom = PR_TRUE;
1361
nsHTMLReflowState* reflowState = (nsHTMLReflowState*)aState.GetReflowState();
1364
nsIFrame* frame = nsnull;
1365
mOuter->GetFrame(&frame);
1367
// get the content rect
1368
nsRect clientRect(0,0,0,0);
1369
mOuter->GetClientRect(clientRect);
1371
// the scroll area size starts off as big as our content area
1372
nsRect scrollAreaRect(clientRect);
1375
Our basic strategy here is to first try laying out the content with
1376
the scrollbars in their current state. We're hoping that that will
1377
just "work"; the content will overflow wherever there's a scrollbar
1378
already visible. If that does work, then there's no need to lay out
1379
the scrollarea. Otherwise we fix up the scrollbars; first we add a
1380
vertical one to scroll the content if necessary, or remove it if
1381
it's not needed. Then we reflow the content if the scrollbar
1382
changed. Then we add a horizontal scrollbar if necessary (or
1383
remove if not needed), and if that changed, we reflow the content
1384
again. At this point, any scrollbars that are needed to scroll the
1385
content have been added.
1387
In the second phase we check to see if any scrollbars are too small
1388
to display, and if so, we remove them. We check the horizontal
1389
scrollbar first; removing it might make room for the vertical
1390
scrollbar, and if we have room for just one scrollbar we'll save
1393
Finally we position and size the scrollbars and scrollcorner (the
1394
square that is needed in the corner of the window when two
1395
scrollbars are visible), and reflow any fixed position views
1396
(if we're the viewport and we added or removed a scrollbar).
1399
nsGfxScrollFrame::ScrollbarStyles styles = mOuter->GetScrollbarStyles();
1401
// Look at our style do we always have vertical or horizontal scrollbars?
1402
if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL)
1403
mHasHorizontalScrollbar = PR_TRUE;
1404
if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL)
1405
mHasVerticalScrollbar = PR_TRUE;
1407
if (mHasHorizontalScrollbar)
1408
AddHorizontalScrollbar(aState, scrollAreaRect, scrollBarBottom);
1410
if (mHasVerticalScrollbar)
1411
AddVerticalScrollbar(aState, scrollAreaRect, scrollBarRight);
1413
nsRect oldScrollAreaBounds;
1414
mScrollAreaBox->GetClientRect(oldScrollAreaBounds);
1416
// layout our the scroll area
1417
LayoutBox(aState, mScrollAreaBox, scrollAreaRect);
1419
// now look at the content area and see if we need scrollbars or not
1420
PRBool needsLayout = PR_FALSE;
1421
nsSize scrolledContentSize(0,0);
1423
// if we have 'auto' scrollbars look at the vertical case
1424
if (styles.mVertical != NS_STYLE_OVERFLOW_SCROLL) {
1425
// get the area frame is the scrollarea
1426
GetScrolledSize(aState.GetPresContext(),&scrolledContentSize.width, &scrolledContentSize.height);
1428
// There are two cases to consider
1429
if (scrolledContentSize.height <= scrollAreaRect.height
1430
|| styles.mVertical != NS_STYLE_OVERFLOW_AUTO) {
1431
if (mHasVerticalScrollbar) {
1432
// We left room for the vertical scrollbar, but it's not needed;
1434
RemoveVerticalScrollbar(aState, scrollAreaRect, scrollBarRight);
1435
needsLayout = PR_TRUE;
1436
SetAttribute(mVScrollbarBox, nsXULAtoms::curpos, 0);
1439
if (!mHasVerticalScrollbar) {
1440
// We didn't leave room for the vertical scrollbar, but it turns
1442
if (AddVerticalScrollbar(aState, scrollAreaRect, scrollBarRight))
1443
needsLayout = PR_TRUE;
1447
// ok layout at the right size
1449
nsBoxLayoutState resizeState(aState);
1450
resizeState.SetLayoutReason(nsBoxLayoutState::Resize);
1452
AdjustReflowStateForPrintPreview(aState, setBack);
1453
LayoutBox(resizeState, mScrollAreaBox, scrollAreaRect);
1454
AdjustReflowStateBack(aState, setBack);
1455
needsLayout = PR_FALSE;
1460
// if scrollbars are auto look at the horizontal case
1461
if (styles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL)
1463
// get the area frame is the scrollarea
1464
GetScrolledSize(aState.GetPresContext(),&scrolledContentSize.width, &scrolledContentSize.height);
1466
// if the child is wider that the scroll area
1467
// and we don't have a scrollbar add one.
1468
if (scrolledContentSize.width > scrollAreaRect.width
1469
&& styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO) {
1471
if (!mHasHorizontalScrollbar) {
1473
if (AddHorizontalScrollbar(aState, scrollAreaRect, scrollBarBottom))
1474
needsLayout = PR_TRUE;
1476
// if we added a horizonal scrollbar and we did not have a vertical
1477
// there is a chance that by adding the horizonal scrollbar we will
1478
// suddenly need a vertical scrollbar. Is a special case but its
1480
//if (!mHasVerticalScrollbar && scrolledContentSize.height > scrollAreaRect.height - sbSize.height)
1481
// printf("****Gfx Scrollbar Special case hit!!*****\n");
1485
const nsStyleVisibility* ourVis = frame->GetStyleVisibility();
1487
if (NS_STYLE_DIRECTION_RTL == ourVis->mDirection) {
1488
nsCOMPtr<nsITextControlFrame> textControl(
1489
do_QueryInterface(mOuter->mParent) );
1491
needsLayout = PR_TRUE;
1492
reflowState->mRightEdge = scrolledContentSize.width;
1493
mScrollAreaBox->MarkDirty(aState);
1498
// if the area is smaller or equal to and we have a scrollbar then
1500
if (mHasHorizontalScrollbar) {
1501
RemoveHorizontalScrollbar(aState, scrollAreaRect, scrollBarBottom);
1502
needsLayout = PR_TRUE;
1503
SetAttribute(mHScrollbarBox, nsXULAtoms::curpos, 0);
1508
// we only need to set the rect. The inner child stays the same size.
1510
nsBoxLayoutState resizeState(aState);
1511
resizeState.SetLayoutReason(nsBoxLayoutState::Resize);
1513
AdjustReflowStateForPrintPreview(aState, setBack);
1514
LayoutBox(resizeState, mScrollAreaBox, scrollAreaRect);
1515
AdjustReflowStateBack(aState, setBack);
1516
needsLayout = PR_FALSE;
1518
reflowState->mRightEdge = NS_UNCONSTRAINEDSIZE;
1522
GetScrolledSize(aState.GetPresContext(),&scrolledContentSize.width, &scrolledContentSize.height);
1524
nsIPresContext* presContext = aState.GetPresContext();
1526
presContext->GetScaledPixelsToTwips(&p2t);
1527
mOnePixel = NSIntPixelsToTwips(1, p2t);
1528
const nsStyleFont* font = mOuter->GetStyleFont();
1529
const nsFont& f = font->mFont;
1530
nsCOMPtr<nsIFontMetrics> fm;
1531
presContext->GetMetricsFor(f, getter_AddRefs(fm));
1532
nscoord fontHeight = 1;
1533
NS_ASSERTION(fm,"FontMetrics is null assuming fontHeight == 1");
1535
fm->GetHeight(fontHeight);
1537
// get the preferred size of the scrollbars
1540
nsSize hMinSize(0,0);
1541
nsSize vMinSize(0,0);
1543
if (mHScrollbarBox && mHasHorizontalScrollbar) {
1544
mHScrollbarBox->GetPrefSize(aState, hSize);
1545
mHScrollbarBox->GetMinSize(aState, hMinSize);
1546
nsBox::AddMargin(mHScrollbarBox, hSize);
1547
nsBox::AddMargin(mHScrollbarBox, hMinSize);
1549
if (mVScrollbarBox && mHasVerticalScrollbar) {
1550
mVScrollbarBox->GetPrefSize(aState, vSize);
1551
mVScrollbarBox->GetMinSize(aState, vMinSize);
1552
nsBox::AddMargin(mVScrollbarBox, vSize);
1553
nsBox::AddMargin(mVScrollbarBox, vMinSize);
1556
// Disable scrollbars that are too small
1557
// Disable horizontal scrollbar first. If we have to disable only one
1558
// scrollbar, we'd rather keep the vertical scrollbar.
1559
// Note that we always give horizontal scrollbars their preferred height,
1560
// never their min-height. So check that there's room for the preferred height.
1561
if (mHasHorizontalScrollbar &&
1562
(hMinSize.width > clientRect.width - vSize.width
1563
|| hSize.height > clientRect.height)) {
1564
RemoveHorizontalScrollbar(aState, scrollAreaRect, scrollBarBottom);
1565
needsLayout = PR_TRUE;
1566
SetAttribute(mHScrollbarBox, nsXULAtoms::curpos, 0);
1567
// set the scrollbar to zero height to make it go away
1569
hMinSize.height = 0;
1571
// Now disable vertical scrollbar if necessary
1572
if (mHasVerticalScrollbar &&
1573
(vMinSize.height > clientRect.height - hSize.height
1574
|| vSize.width > clientRect.width)) {
1575
RemoveVerticalScrollbar(aState, scrollAreaRect, scrollBarRight);
1576
needsLayout = PR_TRUE;
1577
SetAttribute(mVScrollbarBox, nsXULAtoms::curpos, 0);
1578
// set the scrollbar to zero width to make it go away
1583
nscoord maxX = scrolledContentSize.width - scrollAreaRect.width;
1584
nscoord maxY = scrolledContentSize.height - scrollAreaRect.height;
1586
nsIScrollableView* scrollable = GetScrollableView(presContext);
1587
scrollable->SetLineHeight(fontHeight);
1589
if (mVScrollbarBox) {
1590
SetAttribute(mVScrollbarBox, nsXULAtoms::maxpos, maxY);
1591
SetAttribute(mVScrollbarBox, nsXULAtoms::pageincrement, nscoord(scrollAreaRect.height - fontHeight));
1592
SetAttribute(mVScrollbarBox, nsXULAtoms::increment, fontHeight);
1594
nsRect vRect(scrollAreaRect);
1595
vRect.width = vSize.width;
1596
vRect.x = scrollBarRight ? scrollAreaRect.XMost() : clientRect.x;
1598
mVScrollbarBox->GetMargin(margin);
1599
vRect.Deflate(margin);
1600
LayoutBox(aState, mVScrollbarBox, vRect);
1603
if (mHScrollbarBox) {
1604
SetAttribute(mHScrollbarBox, nsXULAtoms::maxpos, maxX);
1605
SetAttribute(mHScrollbarBox, nsXULAtoms::pageincrement, nscoord(float(scrollAreaRect.width)*0.8));
1606
SetAttribute(mHScrollbarBox, nsXULAtoms::increment, 10*mOnePixel);
1608
nsRect hRect(scrollAreaRect);
1609
hRect.height = hSize.height;
1610
hRect.y = scrollBarBottom ? scrollAreaRect.YMost() : clientRect.y;
1612
mHScrollbarBox->GetMargin(margin);
1613
hRect.Deflate(margin);
1614
LayoutBox(aState, mHScrollbarBox, hRect);
1617
// we only need to set the rect. The inner child stays the same size.
1619
nsBoxLayoutState resizeState(aState);
1620
resizeState.SetLayoutReason(nsBoxLayoutState::Resize);
1621
LayoutBox(resizeState, mScrollAreaBox, scrollAreaRect);
1622
needsLayout = PR_FALSE;
1625
// place the scrollcorner
1626
if (mScrollCornerBox) {
1627
nsRect r(0, 0, 0, 0);
1628
if (clientRect.x != scrollAreaRect.x) {
1629
// scrollbar (if any) on left
1631
r.width = scrollAreaRect.x - clientRect.x;
1632
NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
1634
// scrollbar (if any) on right
1635
r.x = scrollAreaRect.XMost();
1636
r.width = clientRect.XMost() - scrollAreaRect.XMost();
1637
NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
1639
if (clientRect.y != scrollAreaRect.y) {
1640
// scrollbar (if any) on top
1642
r.height = scrollAreaRect.y - clientRect.y;
1643
NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
1645
// scrollbar (if any) on bottom
1646
r.y = scrollAreaRect.YMost();
1647
r.height = clientRect.YMost() - scrollAreaRect.YMost();
1648
NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
1650
LayoutBox(aState, mScrollCornerBox, r);
1653
// may need to update fixed position children of the viewport,
1654
// if the client area changed size because of some dirty reflow
1655
// (if the reflow is initial or resize, the fixed children will
1656
// be re-laid out anyway)
1657
if ((oldScrollAreaBounds.width != scrollAreaRect.width
1658
|| oldScrollAreaBounds.height != scrollAreaRect.height)
1659
&& nsBoxLayoutState::Dirty == aState.GetLayoutReason()) {
1660
nsIFrame* parentFrame = mOuter->GetParent();
1662
if (parentFrame->GetType() == nsLayoutAtoms::viewportFrame) {
1663
// Usually there are no fixed children, so don't do anything unless there's
1664
// at least one fixed child
1665
if (parentFrame->GetFirstChild(nsLayoutAtoms::fixedList)) {
1666
// force a reflow of the fixed children
1667
nsFrame::CreateAndPostReflowCommand(mOuter->mPresContext->PresShell(),
1669
eReflowType_UserDefined, nsnull, nsnull, nsLayoutAtoms::fixedList);
1679
nsGfxScrollFrameInner::ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY, PRUint32 aFlags)
1681
nsIScrollableView* scrollable = GetScrollableView(aPresContext);
1682
scrollable->ScrollTo(aX, aY, aFlags);
1683
// printf("scrolling to: %d, %d\n", aX, aY);
1686
nsGfxScrollFrameInner::~nsGfxScrollFrameInner()
1691
* Returns whether it actually needed to change the attribute
1694
nsGfxScrollFrameInner::SetAttribute(nsIBox* aBox, nsIAtom* aAtom, nscoord aSize, PRBool aReflow)
1698
// convert to pixels
1701
// only set the attribute if it changed.
1703
PRInt32 current = GetIntegerAttribute(aBox, aAtom, -1);
1704
if (current != aSize)
1706
nsIFrame* frame = nsnull;
1707
aBox->GetFrame(&frame);
1708
nsAutoString newValue;
1709
newValue.AppendInt(aSize);
1710
frame->GetContent()->SetAttr(kNameSpaceID_None, aAtom, newValue, aReflow);
1719
* Gets the size of the area that lies inside the scrollbars but clips the scrolled frame
1722
nsGfxScrollFrameInner::GetScrolledSize(nsIPresContext* aPresContext,
1724
nscoord *aHeight) const
1727
// our scrolled size is the size of our scrolled view.
1728
nsIBox* child = nsnull;
1729
mScrollAreaBox->GetChildBox(&child);
1731
child->GetFrame(&frame);
1732
nsIView* view = frame->GetView();
1733
NS_ASSERTION(view,"Scrolled frame must have a view!!!");
1735
nsRect rect = view->GetBounds();
1736
nsSize size(rect.width, rect.height);
1738
nsBox::AddMargin(child, size);
1739
nsBox::AddBorderAndPadding(mScrollAreaBox, size);
1740
nsBox::AddInset(mScrollAreaBox, size);
1742
*aWidth = size.width;
1743
*aHeight = size.height;
1749
nsGfxScrollFrameInner::SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisible)
1754
nsCOMPtr<nsIScrollbarFrame> scrollbar(do_QueryInterface(aScrollbar));
1756
// See if we have a mediator.
1757
nsCOMPtr<nsIScrollbarMediator> mediator;
1758
scrollbar->GetScrollbarMediator(getter_AddRefs(mediator));
1760
// Inform the mediator of the visibility change.
1761
mediator->VisibilityChanged(aVisible);
1767
nsGfxScrollFrameInner::GetIntegerAttribute(nsIBox* aBox, nsIAtom* atom, PRInt32 defaultValue)
1769
nsIFrame* frame = nsnull;
1770
aBox->GetFrame(&frame);
1772
nsIContent* content = frame->GetContent();
1775
if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttr(kNameSpaceID_None, atom, value))
1779
// convert it to an integer
1780
defaultValue = value.ToInteger(&error);
1783
return defaultValue;
1787
nsGfxScrollFrame::GetContentOf(nsIContent** aContent)
1789
*aContent = GetContent();
1790
NS_IF_ADDREF(*aContent);