Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / layout / generic / nsFrame.cpp
blobdeeef7a0cc832ff135debfa2e29cadd472f0f86c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Uri Bernstein <uriber@gmail.com>
26 * Eli Friedman <sharparrow1@yahoo.com>
27 * Mats Palmgren <matspal@gmail.com>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either of the GNU General Public License Version 2 or later (the "GPL"),
31 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
43 /* base class of all rendering objects */
45 #include "nsCOMPtr.h"
46 #include "nsFrame.h"
47 #include "nsFrameList.h"
48 #include "nsPlaceholderFrame.h"
49 #include "nsLineLayout.h"
50 #include "nsIContent.h"
51 #include "nsContentUtils.h"
52 #include "nsIAtom.h"
53 #include "nsString.h"
54 #include "nsReadableUtils.h"
55 #include "nsStyleContext.h"
56 #include "nsIView.h"
57 #include "nsIViewManager.h"
58 #include "nsIScrollableFrame.h"
59 #include "nsPresContext.h"
60 #include "nsCRT.h"
61 #include "nsGUIEvent.h"
62 #include "nsIDOMEvent.h"
63 #include "nsPLDOMEvent.h"
64 #include "nsStyleConsts.h"
65 #include "nsIPresShell.h"
66 #include "prlog.h"
67 #include "prprf.h"
68 #include <stdarg.h>
69 #include "nsFrameManager.h"
70 #include "nsCSSRendering.h"
71 #include "nsLayoutUtils.h"
72 #ifdef ACCESSIBILITY
73 #include "nsIAccessible.h"
74 #endif
76 #include "nsIDOMText.h"
77 #include "nsIDOMHTMLAnchorElement.h"
78 #include "nsIDOMHTMLAreaElement.h"
79 #include "nsIDOMHTMLImageElement.h"
80 #include "nsIDOMHTMLHRElement.h"
81 #include "nsIDOMHTMLInputElement.h"
82 #include "nsIDeviceContext.h"
83 #include "nsIEditorDocShell.h"
84 #include "nsIEventStateManager.h"
85 #include "nsISelection.h"
86 #include "nsISelectionPrivate.h"
87 #include "nsFrameSelection.h"
88 #include "nsHTMLParts.h"
89 #include "nsGkAtoms.h"
90 #include "nsCSSAnonBoxes.h"
91 #include "nsCSSPseudoElements.h"
92 #include "nsIHTMLContentSink.h"
93 #include "nsCSSFrameConstructor.h"
95 #include "nsFrameTraversal.h"
96 #include "nsStyleChangeList.h"
97 #include "nsIDOMRange.h"
98 #include "nsITableLayout.h" //selection necessity
99 #include "nsITableCellLayout.h"// "
100 #include "nsITextControlFrame.h"
101 #include "nsINameSpaceManager.h"
102 #include "nsIPercentHeightObserver.h"
103 #include "nsStyleStructInlines.h"
105 #ifdef IBMBIDI
106 #include "nsBidiPresUtils.h"
107 #endif
109 // For triple-click pref
110 #include "nsIServiceManager.h"
111 #include "imgIContainer.h"
112 #include "imgIRequest.h"
113 #include "nsILookAndFeel.h"
114 #include "nsLayoutCID.h"
115 #include "nsWidgetsCID.h" // for NS_LOOKANDFEEL_CID
116 #include "nsUnicharUtils.h"
117 #include "nsLayoutErrors.h"
118 #include "nsContentErrors.h"
119 #include "nsHTMLContainerFrame.h"
120 #include "nsBoxLayoutState.h"
121 #include "nsBlockFrame.h"
122 #include "nsDisplayList.h"
123 #include "nsIObjectLoadingContent.h"
124 #include "nsExpirationTracker.h"
125 #ifdef MOZ_SVG
126 #include "nsSVGIntegrationUtils.h"
127 #include "nsSVGEffects.h"
128 #endif
130 #include "gfxContext.h"
131 #include "CSSCalc.h"
133 using namespace mozilla;
135 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
137 // Struct containing cached metrics for box-wrapped frames.
138 struct nsBoxLayoutMetrics
140 nsSize mPrefSize;
141 nsSize mMinSize;
142 nsSize mMaxSize;
144 nsSize mBlockMinSize;
145 nsSize mBlockPrefSize;
146 nscoord mBlockAscent;
148 nscoord mFlex;
149 nscoord mAscent;
151 nsSize mLastSize;
154 struct nsContentAndOffset
156 nsIContent* mContent;
157 PRInt32 mOffset;
160 // Some Misc #defines
161 #define SELECTION_DEBUG 0
162 #define FORCE_SELECTION_UPDATE 1
163 #define CALC_DEBUG 0
166 #include "nsILineIterator.h"
168 //non Hack prototypes
169 #if 0
170 static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent);
171 #endif
173 #include "prenv.h"
175 // Formerly the nsIFrameDebug interface
177 #ifdef NS_DEBUG
178 static PRBool gShowFrameBorders = PR_FALSE;
180 void nsFrame::ShowFrameBorders(PRBool aEnable)
182 gShowFrameBorders = aEnable;
185 PRBool nsFrame::GetShowFrameBorders()
187 return gShowFrameBorders;
190 static PRBool gShowEventTargetFrameBorder = PR_FALSE;
192 void nsFrame::ShowEventTargetFrameBorder(PRBool aEnable)
194 gShowEventTargetFrameBorder = aEnable;
197 PRBool nsFrame::GetShowEventTargetFrameBorder()
199 return gShowEventTargetFrameBorder;
203 * Note: the log module is created during library initialization which
204 * means that you cannot perform logging before then.
206 static PRLogModuleInfo* gLogModule;
208 static PRLogModuleInfo* gStyleVerifyTreeLogModuleInfo;
210 static PRBool gStyleVerifyTreeEnable = PRBool(0x55);
212 PRBool
213 nsFrame::GetVerifyStyleTreeEnable()
215 if (gStyleVerifyTreeEnable == PRBool(0x55)) {
216 if (nsnull == gStyleVerifyTreeLogModuleInfo) {
217 gStyleVerifyTreeLogModuleInfo = PR_NewLogModule("styleverifytree");
218 gStyleVerifyTreeEnable = 0 != gStyleVerifyTreeLogModuleInfo->level;
221 return gStyleVerifyTreeEnable;
224 void
225 nsFrame::SetVerifyStyleTreeEnable(PRBool aEnabled)
227 gStyleVerifyTreeEnable = aEnabled;
230 PRLogModuleInfo*
231 nsFrame::GetLogModuleInfo()
233 if (nsnull == gLogModule) {
234 gLogModule = PR_NewLogModule("frame");
236 return gLogModule;
239 void
240 nsFrame::DumpFrameTree(nsIFrame* aFrame)
242 RootFrameList(aFrame->PresContext(), stdout, 0);
245 void
246 nsFrame::RootFrameList(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent)
248 if (!aPresContext || !out)
249 return;
251 nsIPresShell *shell = aPresContext->GetPresShell();
252 if (shell) {
253 nsIFrame* frame = shell->FrameManager()->GetRootFrame();
254 if(frame) {
255 frame->List(out, aIndent);
259 #endif
261 void
262 NS_MergeReflowStatusInto(nsReflowStatus* aPrimary, nsReflowStatus aSecondary)
264 *aPrimary |= aSecondary &
265 (NS_FRAME_NOT_COMPLETE | NS_FRAME_OVERFLOW_INCOMPLETE |
266 NS_FRAME_TRUNCATED | NS_FRAME_REFLOW_NEXTINFLOW);
267 if (*aPrimary & NS_FRAME_NOT_COMPLETE) {
268 *aPrimary &= ~NS_FRAME_OVERFLOW_INCOMPLETE;
272 void
273 nsWeakFrame::InitInternal(nsIFrame* aFrame)
275 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nsnull);
276 mFrame = aFrame;
277 if (mFrame) {
278 nsIPresShell* shell = mFrame->PresContext()->GetPresShell();
279 NS_WARN_IF_FALSE(shell, "Null PresShell in nsWeakFrame!");
280 if (shell) {
281 shell->AddWeakFrame(this);
282 } else {
283 mFrame = nsnull;
288 nsIFrame*
289 NS_NewEmptyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
291 return new (aPresShell) nsFrame(aContext);
294 nsFrame::nsFrame(nsStyleContext* aContext)
296 MOZ_COUNT_CTOR(nsFrame);
298 mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY;
299 mStyleContext = aContext;
300 mStyleContext->AddRef();
303 nsFrame::~nsFrame()
305 MOZ_COUNT_DTOR(nsFrame);
307 NS_IF_RELEASE(mContent);
308 if (mStyleContext)
309 mStyleContext->Release();
312 NS_IMPL_FRAMEARENA_HELPERS(nsFrame)
314 // Dummy operator delete. Will never be called, but must be defined
315 // to satisfy some C++ ABIs.
316 void
317 nsFrame::operator delete(void *, size_t)
319 NS_RUNTIMEABORT("nsFrame::operator delete should never be called");
322 NS_QUERYFRAME_HEAD(nsFrame)
323 NS_QUERYFRAME_ENTRY(nsIFrame)
324 NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
326 /////////////////////////////////////////////////////////////////////////////
327 // nsIFrame
329 NS_IMETHODIMP
330 nsFrame::Init(nsIContent* aContent,
331 nsIFrame* aParent,
332 nsIFrame* aPrevInFlow)
334 NS_PRECONDITION(!mContent, "Double-initing a frame?");
335 NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) &&
336 !IsFrameOfType(eDEBUGNoFrames),
337 "IsFrameOfType implementation that doesn't call base class");
339 mContent = aContent;
340 mParent = aParent;
342 if (aContent) {
343 NS_ADDREF(aContent);
346 if (aPrevInFlow) {
347 // Make sure the general flags bits are the same
348 nsFrameState state = aPrevInFlow->GetStateBits();
350 // Make bits that are currently off (see constructor) the same:
351 mState |= state & (NS_FRAME_SELECTED_CONTENT |
352 NS_FRAME_INDEPENDENT_SELECTION |
353 NS_FRAME_IS_SPECIAL |
354 NS_FRAME_MAY_BE_TRANSFORMED);
356 if (mParent) {
357 nsFrameState state = mParent->GetStateBits();
359 // Make bits that are currently off (see constructor) the same:
360 mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
361 NS_FRAME_GENERATED_CONTENT);
363 if (GetStyleDisplay()->HasTransform()) {
364 // The frame gets reconstructed if we toggle the -moz-transform
365 // property, so we can set this bit here and then ignore it.
366 mState |= NS_FRAME_MAY_BE_TRANSFORMED;
369 DidSetStyleContext(nsnull);
371 if (IsBoxWrapped())
372 InitBoxMetrics(PR_FALSE);
374 return NS_OK;
377 NS_IMETHODIMP nsFrame::SetInitialChildList(nsIAtom* aListName,
378 nsFrameList& aChildList)
380 // XXX This shouldn't be getting called at all, but currently is for backwards
381 // compatility reasons...
382 #if 0
383 NS_ERROR("not a container");
384 return NS_ERROR_UNEXPECTED;
385 #else
386 NS_ASSERTION(aChildList.IsEmpty(), "not a container");
387 return NS_OK;
388 #endif
391 NS_IMETHODIMP
392 nsFrame::AppendFrames(nsIAtom* aListName,
393 nsFrameList& aFrameList)
395 NS_PRECONDITION(PR_FALSE, "not a container");
396 return NS_ERROR_UNEXPECTED;
399 NS_IMETHODIMP
400 nsFrame::InsertFrames(nsIAtom* aListName,
401 nsIFrame* aPrevFrame,
402 nsFrameList& aFrameList)
404 NS_PRECONDITION(PR_FALSE, "not a container");
405 return NS_ERROR_UNEXPECTED;
408 NS_IMETHODIMP
409 nsFrame::RemoveFrame(nsIAtom* aListName,
410 nsIFrame* aOldFrame)
412 NS_PRECONDITION(PR_FALSE, "not a container");
413 return NS_ERROR_UNEXPECTED;
416 void
417 nsFrame::DestroyFrom(nsIFrame* aDestructRoot)
419 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
420 "destroy called on frame while scripts not blocked");
421 NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
422 "Frames should be removed before destruction.");
423 NS_ASSERTION(aDestructRoot, "Must specify destruct root");
425 #ifdef MOZ_SVG
426 nsSVGEffects::InvalidateDirectRenderingObservers(this);
427 #endif
429 // Get the view pointer now before the frame properties disappear
430 // when we call NotifyDestroyingFrame()
431 nsIView* view = GetView();
432 nsPresContext* presContext = PresContext();
434 nsIPresShell *shell = presContext->GetPresShell();
435 if (mState & NS_FRAME_OUT_OF_FLOW) {
436 nsPlaceholderFrame* placeholder =
437 shell->FrameManager()->GetPlaceholderFrameFor(this);
438 NS_ASSERTION(!placeholder || (aDestructRoot != this),
439 "Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
440 NS_ASSERTION(!placeholder ||
441 nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, placeholder),
442 "Placeholder relationship should have been torn down already; "
443 "this might mean we have a stray placeholder in the tree.");
444 if (placeholder) {
445 shell->FrameManager()->UnregisterPlaceholderFrame(placeholder);
446 placeholder->SetOutOfFlowFrame(nsnull);
450 shell->NotifyDestroyingFrame(this);
452 if ((mState & NS_FRAME_EXTERNAL_REFERENCE) ||
453 (mState & NS_FRAME_SELECTED_CONTENT)) {
454 shell->ClearFrameRefs(this);
457 if (view) {
458 // Break association between view and frame
459 view->SetClientData(nsnull);
461 // Destroy the view
462 view->Destroy();
465 // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
466 if (mContent && mContent->GetPrimaryFrame() == this) {
467 mContent->SetPrimaryFrame(nsnull);
470 // Must retrieve the object ID before calling destructors, so the
471 // vtable is still valid.
473 // Note to future tweakers: having the method that returns the
474 // object size call the destructor will not avoid an indirect call;
475 // the compiler cannot devirtualize the call to the destructor even
476 // if it's from a method defined in the same class.
478 nsQueryFrame::FrameIID id = GetFrameId();
479 this->~nsFrame();
481 // Now that we're totally cleaned out, we need to add ourselves to
482 // the presshell's recycler.
483 shell->FreeFrame(id, this);
486 NS_IMETHODIMP
487 nsFrame::GetOffsets(PRInt32 &aStart, PRInt32 &aEnd) const
489 aStart = 0;
490 aEnd = 0;
491 return NS_OK;
494 static PRBool
495 EqualImages(imgIRequest *aOldImage, imgIRequest *aNewImage)
497 if (aOldImage == aNewImage)
498 return PR_TRUE;
500 if (!aOldImage || !aNewImage)
501 return PR_FALSE;
503 nsCOMPtr<nsIURI> oldURI, newURI;
504 aOldImage->GetURI(getter_AddRefs(oldURI));
505 aNewImage->GetURI(getter_AddRefs(newURI));
506 PRBool equal;
507 return NS_SUCCEEDED(oldURI->Equals(newURI, &equal)) && equal;
510 // Subclass hook for style post processing
511 /* virtual */ void
512 nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
514 if (aOldStyleContext) {
515 // If the old context had a background image image and new context
516 // does not have the same image, clear the image load notifier
517 // (which keeps the image loading, if it still is) for the frame.
518 // We want to do this conservatively because some frames paint their
519 // backgrounds from some other frame's style data, and we don't want
520 // to clear those notifiers unless we have to. (They'll be reset
521 // when we paint, although we could miss a notification in that
522 // interval.)
523 const nsStyleBackground *oldBG = aOldStyleContext->GetStyleBackground();
524 const nsStyleBackground *newBG = GetStyleBackground();
525 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, oldBG) {
526 if (i >= newBG->mImageCount ||
527 oldBG->mLayers[i].mImage != newBG->mLayers[i].mImage) {
528 // stop the image loading for the frame, the image has changed
529 PresContext()->SetImageLoaders(this,
530 nsPresContext::BACKGROUND_IMAGE, nsnull);
531 break;
535 // If we detect a change on margin, padding or border, we store the old
536 // values on the frame itself between now and reflow, so if someone
537 // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
538 // can give an accurate answer.
539 // We don't want to set the property if one already exists.
540 FrameProperties props = Properties();
541 nsMargin oldValue(0, 0, 0, 0);
542 nsMargin newValue(0, 0, 0, 0);
543 const nsStyleMargin* oldMargin = aOldStyleContext->PeekStyleMargin();
544 if (oldMargin && oldMargin->GetMargin(oldValue)) {
545 if ((!GetStyleMargin()->GetMargin(newValue) || oldValue != newValue) &&
546 !props.Get(UsedMarginProperty())) {
547 props.Set(UsedMarginProperty(), new nsMargin(oldValue));
551 const nsStylePadding* oldPadding = aOldStyleContext->PeekStylePadding();
552 if (oldPadding && oldPadding->GetPadding(oldValue)) {
553 if ((!GetStylePadding()->GetPadding(newValue) || oldValue != newValue) &&
554 !props.Get(UsedPaddingProperty())) {
555 props.Set(UsedPaddingProperty(), new nsMargin(oldValue));
559 const nsStyleBorder* oldBorder = aOldStyleContext->PeekStyleBorder();
560 if (oldBorder) {
561 oldValue = oldBorder->GetActualBorder();
562 newValue = GetStyleBorder()->GetActualBorder();
563 if (oldValue != newValue &&
564 !props.Get(UsedBorderProperty())) {
565 props.Set(UsedBorderProperty(), new nsMargin(oldValue));
570 imgIRequest *oldBorderImage = aOldStyleContext
571 ? aOldStyleContext->GetStyleBorder()->GetBorderImage()
572 : nsnull;
573 // For border-images, we can't be as conservative (we need to set the
574 // new loaders if there has been any change) since the CalcDifference
575 // call depended on the result of GetActualBorder() and that result
576 // depends on whether the image has loaded, start the image load now
577 // so that we'll get notified when it completes loading and can do a
578 // restyle. Otherwise, the image might finish loading from the
579 // network before we start listening to its notifications, and then
580 // we'll never know that it's finished loading. Likewise, we want to
581 // do this for freshly-created frames to prevent a similar race if the
582 // image loads between reflow (which can depend on whether the image
583 // is loaded) and paint. We also don't really care about any callers
584 // who try to paint borders with a different style context, because
585 // they won't have the correct size for the border either.
586 if (!EqualImages(oldBorderImage, GetStyleBorder()->GetBorderImage())) {
587 // stop and restart the image loading/notification
588 PresContext()->SetupBorderImageLoaders(this, GetStyleBorder());
591 // If the page contains markup that overrides text direction, and
592 // does not contain any characters that would activate the Unicode
593 // bidi algorithm, we need to call |SetBidiEnabled| on the pres
594 // context before reflow starts. See bug 115921.
595 if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
596 PresContext()->SetBidiEnabled();
600 /* virtual */ nsMargin
601 nsIFrame::GetUsedMargin() const
603 nsMargin margin(0, 0, 0, 0);
604 if ((mState & NS_FRAME_FIRST_REFLOW) &&
605 !(mState & NS_FRAME_IN_REFLOW))
606 return margin;
608 nsMargin *m = static_cast<nsMargin*>
609 (Properties().Get(UsedMarginProperty()));
610 if (m) {
611 margin = *m;
612 } else {
613 #ifdef DEBUG
614 PRBool hasMargin =
615 #endif
616 GetStyleMargin()->GetMargin(margin);
617 NS_ASSERTION(hasMargin, "We should have a margin here! (out of memory?)");
619 return margin;
622 /* virtual */ nsMargin
623 nsIFrame::GetUsedBorder() const
625 nsMargin border(0, 0, 0, 0);
626 if ((mState & NS_FRAME_FIRST_REFLOW) &&
627 !(mState & NS_FRAME_IN_REFLOW))
628 return border;
630 // Theme methods don't use const-ness.
631 nsIFrame *mutable_this = const_cast<nsIFrame*>(this);
633 const nsStyleDisplay *disp = GetStyleDisplay();
634 if (mutable_this->IsThemed(disp)) {
635 nsIntMargin result;
636 nsPresContext *presContext = PresContext();
637 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
638 mutable_this, disp->mAppearance,
639 &result);
640 border.left = presContext->DevPixelsToAppUnits(result.left);
641 border.top = presContext->DevPixelsToAppUnits(result.top);
642 border.right = presContext->DevPixelsToAppUnits(result.right);
643 border.bottom = presContext->DevPixelsToAppUnits(result.bottom);
644 return border;
647 nsMargin *b = static_cast<nsMargin*>
648 (Properties().Get(UsedBorderProperty()));
649 if (b) {
650 border = *b;
651 } else {
652 border = GetStyleBorder()->GetActualBorder();
654 return border;
657 /* virtual */ nsMargin
658 nsIFrame::GetUsedPadding() const
660 nsMargin padding(0, 0, 0, 0);
661 if ((mState & NS_FRAME_FIRST_REFLOW) &&
662 !(mState & NS_FRAME_IN_REFLOW))
663 return padding;
665 // Theme methods don't use const-ness.
666 nsIFrame *mutable_this = const_cast<nsIFrame*>(this);
668 const nsStyleDisplay *disp = GetStyleDisplay();
669 if (mutable_this->IsThemed(disp)) {
670 nsPresContext *presContext = PresContext();
671 nsIntMargin widget;
672 if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
673 mutable_this,
674 disp->mAppearance,
675 &widget)) {
676 padding.top = presContext->DevPixelsToAppUnits(widget.top);
677 padding.right = presContext->DevPixelsToAppUnits(widget.right);
678 padding.bottom = presContext->DevPixelsToAppUnits(widget.bottom);
679 padding.left = presContext->DevPixelsToAppUnits(widget.left);
680 return padding;
684 nsMargin *p = static_cast<nsMargin*>
685 (Properties().Get(UsedPaddingProperty()));
686 if (p) {
687 padding = *p;
688 } else {
689 #ifdef DEBUG
690 PRBool hasPadding =
691 #endif
692 GetStylePadding()->GetPadding(padding);
693 NS_ASSERTION(hasPadding, "We should have padding here! (out of memory?)");
695 return padding;
698 void
699 nsIFrame::ApplySkipSides(nsMargin& aMargin) const
701 PRIntn skipSides = GetSkipSides();
702 if (skipSides & (1 << NS_SIDE_TOP))
703 aMargin.top = 0;
704 if (skipSides & (1 << NS_SIDE_RIGHT))
705 aMargin.right = 0;
706 if (skipSides & (1 << NS_SIDE_BOTTOM))
707 aMargin.bottom = 0;
708 if (skipSides & (1 << NS_SIDE_LEFT))
709 aMargin.left = 0;
712 nsRect
713 nsIFrame::GetPaddingRect() const
715 nsMargin b(GetUsedBorder());
716 ApplySkipSides(b);
717 nsRect r(mRect);
718 r.Deflate(b);
719 return r;
722 PRBool
723 nsIFrame::IsTransformed() const
725 return (mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
726 GetStyleDisplay()->HasTransform();
729 nsRect
730 nsIFrame::GetContentRect() const
732 nsMargin bp(GetUsedBorderAndPadding());
733 ApplySkipSides(bp);
734 nsRect r(mRect);
735 r.Deflate(bp);
736 return r;
739 PRBool
740 nsIFrame::ComputeBorderRadii(const nsStyleCorners& aBorderRadius,
741 const nsSize& aFrameSize,
742 const nsSize& aBorderArea,
743 PRIntn aSkipSides,
744 nscoord aRadii[8])
746 // Percentages are relative to whichever side they're on.
747 NS_FOR_CSS_HALF_CORNERS(i) {
748 const nsStyleCoord c = aBorderRadius.Get(i);
749 nscoord axis =
750 NS_HALF_CORNER_IS_X(i) ? aFrameSize.width : aFrameSize.height;
752 if (c.IsCoordPercentCalcUnit()) {
753 aRadii[i] = nsRuleNode::ComputeCoordPercentCalc(c, axis);
754 if (aRadii[i] < 0) {
755 // clamp calc()
756 aRadii[i] = 0;
758 } else {
759 NS_NOTREACHED("ComputeBorderRadii: bad unit");
760 aRadii[i] = 0;
764 if (aSkipSides & (1 << NS_SIDE_TOP)) {
765 aRadii[NS_CORNER_TOP_LEFT_X] = 0;
766 aRadii[NS_CORNER_TOP_LEFT_Y] = 0;
767 aRadii[NS_CORNER_TOP_RIGHT_X] = 0;
768 aRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
771 if (aSkipSides & (1 << NS_SIDE_RIGHT)) {
772 aRadii[NS_CORNER_TOP_RIGHT_X] = 0;
773 aRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
774 aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
775 aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
778 if (aSkipSides & (1 << NS_SIDE_BOTTOM)) {
779 aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
780 aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
781 aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
782 aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
785 if (aSkipSides & (1 << NS_SIDE_LEFT)) {
786 aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
787 aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
788 aRadii[NS_CORNER_TOP_LEFT_X] = 0;
789 aRadii[NS_CORNER_TOP_LEFT_Y] = 0;
792 // css3-background specifies this algorithm for reducing
793 // corner radii when they are too big.
794 PRBool haveRadius = PR_FALSE;
795 double ratio = 1.0f;
796 NS_FOR_CSS_SIDES(side) {
797 PRUint32 hc1 = NS_SIDE_TO_HALF_CORNER(side, PR_FALSE, PR_TRUE);
798 PRUint32 hc2 = NS_SIDE_TO_HALF_CORNER(side, PR_TRUE, PR_TRUE);
799 nscoord length =
800 NS_SIDE_IS_VERTICAL(side) ? aBorderArea.height : aBorderArea.width;
801 nscoord sum = aRadii[hc1] + aRadii[hc2];
802 if (sum)
803 haveRadius = PR_TRUE;
805 // avoid floating point division in the normal case
806 if (length < sum)
807 ratio = NS_MIN(ratio, double(length)/sum);
809 if (ratio < 1.0) {
810 NS_FOR_CSS_HALF_CORNERS(corner) {
811 aRadii[corner] *= ratio;
815 return haveRadius;
818 /* static */ void
819 nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets)
821 NS_FOR_CSS_SIDES(side) {
822 nscoord offset = aOffsets.side(side);
823 PRUint32 hc1 = NS_SIDE_TO_HALF_CORNER(side, PR_FALSE, PR_FALSE);
824 PRUint32 hc2 = NS_SIDE_TO_HALF_CORNER(side, PR_TRUE, PR_FALSE);
825 aRadii[hc1] = NS_MAX(0, aRadii[hc1] - offset);
826 aRadii[hc2] = NS_MAX(0, aRadii[hc2] - offset);
830 /* static */ void
831 nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets)
833 NS_FOR_CSS_SIDES(side) {
834 nscoord offset = aOffsets.side(side);
835 PRUint32 hc1 = NS_SIDE_TO_HALF_CORNER(side, PR_FALSE, PR_FALSE);
836 PRUint32 hc2 = NS_SIDE_TO_HALF_CORNER(side, PR_TRUE, PR_FALSE);
837 if (aRadii[hc1] > 0)
838 aRadii[hc1] += offset;
839 if (aRadii[hc2] > 0)
840 aRadii[hc2] += offset;
844 /* virtual */ PRBool
845 nsIFrame::GetBorderRadii(nscoord aRadii[8]) const
847 if (IsThemed()) {
848 // When we're themed, the native theme code draws the border and
849 // background, and therefore it doesn't make sense to tell other
850 // code that's interested in border-radius that we have any radii.
852 // In an ideal world, we might have a way for the them to tell us an
853 // border radius, but since we don't, we're better off assuming
854 // zero.
855 NS_FOR_CSS_HALF_CORNERS(corner) {
856 aRadii[corner] = 0;
858 return PR_FALSE;
860 nsSize size = GetSize();
861 return ComputeBorderRadii(GetStyleBorder()->mBorderRadius, size, size,
862 GetSkipSides(), aRadii);
865 PRBool
866 nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const
868 if (!GetBorderRadii(aRadii))
869 return PR_FALSE;
870 InsetBorderRadii(aRadii, GetUsedBorder());
871 NS_FOR_CSS_HALF_CORNERS(corner) {
872 if (aRadii[corner])
873 return PR_TRUE;
875 return PR_FALSE;
878 PRBool
879 nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const
881 if (!GetBorderRadii(aRadii))
882 return PR_FALSE;
883 InsetBorderRadii(aRadii, GetUsedBorderAndPadding());
884 NS_FOR_CSS_HALF_CORNERS(corner) {
885 if (aRadii[corner])
886 return PR_TRUE;
888 return PR_FALSE;
891 nsStyleContext*
892 nsFrame::GetAdditionalStyleContext(PRInt32 aIndex) const
894 NS_PRECONDITION(aIndex >= 0, "invalid index number");
895 return nsnull;
898 void
899 nsFrame::SetAdditionalStyleContext(PRInt32 aIndex,
900 nsStyleContext* aStyleContext)
902 NS_PRECONDITION(aIndex >= 0, "invalid index number");
905 nscoord
906 nsFrame::GetBaseline() const
908 NS_ASSERTION(!NS_SUBTREE_DIRTY(this),
909 "frame must not be dirty");
910 // Default to the bottom margin edge, per CSS2.1's definition of the
911 // 'baseline' value of 'vertical-align'.
912 return mRect.height + GetUsedMargin().bottom;
915 // Child frame enumeration
917 nsIAtom*
918 nsFrame::GetAdditionalChildListName(PRInt32 aIndex) const
920 NS_PRECONDITION(aIndex >= 0, "invalid index number");
921 return nsnull;
924 nsFrameList
925 nsFrame::GetChildList(nsIAtom* aListName) const
927 return nsFrameList::EmptyList();
930 static nsIFrame*
931 GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
933 nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
934 if (capturingContent) {
935 nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
936 return activeFrame ? activeFrame : aFrame;
939 return aFrame;
942 PRInt16
943 nsFrame::DisplaySelection(nsPresContext* aPresContext, PRBool isOkToTurnOn)
945 PRInt16 selType = nsISelectionController::SELECTION_OFF;
947 nsCOMPtr<nsISelectionController> selCon;
948 nsresult result = GetSelectionController(aPresContext, getter_AddRefs(selCon));
949 if (NS_SUCCEEDED(result) && selCon) {
950 result = selCon->GetDisplaySelection(&selType);
951 if (NS_SUCCEEDED(result) && (selType != nsISelectionController::SELECTION_OFF)) {
952 // Check whether style allows selection.
953 PRBool selectable;
954 IsSelectable(&selectable, nsnull);
955 if (!selectable) {
956 selType = nsISelectionController::SELECTION_OFF;
957 isOkToTurnOn = PR_FALSE;
960 if (isOkToTurnOn && (selType == nsISelectionController::SELECTION_OFF)) {
961 selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
962 selType = nsISelectionController::SELECTION_ON;
965 return selType;
968 class nsDisplaySelectionOverlay : public nsDisplayItem {
969 public:
970 nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
971 nsFrame* aFrame, PRInt16 aSelectionValue)
972 : nsDisplayItem(aBuilder, aFrame), mSelectionValue(aSelectionValue) {
973 MOZ_COUNT_CTOR(nsDisplaySelectionOverlay);
975 #ifdef NS_BUILD_REFCNT_LOGGING
976 virtual ~nsDisplaySelectionOverlay() {
977 MOZ_COUNT_DTOR(nsDisplaySelectionOverlay);
979 #endif
981 virtual void Paint(nsDisplayListBuilder* aBuilder,
982 nsIRenderingContext* aCtx);
983 NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
984 private:
985 PRInt16 mSelectionValue;
988 void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder,
989 nsIRenderingContext* aCtx)
991 nscolor color = NS_RGB(255, 255, 255);
993 nsILookAndFeel::nsColorID colorID;
994 nsresult result;
995 if (mSelectionValue == nsISelectionController::SELECTION_ON) {
996 colorID = nsILookAndFeel::eColor_TextSelectBackground;
997 } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) {
998 colorID = nsILookAndFeel::eColor_TextSelectBackgroundAttention;
999 } else {
1000 colorID = nsILookAndFeel::eColor_TextSelectBackgroundDisabled;
1003 nsCOMPtr<nsILookAndFeel> look;
1004 look = do_GetService(kLookAndFeelCID, &result);
1005 if (NS_SUCCEEDED(result) && look)
1006 look->GetColor(colorID, color);
1008 gfxRGBA c(color);
1009 c.a = .5;
1011 gfxContext *ctx = aCtx->ThebesContext();
1012 ctx->SetColor(c);
1014 nsIntRect pxRect =
1015 mVisibleRect.ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel());
1016 ctx->NewPath();
1017 ctx->Rectangle(gfxRect(pxRect.x, pxRect.y, pxRect.width, pxRect.height), PR_TRUE);
1018 ctx->Fill();
1021 /********************************************************
1022 * Refreshes each content's frame
1023 *********************************************************/
1025 nsresult
1026 nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
1027 nsDisplayList* aList,
1028 PRUint16 aContentType)
1030 //check frame selection state
1031 if ((GetStateBits() & NS_FRAME_SELECTED_CONTENT) != NS_FRAME_SELECTED_CONTENT)
1032 return NS_OK;
1033 if (!IsVisibleForPainting(aBuilder))
1034 return NS_OK;
1036 nsPresContext* presContext = PresContext();
1037 nsIPresShell *shell = presContext->PresShell();
1038 if (!shell)
1039 return NS_OK;
1041 PRInt16 displaySelection = shell->GetSelectionFlags();
1042 if (!(displaySelection & aContentType))
1043 return NS_OK;
1045 const nsFrameSelection* frameSelection = GetConstFrameSelection();
1046 PRInt16 selectionValue = frameSelection->GetDisplaySelection();
1048 if (selectionValue <= nsISelectionController::SELECTION_HIDDEN)
1049 return NS_OK; // selection is hidden or off
1051 nsIContent *newContent = mContent->GetParent();
1053 //check to see if we are anonymous content
1054 PRInt32 offset = 0;
1055 if (newContent) {
1056 // XXXbz there has GOT to be a better way of determining this!
1057 offset = newContent->IndexOf(mContent);
1060 SelectionDetails *details;
1061 //look up to see what selection(s) are on this frame
1062 details = frameSelection->LookUpSelection(newContent, offset, 1, PR_FALSE);
1063 // XXX is the above really necessary? We don't actually DO anything
1064 // with the details other than test that they're non-null
1065 if (!details)
1066 return NS_OK;
1068 while (details) {
1069 SelectionDetails *next = details->mNext;
1070 delete details;
1071 details = next;
1074 return aList->AppendNewToTop(new (aBuilder)
1075 nsDisplaySelectionOverlay(aBuilder, this, selectionValue));
1078 nsresult
1079 nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
1080 const nsDisplayListSet& aLists)
1082 if (GetStyleOutline()->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE)
1083 return NS_OK;
1085 return aLists.Outlines()->AppendNewToTop(
1086 new (aBuilder) nsDisplayOutline(aBuilder, this));
1089 nsresult
1090 nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
1091 const nsDisplayListSet& aLists)
1093 if (!IsVisibleForPainting(aBuilder))
1094 return NS_OK;
1096 return DisplayOutlineUnconditional(aBuilder, aLists);
1099 nsresult
1100 nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
1101 const nsRect& aDirtyRect, nsDisplayList* aList)
1103 if (!IsVisibleForPainting(aBuilder))
1104 return NS_OK;
1106 return aList->AppendNewToTop(
1107 new (aBuilder) nsDisplayCaret(aBuilder, this, aBuilder->GetCaret()));
1110 nscolor
1111 nsIFrame::GetCaretColorAt(PRInt32 aOffset)
1113 // Use text color.
1114 return GetStyleColor()->mColor;
1117 PRBool
1118 nsIFrame::HasBorder() const
1120 // Border images contribute to the background of the content area
1121 // even if there's no border proper.
1122 return (GetUsedBorder() != nsMargin(0,0,0,0) ||
1123 GetStyleBorder()->IsBorderImageLoaded());
1126 nsresult
1127 nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
1128 const nsDisplayListSet& aLists,
1129 PRBool aForceBackground)
1131 // Here we don't try to detect background propagation. Frames that might
1132 // receive a propagated background should just set aForceBackground to
1133 // PR_TRUE.
1134 if (aBuilder->IsForEventDelivery() || aForceBackground ||
1135 !GetStyleBackground()->IsTransparent() || GetStyleDisplay()->mAppearance) {
1136 return aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1137 nsDisplayBackground(aBuilder, this));
1139 return NS_OK;
1142 nsresult
1143 nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
1144 const nsDisplayListSet& aLists,
1145 PRBool aForceBackground)
1147 // The visibility check belongs here since child elements have the
1148 // opportunity to override the visibility property and display even if
1149 // their parent is hidden.
1150 if (!IsVisibleForPainting(aBuilder))
1151 return NS_OK;
1153 PRBool hasBoxShadow = GetStyleBorder()->mBoxShadow != nsnull;
1154 if (hasBoxShadow) {
1155 nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1156 nsDisplayBoxShadowOuter(aBuilder, this));
1157 NS_ENSURE_SUCCESS(rv, rv);
1160 nsresult rv =
1161 DisplayBackgroundUnconditional(aBuilder, aLists, aForceBackground);
1162 NS_ENSURE_SUCCESS(rv, rv);
1164 if (hasBoxShadow) {
1165 rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1166 nsDisplayBoxShadowInner(aBuilder, this));
1167 NS_ENSURE_SUCCESS(rv, rv);
1170 if (HasBorder()) {
1171 rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1172 nsDisplayBorder(aBuilder, this));
1173 NS_ENSURE_SUCCESS(rv, rv);
1176 return DisplayOutlineUnconditional(aBuilder, aLists);
1179 PRBool
1180 nsIFrame::GetAbsPosClipRect(const nsStyleDisplay* aDisp, nsRect* aRect,
1181 const nsSize& aSize)
1183 NS_PRECONDITION(aRect, "Must have aRect out parameter");
1185 if (!aDisp->IsAbsolutelyPositioned() ||
1186 !(aDisp->mClipFlags & NS_STYLE_CLIP_RECT))
1187 return PR_FALSE;
1189 *aRect = aDisp->mClip;
1190 if (NS_STYLE_CLIP_RIGHT_AUTO & aDisp->mClipFlags) {
1191 aRect->width = aSize.width - aRect->x;
1193 if (NS_STYLE_CLIP_BOTTOM_AUTO & aDisp->mClipFlags) {
1194 aRect->height = aSize.height - aRect->y;
1196 return PR_TRUE;
1199 static PRBool ApplyAbsPosClipping(nsDisplayListBuilder* aBuilder,
1200 const nsStyleDisplay* aDisp, nsIFrame* aFrame,
1201 nsRect* aRect) {
1202 if (!aFrame->GetAbsPosClipRect(aDisp, aRect, aFrame->GetSize()))
1203 return PR_FALSE;
1205 *aRect += aBuilder->ToReferenceFrame(aFrame);
1206 return PR_TRUE;
1210 * Returns PR_TRUE if aFrame is overflow:hidden and we should interpret
1211 * that as -moz-hidden-unscrollable.
1213 static inline PRBool ApplyOverflowHiddenClipping(nsIFrame* aFrame,
1214 const nsStyleDisplay* aDisp)
1216 if (aDisp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN)
1217 return PR_FALSE;
1219 nsIAtom* type = aFrame->GetType();
1220 // REVIEW: these are the frame types that call IsTableClip and set up
1221 // clipping. Actually there were also table rows and the inner table frame
1222 // doing this, but 'overflow' isn't applicable to them according to
1223 // CSS 2.1 so I removed them. Also, we used to clip at tableOuterFrame
1224 // but we should actually clip at tableFrame (as per discussion with Hixie and
1225 // bz).
1226 return type == nsGkAtoms::tableFrame ||
1227 type == nsGkAtoms::tableCellFrame ||
1228 type == nsGkAtoms::bcTableCellFrame;
1231 static PRBool ApplyOverflowClipping(nsDisplayListBuilder* aBuilder,
1232 nsIFrame* aFrame,
1233 const nsStyleDisplay* aDisp, nsRect* aRect) {
1234 // REVIEW: from nsContainerFrame.cpp SyncFrameViewGeometryDependentProperties,
1235 // except that that function used the border-edge for
1236 // -moz-hidden-unscrollable which I don't think is correct... Also I've
1237 // changed -moz-hidden-unscrollable to apply to any kind of frame.
1239 // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table
1240 // frames, and any non-visible value for blocks in a paginated context).
1241 // Other overflow clipping is applied by nsHTML/XULScrollFrame.
1242 if (!ApplyOverflowHiddenClipping(aFrame, aDisp) &&
1243 !nsFrame::ApplyPaginatedOverflowClipping(aFrame)) {
1244 PRBool clip = aDisp->mOverflowX == NS_STYLE_OVERFLOW_CLIP;
1245 if (!clip)
1246 return PR_FALSE;
1247 // We allow -moz-hidden-unscrollable to apply to any kind of frame. This
1248 // is required by comboboxes which make their display text (an inline frame)
1249 // have clipping.
1252 *aRect = aFrame->GetPaddingRect() - aFrame->GetPosition() +
1253 aBuilder->ToReferenceFrame(aFrame);
1254 return PR_TRUE;
1257 class nsOverflowClipWrapper : public nsDisplayWrapper
1259 public:
1261 * Create a wrapper to apply overflow clipping for aContainer.
1262 * @param aClipBorderBackground set to PR_TRUE to clip the BorderBackground()
1263 * list, otherwise it will not be clipped
1264 * @param aClipAll set to PR_TRUE to clip all descendants, even those for
1265 * which we aren't the containing block
1267 nsOverflowClipWrapper(nsIFrame* aContainer, const nsRect& aRect,
1268 const nscoord aRadii[8],
1269 PRBool aClipBorderBackground, PRBool aClipAll)
1270 : mContainer(aContainer), mRect(aRect),
1271 mClipBorderBackground(aClipBorderBackground), mClipAll(aClipAll),
1272 mHaveRadius(PR_FALSE)
1274 memcpy(mRadii, aRadii, sizeof(mRadii));
1275 NS_FOR_CSS_HALF_CORNERS(corner) {
1276 if (aRadii[corner] > 0) {
1277 mHaveRadius = PR_TRUE;
1278 break;
1282 virtual PRBool WrapBorderBackground() { return mClipBorderBackground; }
1283 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
1284 nsIFrame* aFrame, nsDisplayList* aList) {
1285 // We are not a stacking context root. There is no valid underlying
1286 // frame for the whole list. These items are all in-flow descendants so
1287 // we can safely just clip them.
1288 if (mHaveRadius) {
1289 return new (aBuilder) nsDisplayClipRoundedRect(aBuilder, nsnull, aList,
1290 mRect, mRadii);
1292 return new (aBuilder) nsDisplayClip(aBuilder, nsnull, aList, mRect);
1294 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
1295 nsDisplayItem* aItem) {
1296 nsIFrame* f = aItem->GetUnderlyingFrame();
1297 if (mClipAll ||
1298 nsLayoutUtils::IsProperAncestorFrame(mContainer, f, nsnull)) {
1299 if (mHaveRadius) {
1300 return new (aBuilder) nsDisplayClipRoundedRect(aBuilder, f, aItem,
1301 mRect, mRadii);
1303 return new (aBuilder) nsDisplayClip(aBuilder, f, aItem, mRect);
1305 return aItem;
1307 protected:
1308 nsIFrame* mContainer;
1309 nsRect mRect;
1310 nscoord mRadii[8];
1311 PRPackedBool mClipBorderBackground;
1312 PRPackedBool mClipAll;
1313 PRPackedBool mHaveRadius;
1316 class nsAbsPosClipWrapper : public nsDisplayWrapper
1318 public:
1319 nsAbsPosClipWrapper(const nsRect& aRect)
1320 : mRect(aRect) {}
1321 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
1322 nsIFrame* aFrame, nsDisplayList* aList) {
1323 // We are not a stacking context root. There is no valid underlying
1324 // frame for the whole list.
1325 return new (aBuilder) nsDisplayClip(aBuilder, nsnull, aList, mRect);
1327 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
1328 nsDisplayItem* aItem) {
1329 return new (aBuilder) nsDisplayClip(aBuilder, aItem->GetUnderlyingFrame(),
1330 aItem, mRect);
1332 protected:
1333 nsRect mRect;
1336 nsresult
1337 nsIFrame::OverflowClip(nsDisplayListBuilder* aBuilder,
1338 const nsDisplayListSet& aFromSet,
1339 const nsDisplayListSet& aToSet,
1340 const nsRect& aClipRect,
1341 const nscoord aClipRadii[8],
1342 PRBool aClipBorderBackground,
1343 PRBool aClipAll)
1345 nsOverflowClipWrapper wrapper(this, aClipRect, aClipRadii,
1346 aClipBorderBackground, aClipAll);
1347 return wrapper.WrapLists(aBuilder, this, aFromSet, aToSet);
1350 static nsresult
1351 BuildDisplayListWithOverflowClip(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
1352 const nsRect& aDirtyRect, const nsDisplayListSet& aSet,
1353 const nsRect& aClipRect, const nscoord aClipRadii[8])
1355 nsDisplayListCollection set;
1356 nsresult rv = aFrame->BuildDisplayList(aBuilder, aDirtyRect, set);
1357 NS_ENSURE_SUCCESS(rv, rv);
1358 rv = aBuilder->DisplayCaret(aFrame, aDirtyRect, aSet.Content());
1359 NS_ENSURE_SUCCESS(rv, rv);
1361 return aFrame->OverflowClip(aBuilder, set, aSet, aClipRect, aClipRadii);
1364 #ifdef NS_DEBUG
1365 static void PaintDebugBorder(nsIFrame* aFrame, nsIRenderingContext* aCtx,
1366 const nsRect& aDirtyRect, nsPoint aPt) {
1367 nsRect r(aPt, aFrame->GetSize());
1368 if (aFrame->HasView()) {
1369 aCtx->SetColor(NS_RGB(0,0,255));
1370 } else {
1371 aCtx->SetColor(NS_RGB(255,0,0));
1373 aCtx->DrawRect(r);
1376 static void PaintEventTargetBorder(nsIFrame* aFrame, nsIRenderingContext* aCtx,
1377 const nsRect& aDirtyRect, nsPoint aPt) {
1378 nsRect r(aPt, aFrame->GetSize());
1379 aCtx->SetColor(NS_RGB(128,0,128));
1380 aCtx->DrawRect(r);
1383 static void
1384 DisplayDebugBorders(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
1385 const nsDisplayListSet& aLists) {
1386 // Draw a border around the child
1387 // REVIEW: From nsContainerFrame::PaintChild
1388 if (nsFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) {
1389 aLists.Outlines()->AppendNewToTop(new (aBuilder)
1390 nsDisplayGeneric(aBuilder, aFrame, PaintDebugBorder, "DebugBorder",
1391 nsDisplayItem::TYPE_DEBUG_BORDER));
1393 // Draw a border around the current event target
1394 if (nsFrame::GetShowEventTargetFrameBorder() &&
1395 aFrame->PresContext()->PresShell()->GetDrawEventTargetFrame() == aFrame) {
1396 aLists.Outlines()->AppendNewToTop(new (aBuilder)
1397 nsDisplayGeneric(aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder",
1398 nsDisplayItem::TYPE_EVENT_TARGET_BORDER));
1401 #endif
1403 nsresult
1404 nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
1405 const nsRect& aDirtyRect,
1406 nsDisplayList* aList) {
1407 if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
1408 return NS_OK;
1410 // Replaced elements have their visibility handled here, because
1411 // they're visually atomic
1412 if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder))
1413 return NS_OK;
1415 nsRect absPosClip;
1416 const nsStyleDisplay* disp = GetStyleDisplay();
1417 // We can stop right away if this is a zero-opacity stacking context and
1418 // we're not checking for event handling.
1419 if (disp->mOpacity == 0.0 && !aBuilder->IsForEventDelivery())
1420 return NS_OK;
1422 PRBool applyAbsPosClipping =
1423 ApplyAbsPosClipping(aBuilder, disp, this, &absPosClip);
1424 nsRect dirtyRect = aDirtyRect;
1426 PRBool inTransform = aBuilder->IsInTransform();
1427 /* If we're being transformed, we need to invert the matrix transform so that we don't
1428 * grab points in the wrong coordinate system!
1430 if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
1431 disp->HasTransform()) {
1432 dirtyRect = nsDisplayTransform::UntransformRect(dirtyRect, this, nsPoint(0, 0));
1433 inTransform = PR_TRUE;
1436 if (applyAbsPosClipping) {
1437 dirtyRect.IntersectRect(dirtyRect,
1438 absPosClip - aBuilder->ToReferenceFrame(this));
1441 #ifdef MOZ_SVG
1442 PRBool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
1443 if (usingSVGEffects) {
1444 dirtyRect =
1445 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
1447 #endif
1449 nsDisplayListCollection set;
1450 nsresult rv;
1452 nsDisplayListBuilder::AutoIsRootSetter rootSetter(aBuilder, PR_TRUE);
1453 nsDisplayListBuilder::AutoInTransformSetter
1454 inTransformSetter(aBuilder, inTransform);
1455 rv = BuildDisplayList(aBuilder, dirtyRect, set);
1457 NS_ENSURE_SUCCESS(rv, rv);
1459 if (aBuilder->IsBackgroundOnly()) {
1460 set.BlockBorderBackgrounds()->DeleteAll();
1461 set.Floats()->DeleteAll();
1462 set.Content()->DeleteAll();
1463 set.PositionedDescendants()->DeleteAll();
1464 set.Outlines()->DeleteAll();
1467 // This z-order sort also sorts secondarily by content order. We need to do
1468 // this so that boxes produced by the same element are placed together
1469 // in the sort. Consider a position:relative inline element that breaks
1470 // across lines and has absolutely positioned children; all the abs-pos
1471 // children should be z-ordered after all the boxes for the position:relative
1472 // element itself.
1473 set.PositionedDescendants()->SortByZOrder(aBuilder, GetContent());
1475 nsRect overflowClip;
1476 if (ApplyOverflowClipping(aBuilder, this, disp, &overflowClip)) {
1477 nscoord radii[8];
1478 this->GetPaddingBoxBorderRadii(radii);
1479 nsOverflowClipWrapper wrapper(this, overflowClip, radii,
1480 PR_FALSE, PR_FALSE);
1481 rv = wrapper.WrapListsInPlace(aBuilder, this, set);
1482 NS_ENSURE_SUCCESS(rv, rv);
1484 // We didn't use overflowClip to restrict the dirty rect, since some of the
1485 // descendants may not be clipped by it. Even if we end up with unnecessary
1486 // display items, they'll be pruned during ComputeVisibility.
1488 nsDisplayList resultList;
1489 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
1490 // 1,2: backgrounds and borders
1491 resultList.AppendToTop(set.BorderBackground());
1492 // 3: negative z-index children.
1493 for (;;) {
1494 nsDisplayItem* item = set.PositionedDescendants()->GetBottom();
1495 if (item) {
1496 nsIFrame* f = item->GetUnderlyingFrame();
1497 NS_ASSERTION(f, "After sorting, every item in the list should have an underlying frame");
1498 if (nsLayoutUtils::GetZIndex(f) < 0) {
1499 set.PositionedDescendants()->RemoveBottom();
1500 resultList.AppendToTop(item);
1501 continue;
1504 break;
1506 // 4: block backgrounds
1507 resultList.AppendToTop(set.BlockBorderBackgrounds());
1508 // 5: floats
1509 resultList.AppendToTop(set.Floats());
1510 // 7: general content
1511 resultList.AppendToTop(set.Content());
1512 // 7.5: outlines, in content tree order. We need to sort by content order
1513 // because an element with outline that breaks and has children with outline
1514 // might have placed child outline items between its own outline items.
1515 // The element's outline items need to all come before any child outline
1516 // items.
1517 set.Outlines()->SortByContentOrder(aBuilder, GetContent());
1518 #ifdef NS_DEBUG
1519 DisplayDebugBorders(aBuilder, this, set);
1520 #endif
1521 resultList.AppendToTop(set.Outlines());
1522 // 8, 9: non-negative z-index children
1523 resultList.AppendToTop(set.PositionedDescendants());
1525 if (applyAbsPosClipping) {
1526 nsAbsPosClipWrapper wrapper(absPosClip);
1527 nsDisplayItem* item = wrapper.WrapList(aBuilder, this, &resultList);
1528 if (!item)
1529 return NS_ERROR_OUT_OF_MEMORY;
1530 // resultList was emptied
1531 resultList.AppendToTop(item);
1534 #ifdef MOZ_SVG
1535 /* If there are any SVG effects, wrap up the list in an effects list. */
1536 if (usingSVGEffects) {
1537 /* List now emptied, so add the new list to the top. */
1538 rv = resultList.AppendNewToTop(
1539 new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList));
1540 if (NS_FAILED(rv))
1541 return rv;
1542 } else
1543 #endif
1545 /* If there is any opacity, wrap it up in an opacity list.
1546 * If there's nothing in the list, don't add anything.
1548 if (disp->mOpacity < 1.0f && !resultList.IsEmpty()) {
1549 rv = resultList.AppendNewToTop(
1550 new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList));
1551 if (NS_FAILED(rv))
1552 return rv;
1555 /* If we're going to apply a transformation, wrap everything in an
1556 * nsDisplayTransform. If there's nothing in the list, don't add anything.
1558 if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
1559 disp->HasTransform() && !resultList.IsEmpty()) {
1560 rv = resultList.AppendNewToTop(
1561 new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList));
1562 if (NS_FAILED(rv))
1563 return rv;
1566 aList->AppendToTop(&resultList);
1567 return rv;
1570 nsresult
1571 nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
1572 nsIFrame* aChild,
1573 const nsRect& aDirtyRect,
1574 const nsDisplayListSet& aLists,
1575 PRUint32 aFlags) {
1576 // If painting is restricted to just the background of the top level frame,
1577 // then we have nothing to do here.
1578 if (aBuilder->IsBackgroundOnly())
1579 return NS_OK;
1581 if (aChild->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
1582 return NS_OK;
1584 const nsStyleDisplay* disp = aChild->GetStyleDisplay();
1585 // PR_TRUE if this is a real or pseudo stacking context
1586 PRBool pseudoStackingContext =
1587 (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0;
1588 // XXX we REALLY need a "are you an inline-block sort of thing?" here!!!
1589 if ((aFlags & DISPLAY_CHILD_INLINE) &&
1590 (disp->mDisplay != NS_STYLE_DISPLAY_INLINE ||
1591 aChild->IsContainingBlock() ||
1592 (aChild->IsFrameOfType(eReplaced)))) {
1593 // child is a non-inline frame in an inline context, i.e.,
1594 // it acts like inline-block or inline-table. Therefore it is a
1595 // pseudo-stacking-context.
1596 pseudoStackingContext = PR_TRUE;
1599 // dirty rect in child-relative coordinates
1600 nsRect dirty = aDirtyRect - aChild->GetOffsetTo(this);
1602 nsIAtom* childType = aChild->GetType();
1603 if (childType == nsGkAtoms::placeholderFrame) {
1604 nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(aChild);
1605 aChild = placeholder->GetOutOfFlowFrame();
1606 NS_ASSERTION(aChild, "No out of flow frame?");
1607 if (!aChild || nsLayoutUtils::IsPopup(aChild))
1608 return NS_OK;
1609 // update for the new child
1610 disp = aChild->GetStyleDisplay();
1611 // Make sure that any attempt to use childType below is disappointed. We
1612 // could call GetType again but since we don't currently need it, let's
1613 // avoid the virtual call.
1614 childType = nsnull;
1615 // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE
1616 if (aChild->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
1617 return NS_OK;
1618 nsRect* savedDirty = static_cast<nsRect*>
1619 (aChild->Properties().Get(nsDisplayListBuilder::OutOfFlowDirtyRectProperty()));
1620 if (savedDirty) {
1621 dirty = *savedDirty;
1622 } else {
1623 // The out-of-flow frame did not intersect the dirty area. We may still
1624 // need to traverse into it, since it may contain placeholders we need
1625 // to enter to reach other out-of-flow frames that are visible.
1626 dirty.Empty();
1628 pseudoStackingContext = PR_TRUE;
1629 } else if (aBuilder->GetSelectedFramesOnly() &&
1630 aChild->IsLeaf() &&
1631 !(aChild->GetStateBits() & NS_FRAME_SELECTED_CONTENT)) {
1632 return NS_OK;
1635 if (aBuilder->GetIncludeAllOutOfFlows() &&
1636 (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
1637 dirty = aChild->GetVisualOverflowRect();
1638 } else if (!(aChild->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
1639 // No need to descend into aChild to catch placeholders for visible
1640 // positioned stuff. So see if we can short-circuit frame traversal here.
1642 // We can stop if aChild's frame subtree's intersection with the
1643 // dirty area is empty.
1644 // If the child is a scrollframe that we want to ignore, then we need
1645 // to descend into it because its scrolled child may intersect the dirty
1646 // area even if the scrollframe itself doesn't.
1647 if (aChild != aBuilder->GetIgnoreScrollFrame()) {
1648 nsRect childDirty;
1649 if (!childDirty.IntersectRect(dirty, aChild->GetVisualOverflowRect()))
1650 return NS_OK;
1651 // Usually we could set dirty to childDirty now but there's no
1652 // benefit, and it can be confusing. It can especially confuse
1653 // situations where we're going to ignore a scrollframe's clipping;
1654 // we wouldn't want to clip the dirty area to the scrollframe's
1655 // bounds in that case.
1659 // XXX need to have inline-block and inline-table set pseudoStackingContext
1661 const nsStyleDisplay* ourDisp = GetStyleDisplay();
1662 // REVIEW: Taken from nsBoxFrame::Paint
1663 // Don't paint our children if the theme object is a leaf.
1664 if (IsThemed(ourDisp) &&
1665 !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance))
1666 return NS_OK;
1668 // Child is composited if it's transformed, partially transparent, or has
1669 // SVG effects.
1670 PRBool isComposited = disp->mOpacity != 1.0f || aChild->IsTransformed()
1671 #ifdef MOZ_SVG
1672 || nsSVGIntegrationUtils::UsingEffectsForFrame(aChild)
1673 #endif
1675 PRBool isPositioned = disp->IsPositioned();
1676 if (isComposited || isPositioned || disp->IsFloating() ||
1677 (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
1678 // If you change this, also change IsPseudoStackingContextFromStyle()
1679 pseudoStackingContext = PR_TRUE;
1682 nsRect overflowClip;
1683 nscoord overflowClipRadii[8];
1684 PRBool applyOverflowClip =
1685 ApplyOverflowClipping(aBuilder, aChild, disp, &overflowClip);
1686 if (applyOverflowClip) {
1687 aChild->GetPaddingBoxBorderRadii(overflowClipRadii);
1689 // Don't use overflowClip to restrict the dirty rect, since some of the
1690 // descendants may not be clipped by it. Even if we end up with unnecessary
1691 // display items, they'll be pruned during ComputeVisibility. Note that
1692 // this overflow-clipping here only applies to overflow:-moz-hidden-unscrollable;
1693 // overflow:hidden etc creates an nsHTML/XULScrollFrame which does its own
1694 // clipping.
1696 nsDisplayListBuilder::AutoIsRootSetter rootSetter(aBuilder, pseudoStackingContext);
1697 nsresult rv;
1698 if (!pseudoStackingContext) {
1699 // THIS IS THE COMMON CASE.
1700 // Not a pseudo or real stacking context. Do the simple thing and
1701 // return early.
1702 if (applyOverflowClip) {
1703 rv = BuildDisplayListWithOverflowClip(aBuilder, aChild, dirty, aLists,
1704 overflowClip, overflowClipRadii);
1705 } else {
1706 rv = aChild->BuildDisplayList(aBuilder, dirty, aLists);
1707 if (NS_SUCCEEDED(rv)) {
1708 rv = aBuilder->DisplayCaret(aChild, dirty, aLists.Content());
1711 #ifdef NS_DEBUG
1712 DisplayDebugBorders(aBuilder, aChild, aLists);
1713 #endif
1714 return rv;
1717 nsDisplayList list;
1718 nsDisplayList extraPositionedDescendants;
1719 const nsStylePosition* pos = aChild->GetStylePosition();
1720 if ((isPositioned && pos->mZIndex.GetUnit() == eStyleUnit_Integer) ||
1721 isComposited || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
1722 // True stacking context
1723 rv = aChild->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
1724 if (NS_SUCCEEDED(rv)) {
1725 rv = aBuilder->DisplayCaret(aChild, dirty, &list);
1727 } else {
1728 nsRect clipRect;
1729 PRBool applyAbsPosClipping =
1730 ApplyAbsPosClipping(aBuilder, disp, aChild, &clipRect);
1731 // A pseudo-stacking context (e.g., a positioned element with z-index auto).
1732 // we allow positioned descendants of this element to escape to our
1733 // container's positioned descendant list, because they might be
1734 // z-index:non-auto
1735 nsDisplayListCollection pseudoStack;
1736 nsRect clippedDirtyRect = dirty;
1737 if (applyAbsPosClipping) {
1738 // clipRect is in builder-reference-frame coordinates,
1739 // dirty/clippedDirtyRect are in aChild coordinates
1740 clippedDirtyRect.IntersectRect(clippedDirtyRect,
1741 clipRect - aBuilder->ToReferenceFrame(aChild));
1744 if (applyOverflowClip) {
1745 rv = BuildDisplayListWithOverflowClip(aBuilder, aChild, clippedDirtyRect,
1746 pseudoStack, overflowClip,
1747 overflowClipRadii);
1748 } else {
1749 rv = aChild->BuildDisplayList(aBuilder, clippedDirtyRect, pseudoStack);
1750 if (NS_SUCCEEDED(rv)) {
1751 rv = aBuilder->DisplayCaret(aChild, dirty, pseudoStack.Content());
1755 if (NS_SUCCEEDED(rv)) {
1756 if (isPositioned && applyAbsPosClipping) {
1757 nsAbsPosClipWrapper wrapper(clipRect);
1758 rv = wrapper.WrapListsInPlace(aBuilder, aChild, pseudoStack);
1761 list.AppendToTop(pseudoStack.BorderBackground());
1762 list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
1763 list.AppendToTop(pseudoStack.Floats());
1764 list.AppendToTop(pseudoStack.Content());
1765 list.AppendToTop(pseudoStack.Outlines());
1766 extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
1767 #ifdef NS_DEBUG
1768 DisplayDebugBorders(aBuilder, aChild, aLists);
1769 #endif
1771 NS_ENSURE_SUCCESS(rv, rv);
1773 if (isPositioned || isComposited ||
1774 (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
1775 // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
1776 // go in this level.
1777 rv = aLists.PositionedDescendants()->AppendNewToTop(new (aBuilder)
1778 nsDisplayWrapList(aBuilder, aChild, &list));
1779 NS_ENSURE_SUCCESS(rv, rv);
1780 } else if (disp->IsFloating()) {
1781 rv = aLists.Floats()->AppendNewToTop(new (aBuilder)
1782 nsDisplayWrapList(aBuilder, aChild, &list));
1783 NS_ENSURE_SUCCESS(rv, rv);
1784 } else {
1785 aLists.Content()->AppendToTop(&list);
1787 // We delay placing the positioned descendants of positioned frames to here,
1788 // because in the absence of z-index this is the correct order for them.
1789 // This doesn't affect correctness because the positioned descendants list
1790 // is sorted by z-order and content in BuildDisplayListForStackingContext,
1791 // but it means that sort routine needs to do less work.
1792 aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
1793 return NS_OK;
1796 void
1797 nsIFrame::WrapReplacedContentForBorderRadius(nsDisplayListBuilder* aBuilder,
1798 nsDisplayList* aFromList,
1799 const nsDisplayListSet& aToLists)
1801 nscoord radii[8];
1802 if (GetContentBoxBorderRadii(radii)) {
1803 // If we have a border-radius, we have to clip our content to that
1804 // radius.
1805 nsDisplayListCollection set;
1806 set.Content()->AppendToTop(aFromList);
1807 nsRect clipRect = GetContentRect() - GetPosition() +
1808 aBuilder->ToReferenceFrame(this);
1809 OverflowClip(aBuilder, set, aToLists, clipRect, radii, PR_FALSE, PR_TRUE);
1811 return;
1814 aToLists.Content()->AppendToTop(aFromList);
1817 NS_IMETHODIMP
1818 nsFrame::GetContentForEvent(nsPresContext* aPresContext,
1819 nsEvent* aEvent,
1820 nsIContent** aContent)
1822 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
1823 *aContent = f->GetContent();
1824 NS_IF_ADDREF(*aContent);
1825 return NS_OK;
1828 void
1829 nsFrame::FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent)
1831 nsIContent* target = aContent ? aContent : mContent;
1833 if (target) {
1834 nsRefPtr<nsPLDOMEvent> event =
1835 new nsPLDOMEvent(target, aDOMEventName, PR_TRUE, PR_FALSE);
1836 if (!event || NS_FAILED(event->PostDOMEvent()))
1837 NS_WARNING("Failed to dispatch nsPLDOMEvent");
1841 NS_IMETHODIMP
1842 nsFrame::HandleEvent(nsPresContext* aPresContext,
1843 nsGUIEvent* aEvent,
1844 nsEventStatus* aEventStatus)
1847 if (aEvent->message == NS_MOUSE_MOVE) {
1848 return HandleDrag(aPresContext, aEvent, aEventStatus);
1851 if (aEvent->eventStructType == NS_MOUSE_EVENT &&
1852 static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eLeftButton) {
1853 if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
1854 HandlePress(aPresContext, aEvent, aEventStatus);
1855 } else if (aEvent->message == NS_MOUSE_BUTTON_UP) {
1856 HandleRelease(aPresContext, aEvent, aEventStatus);
1859 return NS_OK;
1862 NS_IMETHODIMP
1863 nsFrame::GetDataForTableSelection(const nsFrameSelection *aFrameSelection,
1864 nsIPresShell *aPresShell, nsMouseEvent *aMouseEvent,
1865 nsIContent **aParentContent, PRInt32 *aContentOffset, PRInt32 *aTarget)
1867 if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget)
1868 return NS_ERROR_NULL_POINTER;
1870 *aParentContent = nsnull;
1871 *aContentOffset = 0;
1872 *aTarget = 0;
1874 PRInt16 displaySelection = aPresShell->GetSelectionFlags();
1876 PRBool selectingTableCells = aFrameSelection->GetTableCellSelection();
1878 // DISPLAY_ALL means we're in an editor.
1879 // If already in cell selection mode,
1880 // continue selecting with mouse drag or end on mouse up,
1881 // or when using shift key to extend block of cells
1882 // (Mouse down does normal selection unless Ctrl/Cmd is pressed)
1883 PRBool doTableSelection =
1884 displaySelection == nsISelectionDisplay::DISPLAY_ALL && selectingTableCells &&
1885 (aMouseEvent->message == NS_MOUSE_MOVE ||
1886 (aMouseEvent->message == NS_MOUSE_BUTTON_UP &&
1887 aMouseEvent->button == nsMouseEvent::eLeftButton) ||
1888 aMouseEvent->isShift);
1890 if (!doTableSelection)
1892 // In Browser, special 'table selection' key must be pressed for table selection
1893 // or when just Shift is pressed and we're already in table/cell selection mode
1894 #ifdef XP_MACOSX
1895 doTableSelection = aMouseEvent->isMeta || (aMouseEvent->isShift && selectingTableCells);
1896 #else
1897 doTableSelection = aMouseEvent->isControl || (aMouseEvent->isShift && selectingTableCells);
1898 #endif
1900 if (!doTableSelection)
1901 return NS_OK;
1903 // Get the cell frame or table frame (or parent) of the current content node
1904 nsIFrame *frame = this;
1905 PRBool foundCell = PR_FALSE;
1906 PRBool foundTable = PR_FALSE;
1908 // Get the limiting node to stop parent frame search
1909 nsIContent* limiter = aFrameSelection->GetLimiter();
1911 // If our content node is an ancestor of the limiting node,
1912 // we should stop the search right now.
1913 if (limiter && nsContentUtils::ContentIsDescendantOf(limiter, GetContent()))
1914 return NS_OK;
1916 //We don't initiate row/col selection from here now,
1917 // but we may in future
1918 //PRBool selectColumn = PR_FALSE;
1919 //PRBool selectRow = PR_FALSE;
1921 while (frame)
1923 // Check for a table cell by querying to a known CellFrame interface
1924 nsITableCellLayout *cellElement = do_QueryFrame(frame);
1925 if (cellElement)
1927 foundCell = PR_TRUE;
1928 //TODO: If we want to use proximity to top or left border
1929 // for row and column selection, this is the place to do it
1930 break;
1932 else
1934 // If not a cell, check for table
1935 // This will happen when starting frame is the table or child of a table,
1936 // such as a row (we were inbetween cells or in table border)
1937 nsITableLayout *tableElement = do_QueryFrame(frame);
1938 if (tableElement)
1940 foundTable = PR_TRUE;
1941 //TODO: How can we select row when along left table edge
1942 // or select column when along top edge?
1943 break;
1944 } else {
1945 frame = frame->GetParent();
1946 // Stop if we have hit the selection's limiting content node
1947 if (frame && frame->GetContent() == limiter)
1948 break;
1952 // We aren't in a cell or table
1953 if (!foundCell && !foundTable) return NS_OK;
1955 nsIContent* tableOrCellContent = frame->GetContent();
1956 if (!tableOrCellContent) return NS_ERROR_FAILURE;
1958 nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
1959 if (!parentContent) return NS_ERROR_FAILURE;
1961 PRInt32 offset = parentContent->IndexOf(tableOrCellContent);
1962 // Not likely?
1963 if (offset < 0) return NS_ERROR_FAILURE;
1965 // Everything is OK -- set the return values
1966 *aParentContent = parentContent;
1967 NS_ADDREF(*aParentContent);
1969 *aContentOffset = offset;
1971 #if 0
1972 if (selectRow)
1973 *aTarget = nsISelectionPrivate::TABLESELECTION_ROW;
1974 else if (selectColumn)
1975 *aTarget = nsISelectionPrivate::TABLESELECTION_COLUMN;
1976 else
1977 #endif
1978 if (foundCell)
1979 *aTarget = nsISelectionPrivate::TABLESELECTION_CELL;
1980 else if (foundTable)
1981 *aTarget = nsISelectionPrivate::TABLESELECTION_TABLE;
1983 return NS_OK;
1986 NS_IMETHODIMP
1987 nsFrame::IsSelectable(PRBool* aSelectable, PRUint8* aSelectStyle) const
1989 if (!aSelectable) //it's ok if aSelectStyle is null
1990 return NS_ERROR_NULL_POINTER;
1992 // Like 'visibility', we must check all the parents: if a parent
1993 // is not selectable, none of its children is selectable.
1995 // The -moz-all value acts similarly: if a frame has 'user-select:-moz-all',
1996 // all its children are selectable, even those with 'user-select:none'.
1998 // As a result, if 'none' and '-moz-all' are not present in the frame hierarchy,
1999 // aSelectStyle returns the first style that is not AUTO. If these values
2000 // are present in the frame hierarchy, aSelectStyle returns the style of the
2001 // topmost parent that has either 'none' or '-moz-all'.
2003 // For instance, if the frame hierarchy is:
2004 // AUTO -> _MOZ_ALL -> NONE -> TEXT, the returned value is _MOZ_ALL
2005 // TEXT -> NONE -> AUTO -> _MOZ_ALL, the returned value is NONE
2006 // _MOZ_ALL -> TEXT -> AUTO -> AUTO, the returned value is _MOZ_ALL
2007 // AUTO -> CELL -> TEXT -> AUTO, the returned value is TEXT
2009 PRUint8 selectStyle = NS_STYLE_USER_SELECT_AUTO;
2010 nsIFrame* frame = (nsIFrame*)this;
2012 while (frame) {
2013 const nsStyleUIReset* userinterface = frame->GetStyleUIReset();
2014 switch (userinterface->mUserSelect) {
2015 case NS_STYLE_USER_SELECT_ALL:
2016 case NS_STYLE_USER_SELECT_NONE:
2017 case NS_STYLE_USER_SELECT_MOZ_ALL:
2018 // override the previous values
2019 selectStyle = userinterface->mUserSelect;
2020 break;
2021 default:
2022 // otherwise return the first value which is not 'auto'
2023 if (selectStyle == NS_STYLE_USER_SELECT_AUTO) {
2024 selectStyle = userinterface->mUserSelect;
2026 break;
2028 frame = frame->GetParent();
2031 // convert internal values to standard values
2032 if (selectStyle == NS_STYLE_USER_SELECT_AUTO)
2033 selectStyle = NS_STYLE_USER_SELECT_TEXT;
2034 else
2035 if (selectStyle == NS_STYLE_USER_SELECT_MOZ_ALL)
2036 selectStyle = NS_STYLE_USER_SELECT_ALL;
2037 else
2038 if (selectStyle == NS_STYLE_USER_SELECT_MOZ_NONE)
2039 selectStyle = NS_STYLE_USER_SELECT_NONE;
2041 // return stuff
2042 if (aSelectable)
2043 *aSelectable = (selectStyle != NS_STYLE_USER_SELECT_NONE);
2044 if (aSelectStyle)
2045 *aSelectStyle = selectStyle;
2046 if (mState & NS_FRAME_GENERATED_CONTENT)
2047 *aSelectable = PR_FALSE;
2048 return NS_OK;
2052 * Handles the Mouse Press Event for the frame
2054 NS_IMETHODIMP
2055 nsFrame::HandlePress(nsPresContext* aPresContext,
2056 nsGUIEvent* aEvent,
2057 nsEventStatus* aEventStatus)
2059 NS_ENSURE_ARG_POINTER(aEventStatus);
2060 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
2061 return NS_OK;
2064 //We often get out of sync state issues with mousedown events that
2065 //get interrupted by alerts/dialogs.
2066 //Check with the ESM to see if we should process this one
2067 PRBool eventOK;
2068 aPresContext->EventStateManager()->EventStatusOK(aEvent, &eventOK);
2069 if (!eventOK)
2070 return NS_OK;
2072 nsresult rv;
2073 nsIPresShell *shell = aPresContext->GetPresShell();
2074 if (!shell)
2075 return NS_ERROR_FAILURE;
2077 // if we are in Navigator and the click is in a draggable node, we don't want
2078 // to start selection because we don't want to interfere with a potential
2079 // drag of said node and steal all its glory.
2080 PRInt16 isEditor = shell->GetSelectionFlags();
2081 //weaaak. only the editor can display frame selection not just text and images
2082 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
2084 nsInputEvent* keyEvent = (nsInputEvent*)aEvent;
2085 if (!keyEvent->isAlt) {
2087 for (nsIContent* content = mContent; content;
2088 content = content->GetParent()) {
2089 if (nsContentUtils::ContentIsDraggable(content) &&
2090 !content->IsEditable()) {
2091 // coordinate stuff is the fix for bug #55921
2092 if ((mRect - GetPosition()).Contains(
2093 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this)))
2094 return NS_OK;
2099 // check whether style allows selection
2100 // if not, don't tell selection the mouse event even occurred.
2101 PRBool selectable;
2102 PRUint8 selectStyle;
2103 rv = IsSelectable(&selectable, &selectStyle);
2104 if (NS_FAILED(rv)) return rv;
2106 // check for select: none
2107 if (!selectable)
2108 return NS_OK;
2110 // When implementing NS_STYLE_USER_SELECT_ELEMENT, NS_STYLE_USER_SELECT_ELEMENTS and
2111 // NS_STYLE_USER_SELECT_TOGGLE, need to change this logic
2112 PRBool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT);
2114 // If the mouse is dragged outside the nearest enclosing scrollable area
2115 // while making a selection, the area will be scrolled. To do this, capture
2116 // the mouse on the nearest scrollable frame. If there isn't a scrollable
2117 // frame, or something else is already capturing the mouse, there's no
2118 // reason to capture.
2119 if (!nsIPresShell::GetCapturingContent()) {
2120 nsIFrame* checkFrame = this;
2121 nsIScrollableFrame *scrollFrame = nsnull;
2122 while (checkFrame) {
2123 scrollFrame = do_QueryFrame(checkFrame);
2124 if (scrollFrame) {
2125 nsIPresShell::SetCapturingContent(checkFrame->GetContent(), CAPTURE_IGNOREALLOWED);
2126 break;
2128 checkFrame = checkFrame->GetParent();
2132 // XXX This is screwy; it really should use the selection frame, not the
2133 // event frame
2134 const nsFrameSelection* frameselection = nsnull;
2135 if (useFrameSelection)
2136 frameselection = GetConstFrameSelection();
2137 else
2138 frameselection = shell->ConstFrameSelection();
2140 if (frameselection->GetDisplaySelection() == nsISelectionController::SELECTION_OFF)
2141 return NS_OK;//nothing to do we cannot affect selection from here
2143 nsMouseEvent *me = (nsMouseEvent *)aEvent;
2145 #ifdef XP_MACOSX
2146 if (me->isControl)
2147 return NS_OK;//short ciruit. hard coded for mac due to time restraints.
2148 PRBool control = me->isMeta;
2149 #else
2150 PRBool control = me->isControl;
2151 #endif
2153 nsCOMPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
2154 if (me->clickCount >1 )
2156 // These methods aren't const but can't actually delete anything,
2157 // so no need for nsWeakFrame.
2158 fc->SetMouseDownState(PR_TRUE);
2159 fc->SetMouseDoubleDown(PR_TRUE);
2160 return HandleMultiplePress(aPresContext, aEvent, aEventStatus, control);
2163 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
2164 ContentOffsets offsets = GetContentOffsetsFromPoint(pt);
2166 if (!offsets.content)
2167 return NS_ERROR_FAILURE;
2169 // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
2170 nsCOMPtr<nsIContent>parentContent;
2171 PRInt32 contentOffset;
2172 PRInt32 target;
2173 rv = GetDataForTableSelection(frameselection, shell, me, getter_AddRefs(parentContent), &contentOffset, &target);
2174 if (NS_SUCCEEDED(rv) && parentContent)
2176 fc->SetMouseDownState(PR_TRUE);
2177 return fc->HandleTableSelection(parentContent, contentOffset, target, me);
2180 fc->SetDelayedCaretData(0);
2182 // Check if any part of this frame is selected, and if the
2183 // user clicked inside the selected region. If so, we delay
2184 // starting a new selection since the user may be trying to
2185 // drag the selected region to some other app.
2187 SelectionDetails *details = 0;
2188 PRBool isSelected = ((GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT);
2190 if (isSelected)
2192 PRBool inSelection = PR_FALSE;
2193 details = frameselection->LookUpSelection(offsets.content, 0,
2194 offsets.EndOffset(), PR_FALSE);
2197 // If there are any details, check to see if the user clicked
2198 // within any selected region of the frame.
2201 SelectionDetails *curDetail = details;
2203 while (curDetail)
2206 // If the user clicked inside a selection, then just
2207 // return without doing anything. We will handle placing
2208 // the caret later on when the mouse is released. We ignore
2209 // the spellcheck and find selections.
2211 if (curDetail->mType != nsISelectionController::SELECTION_SPELLCHECK &&
2212 curDetail->mType != nsISelectionController::SELECTION_FIND &&
2213 curDetail->mStart <= offsets.StartOffset() &&
2214 offsets.EndOffset() <= curDetail->mEnd)
2216 inSelection = PR_TRUE;
2219 SelectionDetails *nextDetail = curDetail->mNext;
2220 delete curDetail;
2221 curDetail = nextDetail;
2224 if (inSelection) {
2225 fc->SetMouseDownState(PR_FALSE);
2226 fc->SetDelayedCaretData(me);
2227 return NS_OK;
2231 fc->SetMouseDownState(PR_TRUE);
2233 // Do not touch any nsFrame members after this point without adding
2234 // weakFrame checks.
2235 rv = fc->HandleClick(offsets.content, offsets.StartOffset(),
2236 offsets.EndOffset(), me->isShift, control,
2237 offsets.associateWithNext);
2239 if (NS_FAILED(rv))
2240 return rv;
2242 if (offsets.offset != offsets.secondaryOffset)
2243 fc->MaintainSelection();
2245 if (isEditor && !me->isShift &&
2246 (offsets.EndOffset() - offsets.StartOffset()) == 1)
2248 // A single node is selected and we aren't extending an existing
2249 // selection, which means the user clicked directly on an object (either
2250 // -moz-user-select: all or a non-text node without children).
2251 // Therefore, disable selection extension during mouse moves.
2252 // XXX This is a bit hacky; shouldn't editor be able to deal with this?
2253 fc->SetMouseDownState(PR_FALSE);
2256 return rv;
2260 * Multiple Mouse Press -- line or paragraph selection -- for the frame.
2261 * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
2263 NS_IMETHODIMP
2264 nsFrame::HandleMultiplePress(nsPresContext* aPresContext,
2265 nsGUIEvent* aEvent,
2266 nsEventStatus* aEventStatus,
2267 PRBool aControlHeld)
2269 NS_ENSURE_ARG_POINTER(aEventStatus);
2270 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
2271 return NS_OK;
2274 if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
2275 return NS_OK;
2278 // Find out whether we're doing line or paragraph selection.
2279 // If browser.triple_click_selects_paragraph is true, triple-click selects paragraph.
2280 // Otherwise, triple-click selects line, and quadruple-click selects paragraph
2281 // (on platforms that support quadruple-click).
2282 nsSelectionAmount beginAmount, endAmount;
2283 nsMouseEvent *me = (nsMouseEvent *)aEvent;
2284 if (!me) return NS_OK;
2286 if (me->clickCount == 4) {
2287 beginAmount = endAmount = eSelectParagraph;
2288 } else if (me->clickCount == 3) {
2289 if (nsContentUtils::GetBoolPref("browser.triple_click_selects_paragraph")) {
2290 beginAmount = endAmount = eSelectParagraph;
2291 } else {
2292 beginAmount = eSelectBeginLine;
2293 endAmount = eSelectEndLine;
2295 } else if (me->clickCount == 2) {
2296 // We only want inline frames; PeekBackwardAndForward dislikes blocks
2297 beginAmount = endAmount = eSelectWord;
2298 } else {
2299 return NS_OK;
2302 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
2303 ContentOffsets offsets = GetContentOffsetsFromPoint(pt);
2304 if (!offsets.content) return NS_ERROR_FAILURE;
2306 nsIFrame* theFrame;
2307 PRInt32 offset;
2308 // Maybe make this a static helper?
2309 const nsFrameSelection* frameSelection =
2310 PresContext()->GetPresShell()->ConstFrameSelection();
2311 theFrame = frameSelection->
2312 GetFrameForNodeOffset(offsets.content, offsets.offset,
2313 nsFrameSelection::HINT(offsets.associateWithNext),
2314 &offset);
2315 if (!theFrame)
2316 return NS_ERROR_FAILURE;
2318 nsFrame* frame = static_cast<nsFrame*>(theFrame);
2320 return frame->PeekBackwardAndForward(beginAmount, endAmount,
2321 offsets.offset, aPresContext,
2322 beginAmount != eSelectWord,
2323 aControlHeld);
2326 NS_IMETHODIMP
2327 nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack,
2328 nsSelectionAmount aAmountForward,
2329 PRInt32 aStartPos,
2330 nsPresContext* aPresContext,
2331 PRBool aJumpLines,
2332 PRBool aMultipleSelection)
2334 nsIFrame* baseFrame = this;
2335 PRInt32 baseOffset = aStartPos;
2336 nsresult rv;
2338 if (aAmountBack == eSelectWord) {
2339 // To avoid selecting the previous word when at start of word,
2340 // first move one character forward.
2341 nsPeekOffsetStruct pos;
2342 pos.SetData(eSelectCharacter,
2343 eDirNext,
2344 aStartPos,
2346 aJumpLines,
2347 PR_TRUE, //limit on scrolled views
2348 PR_FALSE,
2349 PR_FALSE);
2350 rv = PeekOffset(&pos);
2351 if (NS_SUCCEEDED(rv)) {
2352 baseFrame = pos.mResultFrame;
2353 baseOffset = pos.mContentOffset;
2357 // Use peek offset one way then the other:
2358 nsPeekOffsetStruct startpos;
2359 startpos.SetData(aAmountBack,
2360 eDirPrevious,
2361 baseOffset,
2363 aJumpLines,
2364 PR_TRUE, //limit on scrolled views
2365 PR_FALSE,
2366 PR_FALSE);
2367 rv = baseFrame->PeekOffset(&startpos);
2368 if (NS_FAILED(rv))
2369 return rv;
2371 nsPeekOffsetStruct endpos;
2372 endpos.SetData(aAmountForward,
2373 eDirNext,
2374 aStartPos,
2376 aJumpLines,
2377 PR_TRUE, //limit on scrolled views
2378 PR_FALSE,
2379 PR_FALSE);
2380 rv = PeekOffset(&endpos);
2381 if (NS_FAILED(rv))
2382 return rv;
2384 // Keep frameSelection alive.
2385 nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
2387 rv = frameSelection->HandleClick(startpos.mResultContent,
2388 startpos.mContentOffset, startpos.mContentOffset,
2389 PR_FALSE, aMultipleSelection,
2390 nsFrameSelection::HINTRIGHT);
2391 if (NS_FAILED(rv))
2392 return rv;
2394 rv = frameSelection->HandleClick(endpos.mResultContent,
2395 endpos.mContentOffset, endpos.mContentOffset,
2396 PR_TRUE, PR_FALSE,
2397 nsFrameSelection::HINTLEFT);
2398 if (NS_FAILED(rv))
2399 return rv;
2401 // maintain selection
2402 return frameSelection->MaintainSelection(aAmountBack);
2405 NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext,
2406 nsGUIEvent* aEvent,
2407 nsEventStatus* aEventStatus)
2409 PRBool selectable;
2410 PRUint8 selectStyle;
2411 IsSelectable(&selectable, &selectStyle);
2412 // XXX Do we really need to exclude non-selectable content here?
2413 // GetContentOffsetsFromPoint can handle it just fine, although some
2414 // other stuff might not like it.
2415 if (!selectable)
2416 return NS_OK;
2417 if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
2418 return NS_OK;
2420 nsIPresShell *presShell = aPresContext->PresShell();
2422 nsCOMPtr<nsFrameSelection> frameselection = GetFrameSelection();
2423 PRBool mouseDown = frameselection->GetMouseDownState();
2424 if (!mouseDown)
2425 return NS_OK;
2427 frameselection->StopAutoScrollTimer();
2429 // Check if we are dragging in a table cell
2430 nsCOMPtr<nsIContent> parentContent;
2431 PRInt32 contentOffset;
2432 PRInt32 target;
2433 nsMouseEvent *me = (nsMouseEvent *)aEvent;
2434 nsresult result;
2435 result = GetDataForTableSelection(frameselection, presShell, me,
2436 getter_AddRefs(parentContent),
2437 &contentOffset, &target);
2439 nsWeakFrame weakThis = this;
2440 if (NS_SUCCEEDED(result) && parentContent) {
2441 frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
2442 } else {
2443 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
2444 frameselection->HandleDrag(this, pt);
2447 // The frameselection object notifies selection listeners synchronously above
2448 // which might have killed us.
2449 if (!weakThis.IsAlive()) {
2450 return NS_OK;
2453 // get the nearest scrollframe
2454 nsIFrame* checkFrame = this;
2455 nsIScrollableFrame *scrollFrame = nsnull;
2456 while (checkFrame) {
2457 scrollFrame = do_QueryFrame(checkFrame);
2458 if (scrollFrame) {
2459 break;
2461 checkFrame = checkFrame->GetParent();
2464 if (scrollFrame) {
2465 nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
2466 if (capturingFrame) {
2467 nsPoint pt =
2468 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, capturingFrame);
2469 frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
2473 return NS_OK;
2477 * This static method handles part of the nsFrame::HandleRelease in a way
2478 * which doesn't rely on the nsFrame object to stay alive.
2480 static nsresult
2481 HandleFrameSelection(nsFrameSelection* aFrameSelection,
2482 nsIFrame::ContentOffsets& aOffsets,
2483 PRBool aHandleTableSel,
2484 PRInt32 aContentOffsetForTableSel,
2485 PRInt32 aTargetForTableSel,
2486 nsIContent* aParentContentForTableSel,
2487 nsGUIEvent* aEvent,
2488 nsEventStatus* aEventStatus)
2490 if (!aFrameSelection) {
2491 return NS_OK;
2494 nsresult rv = NS_OK;
2496 if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
2497 if (!aHandleTableSel) {
2498 nsMouseEvent *me = aFrameSelection->GetDelayedCaretData();
2499 if (!aOffsets.content || !me) {
2500 return NS_ERROR_FAILURE;
2503 // We are doing this to simulate what we would have done on HandlePress.
2504 // We didn't do it there to give the user an opportunity to drag
2505 // the text, but since they didn't drag, we want to place the
2506 // caret.
2507 // However, we'll use the mouse position from the release, since:
2508 // * it's easier
2509 // * that's the normal click position to use (although really, in
2510 // the normal case, small movements that don't count as a drag
2511 // can do selection)
2512 aFrameSelection->SetMouseDownState(PR_TRUE);
2514 rv = aFrameSelection->HandleClick(aOffsets.content,
2515 aOffsets.StartOffset(),
2516 aOffsets.EndOffset(),
2517 me->isShift, PR_FALSE,
2518 aOffsets.associateWithNext);
2519 if (NS_FAILED(rv)) {
2520 return rv;
2522 } else if (aParentContentForTableSel) {
2523 aFrameSelection->SetMouseDownState(PR_FALSE);
2524 rv = aFrameSelection->HandleTableSelection(aParentContentForTableSel,
2525 aContentOffsetForTableSel,
2526 aTargetForTableSel,
2527 (nsMouseEvent *)aEvent);
2528 if (NS_FAILED(rv)) {
2529 return rv;
2532 aFrameSelection->SetDelayedCaretData(0);
2535 aFrameSelection->SetMouseDownState(PR_FALSE);
2536 aFrameSelection->StopAutoScrollTimer();
2538 return NS_OK;
2541 NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext,
2542 nsGUIEvent* aEvent,
2543 nsEventStatus* aEventStatus)
2545 nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
2547 nsCOMPtr<nsIContent> captureContent = nsIPresShell::GetCapturingContent();
2549 // We can unconditionally stop capturing because
2550 // we should never be capturing when the mouse button is up
2551 nsIPresShell::SetCapturingContent(nsnull, 0);
2553 PRBool selectionOff =
2554 (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF);
2556 nsRefPtr<nsFrameSelection> frameselection;
2557 ContentOffsets offsets;
2558 nsCOMPtr<nsIContent> parentContent;
2559 PRInt32 contentOffsetForTableSel = 0;
2560 PRInt32 targetForTableSel = 0;
2561 PRBool handleTableSelection = PR_TRUE;
2563 if (!selectionOff) {
2564 frameselection = GetFrameSelection();
2565 if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
2566 // Check if the frameselection recorded the mouse going down.
2567 // If not, the user must have clicked in a part of the selection.
2568 // Place the caret before continuing!
2570 PRBool mouseDown = frameselection->GetMouseDownState();
2571 nsMouseEvent *me = frameselection->GetDelayedCaretData();
2573 if (!mouseDown && me && me->clickCount < 2) {
2574 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
2575 offsets = GetContentOffsetsFromPoint(pt);
2576 handleTableSelection = PR_FALSE;
2577 } else {
2578 GetDataForTableSelection(frameselection, PresContext()->PresShell(),
2579 (nsMouseEvent *)aEvent,
2580 getter_AddRefs(parentContent),
2581 &contentOffsetForTableSel,
2582 &targetForTableSel);
2587 // We might be capturing in some other document and the event just happened to
2588 // trickle down here. Make sure that document's frame selection is notified.
2589 // Note, this may cause the current nsFrame object to be deleted, bug 336592.
2590 nsRefPtr<nsFrameSelection> frameSelection;
2591 if (activeFrame != this &&
2592 static_cast<nsFrame*>(activeFrame)->DisplaySelection(activeFrame->PresContext())
2593 != nsISelectionController::SELECTION_OFF) {
2594 frameSelection = activeFrame->GetFrameSelection();
2597 // Also check the selection of the capturing content which might be in a
2598 // different document.
2599 if (!frameSelection && captureContent) {
2600 nsIDocument* doc = captureContent->GetCurrentDoc();
2601 if (doc) {
2602 nsIPresShell* capturingShell = doc->GetShell();
2603 if (capturingShell && capturingShell != PresContext()->GetPresShell()) {
2604 frameSelection = capturingShell->FrameSelection();
2609 if (frameSelection) {
2610 frameSelection->SetMouseDownState(PR_FALSE);
2611 frameSelection->StopAutoScrollTimer();
2614 // Do not call any methods of the current object after this point!!!
2615 // The object is perhaps dead!
2617 return selectionOff
2618 ? NS_OK
2619 : HandleFrameSelection(frameselection, offsets, handleTableSelection,
2620 contentOffsetForTableSel, targetForTableSel,
2621 parentContent, aEvent, aEventStatus);
2624 struct NS_STACK_CLASS FrameContentRange {
2625 FrameContentRange(nsIContent* aContent, PRInt32 aStart, PRInt32 aEnd) :
2626 content(aContent), start(aStart), end(aEnd) { }
2627 nsCOMPtr<nsIContent> content;
2628 PRInt32 start;
2629 PRInt32 end;
2632 // Retrieve the content offsets of a frame
2633 static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) {
2634 nsCOMPtr<nsIContent> content, parent;
2635 content = aFrame->GetContent();
2636 if (!content) {
2637 NS_WARNING("Frame has no content");
2638 return FrameContentRange(nsnull, -1, -1);
2640 nsIAtom* type = aFrame->GetType();
2641 if (type == nsGkAtoms::textFrame) {
2642 PRInt32 offset, offsetEnd;
2643 aFrame->GetOffsets(offset, offsetEnd);
2644 return FrameContentRange(content, offset, offsetEnd);
2646 if (type == nsGkAtoms::brFrame) {
2647 parent = content->GetParent();
2648 PRInt32 beginOffset = parent->IndexOf(content);
2649 return FrameContentRange(parent, beginOffset, beginOffset);
2651 // Loop to deal with anonymous content, which has no index; this loop
2652 // probably won't run more than twice under normal conditions
2653 do {
2654 parent = content->GetParent();
2655 if (parent) {
2656 PRInt32 beginOffset = parent->IndexOf(content);
2657 if (beginOffset >= 0)
2658 return FrameContentRange(parent, beginOffset, beginOffset + 1);
2659 content = parent;
2661 } while (parent);
2663 // The root content node must act differently
2664 return FrameContentRange(content, 0, content->GetChildCount());
2667 // The FrameTarget represents the closest frame to a point that can be selected
2668 // The frame is the frame represented, frameEdge says whether one end of the
2669 // frame is the result (in which case different handling is needed), and
2670 // afterFrame says which end is repersented if frameEdge is true
2671 struct FrameTarget {
2672 FrameTarget(nsIFrame* aFrame, PRBool aFrameEdge, PRBool aAfterFrame,
2673 PRBool aEmptyBlock = PR_FALSE) :
2674 frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame),
2675 emptyBlock(aEmptyBlock) { }
2676 static FrameTarget Null() {
2677 return FrameTarget(nsnull, PR_FALSE, PR_FALSE);
2679 PRBool IsNull() {
2680 return !frame;
2682 nsIFrame* frame;
2683 PRPackedBool frameEdge;
2684 PRPackedBool afterFrame;
2685 PRPackedBool emptyBlock;
2688 // See function implementation for information
2689 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint);
2691 static PRBool SelfIsSelectable(nsIFrame* aFrame)
2693 return !(aFrame->IsGeneratedContentFrame() ||
2694 aFrame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_NONE);
2697 static PRBool SelectionDescendToKids(nsIFrame* aFrame) {
2698 PRUint8 style = aFrame->GetStyleUIReset()->mUserSelect;
2699 nsIFrame* parent = aFrame->GetParent();
2700 // If we are only near (not directly over) then don't traverse
2701 // frames with independent selection (e.g. text and list controls)
2702 // unless we're already inside such a frame (see bug 268497). Note that this
2703 // prevents any of the users of this method from entering form controls.
2704 // XXX We might want some way to allow using the up-arrow to go into a form
2705 // control, but the focus didn't work right anyway; it'd probably be enough
2706 // if the left and right arrows could enter textboxes (which I don't believe
2707 // they can at the moment)
2708 return !aFrame->IsGeneratedContentFrame() &&
2709 style != NS_STYLE_USER_SELECT_ALL &&
2710 style != NS_STYLE_USER_SELECT_NONE &&
2711 ((parent->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) ||
2712 !(aFrame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION));
2715 static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
2716 nsPoint aPoint)
2718 nsIFrame* parent = aChild->GetParent();
2719 if (SelectionDescendToKids(aChild)) {
2720 nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
2721 return GetSelectionClosestFrame(aChild, pt);
2723 return FrameTarget(aChild, PR_FALSE, PR_FALSE);
2726 // When the cursor needs to be at the beginning of a block, it shouldn't be
2727 // before the first child. A click on a block whose first child is a block
2728 // should put the cursor in the child. The cursor shouldn't be between the
2729 // blocks, because that's not where it's expected.
2730 // Note that this method is guaranteed to succeed.
2731 static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame,
2732 PRBool aEndFrame) {
2733 if (SelectionDescendToKids(aFrame)) {
2734 nsIFrame* result = nsnull;
2735 nsIFrame *frame = aFrame->GetFirstChild(nsnull);
2736 if (!aEndFrame) {
2737 while (frame && (!SelfIsSelectable(frame) ||
2738 frame->IsEmpty()))
2739 frame = frame->GetNextSibling();
2740 if (frame)
2741 result = frame;
2742 } else {
2743 // Because the frame tree is singly linked, to find the last frame,
2744 // we have to iterate through all the frames
2745 // XXX I have a feeling this could be slow for long blocks, although
2746 // I can't find any slowdowns
2747 while (frame) {
2748 if (!frame->IsEmpty() && SelfIsSelectable(frame))
2749 result = frame;
2750 frame = frame->GetNextSibling();
2753 if (result)
2754 return DrillDownToSelectionFrame(result, aEndFrame);
2756 // If the current frame has no targetable children, target the current frame
2757 return FrameTarget(aFrame, PR_TRUE, aEndFrame);
2760 // This method finds the closest valid FrameTarget on a given line; if there is
2761 // no valid FrameTarget on the line, it returns a null FrameTarget
2762 static FrameTarget GetSelectionClosestFrameForLine(
2763 nsBlockFrame* aParent,
2764 nsBlockFrame::line_iterator aLine,
2765 nsPoint aPoint)
2767 nsIFrame *frame = aLine->mFirstChild;
2768 // Account for end of lines (any iterator from the block is valid)
2769 if (aLine == aParent->end_lines())
2770 return DrillDownToSelectionFrame(aParent, PR_TRUE);
2771 nsIFrame *closestFromLeft = nsnull, *closestFromRight = nsnull;
2772 nsRect rect = aLine->mBounds;
2773 nscoord closestLeft = rect.x, closestRight = rect.XMost();
2774 for (PRInt32 n = aLine->GetChildCount(); n;
2775 --n, frame = frame->GetNextSibling()) {
2776 if (!SelfIsSelectable(frame) || frame->IsEmpty())
2777 continue;
2778 nsRect frameRect = frame->GetRect();
2779 if (aPoint.x >= frameRect.x) {
2780 if (aPoint.x < frameRect.XMost()) {
2781 return GetSelectionClosestFrameForChild(frame, aPoint);
2783 if (frameRect.XMost() >= closestLeft) {
2784 closestFromLeft = frame;
2785 closestLeft = frameRect.XMost();
2787 } else {
2788 if (frameRect.x <= closestRight) {
2789 closestFromRight = frame;
2790 closestRight = frameRect.x;
2794 if (!closestFromLeft && !closestFromRight) {
2795 // We should only get here if there are no selectable frames on a line
2796 // XXX Do we need more elaborate handling here?
2797 return FrameTarget::Null();
2799 if (closestFromLeft &&
2800 (!closestFromRight ||
2801 (abs(aPoint.x - closestLeft) <= abs(aPoint.x - closestRight)))) {
2802 return GetSelectionClosestFrameForChild(closestFromLeft, aPoint);
2804 return GetSelectionClosestFrameForChild(closestFromRight, aPoint);
2807 // This method is for the special handling we do for block frames; they're
2808 // special because they represent paragraphs and because they are organized
2809 // into lines, which have bounds that are not stored elsewhere in the
2810 // frame tree. Returns a null FrameTarget for frames which are not
2811 // blocks or blocks with no lines except editable one.
2812 static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
2813 nsPoint aPoint)
2815 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aFrame); // used only for QI
2816 if (!bf)
2817 return FrameTarget::Null();
2819 // This code searches for the correct line
2820 nsBlockFrame::line_iterator firstLine = bf->begin_lines();
2821 nsBlockFrame::line_iterator end = bf->end_lines();
2822 if (firstLine == end) {
2823 nsIContent *blockContent = aFrame->GetContent();
2824 if (blockContent && blockContent->IsEditable()) {
2825 // If the frame is ediable empty block, we should return it with empty
2826 // flag.
2827 return FrameTarget(aFrame, PR_FALSE, PR_FALSE, PR_TRUE);
2829 return FrameTarget::Null();
2831 nsBlockFrame::line_iterator curLine = firstLine;
2832 nsBlockFrame::line_iterator closestLine = end;
2833 while (curLine != end) {
2834 // Check to see if our point lies with the line's Y bounds
2835 nscoord y = aPoint.y - curLine->mBounds.y;
2836 nscoord height = curLine->mBounds.height;
2837 if (y >= 0 && y < height) {
2838 closestLine = curLine;
2839 break; // We found the line; stop looking
2841 if (y < 0)
2842 break;
2843 ++curLine;
2846 if (closestLine == end) {
2847 nsBlockFrame::line_iterator prevLine = curLine.prev();
2848 nsBlockFrame::line_iterator nextLine = curLine;
2849 // Avoid empty lines
2850 while (nextLine != end && nextLine->IsEmpty())
2851 ++nextLine;
2852 while (prevLine != end && prevLine->IsEmpty())
2853 --prevLine;
2855 // This hidden pref dictates whether a point above or below all lines comes
2856 // up with a line or the beginning or end of the frame; 0 on Windows,
2857 // 1 on other platforms by default at the writing of this code
2858 PRInt32 dragOutOfFrame =
2859 nsContentUtils::GetIntPref("browser.drag_out_of_frame_style");
2861 if (prevLine == end) {
2862 if (dragOutOfFrame == 1 || nextLine == end)
2863 return DrillDownToSelectionFrame(aFrame, PR_FALSE);
2864 closestLine = nextLine;
2865 } else if (nextLine == end) {
2866 if (dragOutOfFrame == 1)
2867 return DrillDownToSelectionFrame(aFrame, PR_TRUE);
2868 closestLine = prevLine;
2869 } else { // Figure out which line is closer
2870 if (aPoint.y - prevLine->mBounds.YMost() < nextLine->mBounds.y - aPoint.y)
2871 closestLine = prevLine;
2872 else
2873 closestLine = nextLine;
2877 do {
2878 FrameTarget target = GetSelectionClosestFrameForLine(bf, closestLine,
2879 aPoint);
2880 if (!target.IsNull())
2881 return target;
2882 ++closestLine;
2883 } while (closestLine != end);
2884 // Fall back to just targeting the last targetable place
2885 return DrillDownToSelectionFrame(aFrame, PR_TRUE);
2888 // GetSelectionClosestFrame is the helper function that calculates the closest
2889 // frame to the given point.
2890 // It doesn't completely account for offset styles, so needs to be used in
2891 // restricted environments.
2892 // Cannot handle overlapping frames correctly, so it should receive the output
2893 // of GetFrameForPoint
2894 // Guaranteed to return a valid FrameTarget
2895 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint)
2898 // Handle blocks; if the frame isn't a block, the method fails
2899 FrameTarget target = GetSelectionClosestFrameForBlock(aFrame, aPoint);
2900 if (!target.IsNull())
2901 return target;
2904 nsIFrame *kid = aFrame->GetFirstChild(nsnull);
2906 if (kid) {
2907 // Go through all the child frames to find the closest one
2909 // Large number to force the comparison to succeed
2910 const nscoord HUGE_DISTANCE = nscoord_MAX;
2911 nscoord closestXDistance = HUGE_DISTANCE;
2912 nscoord closestYDistance = HUGE_DISTANCE;
2913 nsIFrame *closestFrame = nsnull;
2915 for (; kid; kid = kid->GetNextSibling()) {
2916 if (!SelfIsSelectable(kid) || kid->IsEmpty())
2917 continue;
2919 nsRect rect = kid->GetRect();
2921 nscoord fromLeft = aPoint.x - rect.x;
2922 nscoord fromRight = aPoint.x - rect.XMost();
2924 nscoord xDistance;
2925 if (fromLeft >= 0 && fromRight <= 0) {
2926 xDistance = 0;
2927 } else {
2928 xDistance = NS_MIN(abs(fromLeft), abs(fromRight));
2931 if (xDistance <= closestXDistance)
2933 if (xDistance < closestXDistance)
2934 closestYDistance = HUGE_DISTANCE;
2936 nscoord fromTop = aPoint.y - rect.y;
2937 nscoord fromBottom = aPoint.y - rect.YMost();
2939 nscoord yDistance;
2940 if (fromTop >= 0 && fromBottom <= 0)
2941 yDistance = 0;
2942 else
2943 yDistance = NS_MIN(abs(fromTop), abs(fromBottom));
2945 if (yDistance < closestYDistance)
2947 closestXDistance = xDistance;
2948 closestYDistance = yDistance;
2949 closestFrame = kid;
2953 if (closestFrame)
2954 return GetSelectionClosestFrameForChild(closestFrame, aPoint);
2956 return FrameTarget(aFrame, PR_FALSE, PR_FALSE);
2959 nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame, nsPoint aPoint)
2961 nsIFrame::ContentOffsets offsets;
2962 FrameContentRange range = GetRangeForFrame(aFrame);
2963 offsets.content = range.content;
2964 // If there are continuations (meaning it's not one rectangle), this is the
2965 // best this function can do
2966 if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
2967 offsets.offset = range.start;
2968 offsets.secondaryOffset = range.end;
2969 offsets.associateWithNext = PR_TRUE;
2970 return offsets;
2973 // Figure out whether the offsets should be over, after, or before the frame
2974 nsRect rect(nsPoint(0, 0), aFrame->GetSize());
2976 PRBool isBlock = (aFrame->GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_INLINE);
2977 PRBool isRtl = (aFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL);
2978 if ((isBlock && rect.y < aPoint.y) ||
2979 (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
2980 (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
2981 offsets.offset = range.end;
2982 if (rect.Contains(aPoint))
2983 offsets.secondaryOffset = range.start;
2984 else
2985 offsets.secondaryOffset = range.end;
2986 } else {
2987 offsets.offset = range.start;
2988 if (rect.Contains(aPoint))
2989 offsets.secondaryOffset = range.end;
2990 else
2991 offsets.secondaryOffset = range.start;
2993 offsets.associateWithNext = (offsets.offset == range.start);
2994 return offsets;
2997 static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
2998 nsIFrame* adjustedFrame = aFrame;
2999 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent())
3001 // These are the conditions that make all children not able to handle
3002 // a cursor.
3003 if (frame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_NONE ||
3004 frame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_ALL ||
3005 frame->IsGeneratedContentFrame()) {
3006 adjustedFrame = frame;
3009 return adjustedFrame;
3013 nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint,
3014 PRBool aIgnoreSelectionStyle)
3016 nsIFrame *adjustedFrame;
3017 if (aIgnoreSelectionStyle) {
3018 adjustedFrame = this;
3020 else {
3021 // This section of code deals with special selection styles. Note that
3022 // -moz-none and -moz-all exist, even though they don't need to be explicitly
3023 // handled.
3024 // The offset is forced not to end up in generated content; content offsets
3025 // cannot represent content outside of the document's content tree.
3027 adjustedFrame = AdjustFrameForSelectionStyles(this);
3029 // -moz-user-select: all needs special handling, because clicking on it
3030 // should lead to the whole frame being selected
3031 if (adjustedFrame && adjustedFrame->GetStyleUIReset()->mUserSelect ==
3032 NS_STYLE_USER_SELECT_ALL) {
3033 return OffsetsForSingleFrame(adjustedFrame, aPoint +
3034 this->GetOffsetTo(adjustedFrame));
3037 // For other cases, try to find a closest frame starting from the parent of
3038 // the unselectable frame
3039 if (adjustedFrame != this)
3040 adjustedFrame = adjustedFrame->GetParent();
3043 nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
3045 FrameTarget closest = GetSelectionClosestFrame(adjustedFrame, adjustedPoint);
3047 if (closest.emptyBlock) {
3048 ContentOffsets offsets;
3049 NS_ASSERTION(closest.frame,
3050 "closest.frame must not be null when it's empty");
3051 offsets.content = closest.frame->GetContent();
3052 offsets.offset = 0;
3053 offsets.secondaryOffset = 0;
3054 offsets.associateWithNext = PR_TRUE;
3055 return offsets;
3058 // If the correct offset is at one end of a frame, use offset-based
3059 // calculation method
3060 if (closest.frameEdge) {
3061 ContentOffsets offsets;
3062 FrameContentRange range = GetRangeForFrame(closest.frame);
3063 offsets.content = range.content;
3064 if (closest.afterFrame)
3065 offsets.offset = range.end;
3066 else
3067 offsets.offset = range.start;
3068 offsets.secondaryOffset = offsets.offset;
3069 offsets.associateWithNext = (offsets.offset == range.start);
3070 return offsets;
3072 nsPoint pt = aPoint - closest.frame->GetOffsetTo(this);
3073 return static_cast<nsFrame*>(closest.frame)->CalcContentOffsetsFromFramePoint(pt);
3075 // XXX should I add some kind of offset standardization?
3076 // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
3077 // x and first z put the cursor in the same logical position in addition
3078 // to the same visual position?
3081 nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint)
3083 return OffsetsForSingleFrame(this, aPoint);
3086 NS_IMETHODIMP
3087 nsFrame::GetCursor(const nsPoint& aPoint,
3088 nsIFrame::Cursor& aCursor)
3090 FillCursorInformationFromStyle(GetStyleUserInterface(), aCursor);
3091 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
3092 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
3096 return NS_OK;
3099 // Resize and incremental reflow
3101 /* virtual */ void
3102 nsFrame::MarkIntrinsicWidthsDirty()
3104 // This version is meant only for what used to be box-to-block adaptors.
3105 // It should not be called by other derived classes.
3106 if (IsBoxWrapped()) {
3107 nsBoxLayoutMetrics *metrics = BoxMetrics();
3109 SizeNeedsRecalc(metrics->mPrefSize);
3110 SizeNeedsRecalc(metrics->mMinSize);
3111 SizeNeedsRecalc(metrics->mMaxSize);
3112 SizeNeedsRecalc(metrics->mBlockPrefSize);
3113 SizeNeedsRecalc(metrics->mBlockMinSize);
3114 CoordNeedsRecalc(metrics->mFlex);
3115 CoordNeedsRecalc(metrics->mAscent);
3119 /* virtual */ nscoord
3120 nsFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
3122 nscoord result = 0;
3123 DISPLAY_MIN_WIDTH(this, result);
3124 return result;
3127 /* virtual */ nscoord
3128 nsFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
3130 nscoord result = 0;
3131 DISPLAY_PREF_WIDTH(this, result);
3132 return result;
3135 /* virtual */ void
3136 nsFrame::AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
3137 nsIFrame::InlineMinWidthData *aData)
3139 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
3140 PRBool canBreak = !CanContinueTextRun() &&
3141 GetParent()->GetStyleText()->WhiteSpaceCanWrap();
3143 if (canBreak)
3144 aData->OptionallyBreak(aRenderingContext);
3145 aData->trailingWhitespace = 0;
3146 aData->skipWhitespace = PR_FALSE;
3147 aData->trailingTextFrame = nsnull;
3148 aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
3149 this, nsLayoutUtils::MIN_WIDTH);
3150 aData->atStartOfLine = PR_FALSE;
3151 if (canBreak)
3152 aData->OptionallyBreak(aRenderingContext);
3155 /* virtual */ void
3156 nsFrame::AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
3157 nsIFrame::InlinePrefWidthData *aData)
3159 aData->trailingWhitespace = 0;
3160 aData->skipWhitespace = PR_FALSE;
3161 nscoord myPref = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
3162 this, nsLayoutUtils::PREF_WIDTH);
3163 aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, myPref);
3166 void
3167 nsIFrame::InlineMinWidthData::ForceBreak(nsIRenderingContext *aRenderingContext)
3169 currentLine -= trailingWhitespace;
3170 prevLines = NS_MAX(prevLines, currentLine);
3171 currentLine = trailingWhitespace = 0;
3173 for (PRUint32 i = 0, i_end = floats.Length(); i != i_end; ++i) {
3174 nsIFrame *floatFrame = floats[i];
3175 nscoord float_min =
3176 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, floatFrame,
3177 nsLayoutUtils::MIN_WIDTH);
3178 if (float_min > prevLines)
3179 prevLines = float_min;
3181 floats.Clear();
3182 trailingTextFrame = nsnull;
3183 skipWhitespace = PR_TRUE;
3186 void
3187 nsIFrame::InlineMinWidthData::OptionallyBreak(nsIRenderingContext *aRenderingContext)
3189 trailingTextFrame = nsnull;
3191 // If we can fit more content into a smaller width by staying on this
3192 // line (because we're still at a negative offset due to negative
3193 // text-indent or negative margin), don't break. Otherwise, do the
3194 // same as ForceBreak. it doesn't really matter when we accumulate
3195 // floats.
3196 if (currentLine < 0 || atStartOfLine)
3197 return;
3198 ForceBreak(aRenderingContext);
3201 void
3202 nsIFrame::InlinePrefWidthData::ForceBreak(nsIRenderingContext *aRenderingContext)
3204 if (floats.Length() != 0) {
3205 // preferred widths accumulated for floats that have already
3206 // been cleared past
3207 nscoord floats_done = 0,
3208 // preferred widths accumulated for floats that have not yet
3209 // been cleared past
3210 floats_cur_left = 0,
3211 floats_cur_right = 0;
3213 for (PRUint32 i = 0, i_end = floats.Length(); i != i_end; ++i) {
3214 nsIFrame *floatFrame = floats[i];
3215 const nsStyleDisplay *floatDisp = floatFrame->GetStyleDisplay();
3216 if (floatDisp->mBreakType == NS_STYLE_CLEAR_LEFT ||
3217 floatDisp->mBreakType == NS_STYLE_CLEAR_RIGHT ||
3218 floatDisp->mBreakType == NS_STYLE_CLEAR_LEFT_AND_RIGHT) {
3219 nscoord floats_cur = NSCoordSaturatingAdd(floats_cur_left,
3220 floats_cur_right);
3221 if (floats_cur > floats_done)
3222 floats_done = floats_cur;
3223 if (floatDisp->mBreakType != NS_STYLE_CLEAR_RIGHT)
3224 floats_cur_left = 0;
3225 if (floatDisp->mBreakType != NS_STYLE_CLEAR_LEFT)
3226 floats_cur_right = 0;
3229 nscoord &floats_cur = floatDisp->mFloats == NS_STYLE_FLOAT_LEFT
3230 ? floats_cur_left : floats_cur_right;
3231 nscoord floatWidth =
3232 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
3233 floatFrame,
3234 nsLayoutUtils::PREF_WIDTH);
3235 // Negative-width floats don't change the available space so they
3236 // shouldn't change our intrinsic line width either.
3237 floats_cur =
3238 NSCoordSaturatingAdd(floats_cur, NS_MAX(0, floatWidth));
3241 nscoord floats_cur =
3242 NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
3243 if (floats_cur > floats_done)
3244 floats_done = floats_cur;
3246 currentLine = NSCoordSaturatingAdd(currentLine, floats_done);
3248 floats.Clear();
3251 currentLine =
3252 NSCoordSaturatingSubtract(currentLine, trailingWhitespace, nscoord_MAX);
3253 prevLines = NS_MAX(prevLines, currentLine);
3254 currentLine = trailingWhitespace = 0;
3255 skipWhitespace = PR_TRUE;
3258 static void
3259 AddCoord(const nsStyleCoord& aStyle,
3260 nsIRenderingContext* aRenderingContext,
3261 nsIFrame* aFrame,
3262 nscoord* aCoord, float* aPercent,
3263 PRBool aClampNegativeToZero)
3265 switch (aStyle.GetUnit()) {
3266 case eStyleUnit_Coord: {
3267 NS_ASSERTION(!aClampNegativeToZero || aStyle.GetCoordValue() >= 0,
3268 "unexpected negative value");
3269 *aCoord += aStyle.GetCoordValue();
3270 return;
3272 case eStyleUnit_Percent: {
3273 NS_ASSERTION(!aClampNegativeToZero || aStyle.GetPercentValue() >= 0.0f,
3274 "unexpected negative value");
3275 *aPercent += aStyle.GetPercentValue();
3276 return;
3278 case eStyleUnit_Calc: {
3279 const nsStyleCoord::Calc *calc = aStyle.GetCalcValue();
3280 if (aClampNegativeToZero) {
3281 // This is far from ideal when one is negative and one is positive.
3282 *aCoord += NS_MAX(calc->mLength, 0);
3283 *aPercent += NS_MAX(calc->mPercent, 0.0f);
3284 } else {
3285 *aCoord += calc->mLength;
3286 *aPercent += calc->mPercent;
3288 return;
3290 default: {
3291 return;
3296 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
3297 nsFrame::IntrinsicWidthOffsets(nsIRenderingContext* aRenderingContext)
3299 IntrinsicWidthOffsetData result;
3301 const nsStyleMargin *styleMargin = GetStyleMargin();
3302 AddCoord(styleMargin->mMargin.GetLeft(), aRenderingContext, this,
3303 &result.hMargin, &result.hPctMargin, PR_FALSE);
3304 AddCoord(styleMargin->mMargin.GetRight(), aRenderingContext, this,
3305 &result.hMargin, &result.hPctMargin, PR_FALSE);
3307 const nsStylePadding *stylePadding = GetStylePadding();
3308 AddCoord(stylePadding->mPadding.GetLeft(), aRenderingContext, this,
3309 &result.hPadding, &result.hPctPadding, PR_TRUE);
3310 AddCoord(stylePadding->mPadding.GetRight(), aRenderingContext, this,
3311 &result.hPadding, &result.hPctPadding, PR_TRUE);
3313 const nsStyleBorder *styleBorder = GetStyleBorder();
3314 result.hBorder += styleBorder->GetActualBorderWidth(NS_SIDE_LEFT);
3315 result.hBorder += styleBorder->GetActualBorderWidth(NS_SIDE_RIGHT);
3317 const nsStyleDisplay *disp = GetStyleDisplay();
3318 if (IsThemed(disp)) {
3319 nsPresContext *presContext = PresContext();
3321 nsIntMargin border;
3322 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
3323 this, disp->mAppearance,
3324 &border);
3325 result.hBorder = presContext->DevPixelsToAppUnits(border.LeftRight());
3327 nsIntMargin padding;
3328 if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
3329 this, disp->mAppearance,
3330 &padding)) {
3331 result.hPadding = presContext->DevPixelsToAppUnits(padding.LeftRight());
3332 result.hPctPadding = 0;
3336 return result;
3339 /* virtual */ nsIFrame::IntrinsicSize
3340 nsFrame::GetIntrinsicSize()
3342 return IntrinsicSize(); // default is width/height set to eStyleUnit_None
3345 /* virtual */ nsSize
3346 nsFrame::GetIntrinsicRatio()
3348 return nsSize(0, 0);
3351 /* virtual */ nsSize
3352 nsFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
3353 nsSize aCBSize, nscoord aAvailableWidth,
3354 nsSize aMargin, nsSize aBorder, nsSize aPadding,
3355 PRBool aShrinkWrap)
3357 nsSize result = ComputeAutoSize(aRenderingContext, aCBSize, aAvailableWidth,
3358 aMargin, aBorder, aPadding, aShrinkWrap);
3359 nsSize boxSizingAdjust(0,0);
3360 const nsStylePosition *stylePos = GetStylePosition();
3362 switch (stylePos->mBoxSizing) {
3363 case NS_STYLE_BOX_SIZING_BORDER:
3364 boxSizingAdjust += aBorder;
3365 // fall through
3366 case NS_STYLE_BOX_SIZING_PADDING:
3367 boxSizingAdjust += aPadding;
3369 nscoord boxSizingToMarginEdgeWidth =
3370 aMargin.width + aBorder.width + aPadding.width - boxSizingAdjust.width;
3372 // Compute width
3374 if (stylePos->mWidth.GetUnit() != eStyleUnit_Auto) {
3375 result.width =
3376 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
3377 aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth,
3378 stylePos->mWidth);
3381 if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None) {
3382 nscoord maxWidth =
3383 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
3384 aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth,
3385 stylePos->mMaxWidth);
3386 if (maxWidth < result.width)
3387 result.width = maxWidth;
3390 nscoord minWidth =
3391 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
3392 aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth,
3393 stylePos->mMinWidth);
3394 if (minWidth > result.width)
3395 result.width = minWidth;
3397 // Compute height
3399 if (!nsLayoutUtils::IsAutoHeight(stylePos->mHeight, aCBSize.height)) {
3400 result.height =
3401 nsLayoutUtils::ComputeHeightValue(aCBSize.height, stylePos->mHeight) -
3402 boxSizingAdjust.height;
3405 if (result.height != NS_UNCONSTRAINEDSIZE) {
3406 if (!nsLayoutUtils::IsAutoHeight(stylePos->mMaxHeight, aCBSize.height)) {
3407 nscoord maxHeight =
3408 nsLayoutUtils::ComputeHeightValue(aCBSize.height, stylePos->mMaxHeight) -
3409 boxSizingAdjust.height;
3410 if (maxHeight < result.height)
3411 result.height = maxHeight;
3414 if (!nsLayoutUtils::IsAutoHeight(stylePos->mMinHeight, aCBSize.height)) {
3415 nscoord minHeight =
3416 nsLayoutUtils::ComputeHeightValue(aCBSize.height, stylePos->mMinHeight) -
3417 boxSizingAdjust.height;
3418 if (minHeight > result.height)
3419 result.height = minHeight;
3423 const nsStyleDisplay *disp = GetStyleDisplay();
3424 if (IsThemed(disp)) {
3425 nsIntSize widget(0, 0);
3426 PRBool canOverride = PR_TRUE;
3427 nsPresContext *presContext = PresContext();
3428 presContext->GetTheme()->
3429 GetMinimumWidgetSize(aRenderingContext, this, disp->mAppearance,
3430 &widget, &canOverride);
3432 nsSize size;
3433 size.width = presContext->DevPixelsToAppUnits(widget.width);
3434 size.height = presContext->DevPixelsToAppUnits(widget.height);
3436 // GMWS() returns border-box; we need content-box
3437 size.width -= aBorder.width + aPadding.width;
3438 size.height -= aBorder.height + aPadding.height;
3440 if (size.height > result.height || !canOverride)
3441 result.height = size.height;
3442 if (size.width > result.width || !canOverride)
3443 result.width = size.width;
3446 if (result.width < 0)
3447 result.width = 0;
3449 if (result.height < 0)
3450 result.height = 0;
3452 return result;
3455 nsRect
3456 nsIFrame::ComputeTightBounds(gfxContext* aContext) const
3458 return GetVisualOverflowRect();
3461 nsRect
3462 nsFrame::ComputeSimpleTightBounds(gfxContext* aContext) const
3464 if (GetStyleOutline()->GetOutlineStyle() != NS_STYLE_BORDER_STYLE_NONE ||
3465 HasBorder() || !GetStyleBackground()->IsTransparent() ||
3466 GetStyleDisplay()->mAppearance) {
3467 // Not necessarily tight, due to clipping, negative
3468 // outline-offset, and lots of other issues, but that's OK
3469 return GetVisualOverflowRect();
3472 nsRect r(0, 0, 0, 0);
3473 PRInt32 listIndex = 0;
3474 nsIAtom* childList = nsnull;
3475 do {
3476 nsIFrame* child = GetFirstChild(childList);
3477 while (child) {
3478 r.UnionRect(r, child->ComputeTightBounds(aContext) + child->GetPosition());
3479 child = child->GetNextSibling();
3481 childList = GetAdditionalChildListName(listIndex++);
3482 } while (childList);
3483 return r;
3486 /* virtual */ nsSize
3487 nsFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
3488 nsSize aCBSize, nscoord aAvailableWidth,
3489 nsSize aMargin, nsSize aBorder, nsSize aPadding,
3490 PRBool aShrinkWrap)
3492 // Use basic shrink-wrapping as a default implementation.
3493 nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE);
3495 // don't bother setting it if the result won't be used
3496 if (GetStylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) {
3497 nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width -
3498 aPadding.width;
3499 result.width = ShrinkWidthToFit(aRenderingContext, availBased);
3501 return result;
3504 nscoord
3505 nsFrame::ShrinkWidthToFit(nsIRenderingContext *aRenderingContext,
3506 nscoord aWidthInCB)
3508 nscoord result;
3509 nscoord minWidth = GetMinWidth(aRenderingContext);
3510 if (minWidth > aWidthInCB) {
3511 result = minWidth;
3512 } else {
3513 nscoord prefWidth = GetPrefWidth(aRenderingContext);
3514 if (prefWidth > aWidthInCB) {
3515 result = aWidthInCB;
3516 } else {
3517 result = prefWidth;
3520 return result;
3523 NS_IMETHODIMP
3524 nsFrame::WillReflow(nsPresContext* aPresContext)
3526 #ifdef DEBUG_dbaron_off
3527 // bug 81268
3528 NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
3529 "nsFrame::WillReflow: frame is already in reflow");
3530 #endif
3532 NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
3533 ("WillReflow: oldState=%x", mState));
3534 mState |= NS_FRAME_IN_REFLOW;
3535 return NS_OK;
3538 NS_IMETHODIMP
3539 nsFrame::DidReflow(nsPresContext* aPresContext,
3540 const nsHTMLReflowState* aReflowState,
3541 nsDidReflowStatus aStatus)
3543 NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
3544 ("nsFrame::DidReflow: aStatus=%d", aStatus));
3545 if (NS_FRAME_REFLOW_FINISHED == aStatus) {
3546 mState &= ~(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
3547 NS_FRAME_HAS_DIRTY_CHILDREN);
3550 // Notify the percent height observer if there is a percent height.
3551 // The observer may be able to initiate another reflow with a computed
3552 // height. This happens in the case where a table cell has no computed
3553 // height but can fabricate one when the cell height is known.
3554 if (aReflowState && aReflowState->mPercentHeightObserver &&
3555 !GetPrevInFlow()) {
3556 const nsStyleCoord &height = aReflowState->mStylePosition->mHeight;
3557 if (height.HasPercent()) {
3558 aReflowState->mPercentHeightObserver->NotifyPercentHeight(*aReflowState);
3562 return NS_OK;
3565 /* virtual */ PRBool
3566 nsFrame::CanContinueTextRun() const
3568 // By default, a frame will *not* allow a text run to be continued
3569 // through it.
3570 return PR_FALSE;
3573 NS_IMETHODIMP
3574 nsFrame::Reflow(nsPresContext* aPresContext,
3575 nsHTMLReflowMetrics& aDesiredSize,
3576 const nsHTMLReflowState& aReflowState,
3577 nsReflowStatus& aStatus)
3579 DO_GLOBAL_REFLOW_COUNT("nsFrame");
3580 aDesiredSize.width = 0;
3581 aDesiredSize.height = 0;
3582 aStatus = NS_FRAME_COMPLETE;
3583 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
3584 return NS_OK;
3587 NS_IMETHODIMP
3588 nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
3590 NS_NOTREACHED("should only be called for text frames");
3591 return NS_OK;
3594 NS_IMETHODIMP
3595 nsFrame::AttributeChanged(PRInt32 aNameSpaceID,
3596 nsIAtom* aAttribute,
3597 PRInt32 aModType)
3599 return NS_OK;
3602 // Flow member functions
3604 nsSplittableType
3605 nsFrame::GetSplittableType() const
3607 return NS_FRAME_NOT_SPLITTABLE;
3610 nsIFrame* nsFrame::GetPrevContinuation() const
3612 return nsnull;
3615 NS_IMETHODIMP nsFrame::SetPrevContinuation(nsIFrame* aPrevContinuation)
3617 // Ignore harmless requests to set it to NULL
3618 if (aPrevContinuation) {
3619 NS_ERROR("not splittable");
3620 return NS_ERROR_NOT_IMPLEMENTED;
3623 return NS_OK;
3626 nsIFrame* nsFrame::GetNextContinuation() const
3628 return nsnull;
3631 NS_IMETHODIMP nsFrame::SetNextContinuation(nsIFrame*)
3633 NS_ERROR("not splittable");
3634 return NS_ERROR_NOT_IMPLEMENTED;
3637 nsIFrame* nsFrame::GetPrevInFlowVirtual() const
3639 return nsnull;
3642 NS_IMETHODIMP nsFrame::SetPrevInFlow(nsIFrame* aPrevInFlow)
3644 // Ignore harmless requests to set it to NULL
3645 if (aPrevInFlow) {
3646 NS_ERROR("not splittable");
3647 return NS_ERROR_NOT_IMPLEMENTED;
3650 return NS_OK;
3653 nsIFrame* nsFrame::GetNextInFlowVirtual() const
3655 return nsnull;
3658 NS_IMETHODIMP nsFrame::SetNextInFlow(nsIFrame*)
3660 NS_ERROR("not splittable");
3661 return NS_ERROR_NOT_IMPLEMENTED;
3664 nsIFrame* nsIFrame::GetTailContinuation()
3666 nsIFrame* frame = this;
3667 while (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
3668 frame = frame->GetPrevContinuation();
3669 NS_ASSERTION(frame, "first continuation can't be overflow container");
3671 for (nsIFrame* next = frame->GetNextContinuation();
3672 next && !(next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
3673 next = frame->GetNextContinuation()) {
3674 frame = next;
3676 NS_POSTCONDITION(frame, "illegal state in continuation chain.");
3677 return frame;
3680 NS_DECLARE_FRAME_PROPERTY(ViewProperty, nsnull)
3682 // Associated view object
3683 nsIView*
3684 nsIFrame::GetView() const
3686 // Check the frame state bit and see if the frame has a view
3687 if (!(GetStateBits() & NS_FRAME_HAS_VIEW))
3688 return nsnull;
3690 // Check for a property on the frame
3691 void* value = Properties().Get(ViewProperty());
3692 NS_ASSERTION(value, "frame state bit was set but frame has no view");
3693 return static_cast<nsIView*>(value);
3696 /* virtual */ nsIView*
3697 nsIFrame::GetViewExternal() const
3699 return GetView();
3702 nsresult
3703 nsIFrame::SetView(nsIView* aView)
3705 if (aView) {
3706 aView->SetClientData(this);
3708 // Set a property on the frame
3709 Properties().Set(ViewProperty(), aView);
3711 // Set the frame state bit that says the frame has a view
3712 AddStateBits(NS_FRAME_HAS_VIEW);
3714 // Let all of the ancestors know they have a descendant with a view.
3715 for (nsIFrame* f = GetParent();
3716 f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
3717 f = f->GetParent())
3718 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
3721 return NS_OK;
3724 nsIFrame* nsIFrame::GetAncestorWithViewExternal() const
3726 return GetAncestorWithView();
3729 // Find the first geometric parent that has a view
3730 nsIFrame* nsIFrame::GetAncestorWithView() const
3732 for (nsIFrame* f = mParent; nsnull != f; f = f->GetParent()) {
3733 if (f->HasView()) {
3734 return f;
3737 return nsnull;
3740 // virtual
3741 nsPoint nsIFrame::GetOffsetToExternal(const nsIFrame* aOther) const
3743 return GetOffsetTo(aOther);
3746 nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const
3748 NS_PRECONDITION(aOther,
3749 "Must have frame for destination coordinate system!");
3751 NS_ASSERTION(PresContext() == aOther->PresContext(),
3752 "GetOffsetTo called on frames in different documents");
3754 nsPoint offset(0, 0);
3755 const nsIFrame* f;
3756 for (f = this; f != aOther && f; f = f->GetParent()) {
3757 offset += f->GetPosition();
3760 if (f != aOther) {
3761 // Looks like aOther wasn't an ancestor of |this|. So now we have
3762 // the root-frame-relative position of |this| in |offset|. Convert back
3763 // to the coordinates of aOther
3764 while (aOther) {
3765 offset -= aOther->GetPosition();
3766 aOther = aOther->GetParent();
3770 return offset;
3773 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const
3775 return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
3778 nsPoint
3779 nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther, const PRInt32 aAPD) const
3781 NS_PRECONDITION(aOther,
3782 "Must have frame for destination coordinate system!");
3783 NS_ASSERTION(PresContext()->GetRootPresContext() ==
3784 aOther->PresContext()->GetRootPresContext(),
3785 "trying to get the offset between frames in different document "
3786 "hierarchies?");
3788 const nsIFrame* root = nsnull;
3789 // offset will hold the final offset
3790 // docOffset holds the currently accumulated offset at the current APD, it
3791 // will be converted and added to offset when the current APD changes.
3792 nsPoint offset(0, 0), docOffset(0, 0);
3793 const nsIFrame* f = this;
3794 PRInt32 currAPD = PresContext()->AppUnitsPerDevPixel();
3795 while (f && f != aOther) {
3796 docOffset += f->GetPosition();
3797 nsIFrame* parent = f->GetParent();
3798 if (parent) {
3799 f = parent;
3800 } else {
3801 nsPoint newOffset(0, 0);
3802 root = f;
3803 f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset);
3804 PRInt32 newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
3805 if (!f || newAPD != currAPD) {
3806 // Convert docOffset to the right APD and add it to offset.
3807 offset += docOffset.ConvertAppUnits(currAPD, aAPD);
3808 docOffset.x = docOffset.y = 0;
3810 currAPD = newAPD;
3811 docOffset += newOffset;
3814 if (f == aOther) {
3815 offset += docOffset.ConvertAppUnits(currAPD, aAPD);
3816 } else {
3817 // Looks like aOther wasn't an ancestor of |this|. So now we have
3818 // the root-document-relative position of |this| in |offset|. Subtract the
3819 // root-document-relative position of |aOther| from |offset|.
3820 // This call won't try to recurse again because root is an ancestor of
3821 // aOther.
3822 nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
3823 offset -= negOffset;
3826 return offset;
3829 // virtual
3830 nsIntRect nsIFrame::GetScreenRectExternal() const
3832 return GetScreenRect();
3835 nsIntRect nsIFrame::GetScreenRect() const
3837 return GetScreenRectInAppUnits().ToNearestPixels(PresContext()->AppUnitsPerCSSPixel());
3840 // virtual
3841 nsRect nsIFrame::GetScreenRectInAppUnitsExternal() const
3843 return GetScreenRectInAppUnits();
3846 nsRect nsIFrame::GetScreenRectInAppUnits() const
3848 nsPresContext* presContext = PresContext();
3849 nsIFrame* rootFrame =
3850 presContext->PresShell()->FrameManager()->GetRootFrame();
3851 nsPoint rootScreenPos(0, 0);
3852 nsPoint rootFrameOffsetInParent(0, 0);
3853 nsIFrame* rootFrameParent =
3854 nsLayoutUtils::GetCrossDocParentFrame(rootFrame, &rootFrameOffsetInParent);
3855 if (rootFrameParent) {
3856 nsRect parentScreenRectAppUnits = rootFrameParent->GetScreenRectInAppUnits();
3857 nsPresContext* parentPresContext = rootFrameParent->PresContext();
3858 double parentScale = double(presContext->AppUnitsPerDevPixel())/
3859 parentPresContext->AppUnitsPerDevPixel();
3860 nsPoint rootPt = parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
3861 rootScreenPos.x = NS_round(parentScale*rootPt.x);
3862 rootScreenPos.y = NS_round(parentScale*rootPt.y);
3863 } else {
3864 nsCOMPtr<nsIWidget> rootWidget;
3865 presContext->PresShell()->GetViewManager()->GetRootWidget(getter_AddRefs(rootWidget));
3866 if (rootWidget) {
3867 nsIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
3868 rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
3869 rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
3873 return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
3876 // Returns the offset from this frame to the closest geometric parent that
3877 // has a view. Also returns the containing view or null in case of error
3878 NS_IMETHODIMP nsFrame::GetOffsetFromView(nsPoint& aOffset,
3879 nsIView** aView) const
3881 NS_PRECONDITION(nsnull != aView, "null OUT parameter pointer");
3882 nsIFrame* frame = (nsIFrame*)this;
3884 *aView = nsnull;
3885 aOffset.MoveTo(0, 0);
3886 do {
3887 aOffset += frame->GetPosition();
3888 frame = frame->GetParent();
3889 } while (frame && !frame->HasView());
3890 if (frame)
3891 *aView = frame->GetView();
3892 return NS_OK;
3895 /* virtual */ PRBool
3896 nsIFrame::AreAncestorViewsVisible() const
3898 const nsIFrame* parent;
3899 for (const nsIFrame* f = this; f; f = parent) {
3900 nsIView* view = f->GetView();
3901 if (view && view->GetVisibility() == nsViewVisibility_kHide) {
3902 return PR_FALSE;
3904 parent = f->GetParent();
3905 if (!parent) {
3906 parent = nsLayoutUtils::GetCrossDocParentFrame(f);
3907 if (parent && parent->PresContext()->IsChrome() &&
3908 !f->PresContext()->IsChrome()) {
3909 // Don't look beyond chrome/content boundary ... if the chrome
3910 // has hidden a content docshell, the content in the content
3911 // docshell shouldn't be affected (e.g. it should remain focusable).
3912 break;
3916 return PR_TRUE;
3919 nsIWidget*
3920 nsIFrame::GetNearestWidget() const
3922 return GetClosestView()->GetNearestWidget(nsnull);
3925 nsIWidget*
3926 nsIFrame::GetNearestWidget(nsPoint& aOffset) const
3928 nsPoint offsetToView;
3929 nsPoint offsetToWidget;
3930 nsIWidget* widget =
3931 GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
3932 aOffset = offsetToView + offsetToWidget;
3933 return widget;
3936 nsIAtom*
3937 nsFrame::GetType() const
3939 return nsnull;
3942 PRBool
3943 nsIFrame::IsLeaf() const
3945 return PR_TRUE;
3948 void
3949 nsIFrame::InvalidateLayer(const nsRect& aDamageRect, PRUint32 aDisplayItemKey)
3951 NS_ASSERTION(aDisplayItemKey > 0, "Need a key");
3953 if (!FrameLayerBuilder::HasDedicatedLayer(this, aDisplayItemKey)) {
3954 Invalidate(aDamageRect);
3955 return;
3958 PRUint32 flags = INVALIDATE_NO_THEBES_LAYERS;
3959 if (aDisplayItemKey == nsDisplayItem::TYPE_VIDEO ||
3960 aDisplayItemKey == nsDisplayItem::TYPE_PLUGIN) {
3961 flags = INVALIDATE_NO_UPDATE_LAYER_TREE;
3964 InvalidateWithFlags(aDamageRect, flags);
3967 void
3968 nsIFrame::InvalidateTransformLayer()
3970 NS_ASSERTION(mParent, "How can a viewport frame have a transform?");
3972 PRBool hasLayer =
3973 FrameLayerBuilder::HasDedicatedLayer(this, nsDisplayItem::TYPE_TRANSFORM);
3974 // Invalidate post-transform area in the parent. We have to invalidate
3975 // in the parent because our transform style may have changed from what was
3976 // used to paint this frame.
3977 // It's OK to bypass the SVG effects processing and other processing
3978 // performed if we called this->InvalidateWithFlags, because those effects
3979 // are performed before applying transforms.
3980 mParent->InvalidateInternal(GetVisualOverflowRect() + GetPosition(),
3981 0, 0, this,
3982 hasLayer ? INVALIDATE_NO_THEBES_LAYERS : 0);
3985 class LayerActivity {
3986 public:
3987 LayerActivity(nsIFrame* aFrame) : mFrame(aFrame) {}
3988 ~LayerActivity();
3989 nsExpirationState* GetExpirationState() { return &mState; }
3991 nsIFrame* mFrame;
3992 nsExpirationState mState;
3995 class LayerActivityTracker : public nsExpirationTracker<LayerActivity,4> {
3996 public:
3997 // 75-100ms is a good timeout period. We use 4 generations of 25ms each.
3998 enum { GENERATION_MS = 100 };
3999 LayerActivityTracker()
4000 : nsExpirationTracker<LayerActivity,4>(GENERATION_MS) {}
4001 ~LayerActivityTracker() {
4002 AgeAllGenerations();
4005 virtual void NotifyExpired(LayerActivity* aObject);
4008 static LayerActivityTracker* gLayerActivityTracker = nsnull;
4010 LayerActivity::~LayerActivity()
4012 if (mFrame) {
4013 NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker");
4014 gLayerActivityTracker->RemoveObject(this);
4018 static void DestroyLayerActivity(void* aPropertyValue)
4020 delete static_cast<LayerActivity*>(aPropertyValue);
4023 NS_DECLARE_FRAME_PROPERTY(LayerActivityProperty, DestroyLayerActivity)
4025 void
4026 LayerActivityTracker::NotifyExpired(LayerActivity* aObject)
4028 RemoveObject(aObject);
4030 nsIFrame* f = aObject->mFrame;
4031 aObject->mFrame = nsnull;
4032 f->Properties().Delete(LayerActivityProperty());
4033 f->InvalidateFrameSubtree();
4036 void
4037 nsIFrame::MarkLayersActive()
4039 FrameProperties properties = Properties();
4040 LayerActivity* layerActivity =
4041 static_cast<LayerActivity*>(properties.Get(LayerActivityProperty()));
4042 if (layerActivity) {
4043 gLayerActivityTracker->MarkUsed(layerActivity);
4044 } else {
4045 if (!gLayerActivityTracker) {
4046 gLayerActivityTracker = new LayerActivityTracker();
4048 layerActivity = new LayerActivity(this);
4049 gLayerActivityTracker->AddObject(layerActivity);
4050 properties.Set(LayerActivityProperty(), layerActivity);
4054 PRBool
4055 nsIFrame::AreLayersMarkedActive()
4057 return Properties().Get(LayerActivityProperty()) != nsnull;
4060 /* static */ void
4061 nsFrame::ShutdownLayerActivityTimer()
4063 delete gLayerActivityTracker;
4064 gLayerActivityTracker = nsnull;
4067 void
4068 nsIFrame::InvalidateWithFlags(const nsRect& aDamageRect, PRUint32 aFlags)
4070 if (aDamageRect.IsEmpty()) {
4071 return;
4074 // Don't allow invalidates to do anything when
4075 // painting is suppressed.
4076 nsIPresShell *shell = PresContext()->GetPresShell();
4077 if (shell) {
4078 if (shell->IsPaintingSuppressed())
4079 return;
4082 InvalidateInternal(aDamageRect, 0, 0, nsnull, aFlags);
4086 * Helper function that funnels an InvalidateInternal request up to the
4087 * parent. This function is used so that if MOZ_SVG is not defined, we still
4088 * have unified control paths in the InvalidateInternal chain.
4090 * @param aDamageRect The rect to invalidate.
4091 * @param aX The x offset from the origin of this frame to the rectangle.
4092 * @param aY The y offset from the origin of this frame to the rectangle.
4093 * @param aImmediate Whether to redraw immediately.
4094 * @return None, though this funnels the request up to the parent frame.
4096 void
4097 nsIFrame::InvalidateInternalAfterResize(const nsRect& aDamageRect, nscoord aX,
4098 nscoord aY, PRUint32 aFlags)
4100 /* If we're a transformed frame, then we need to apply our transform to the
4101 * damage rectangle so that the redraw correctly redraws the transformed
4102 * region. We're moved over aX and aY from our origin, but since this aX
4103 * and aY is contained within our border, we need to scoot back by -aX and
4104 * -aY to get back to the origin of the transform.
4106 * There's one more problem, though, and that's that we don't know what
4107 * coordinate space this rectangle is in. Sometimes it's in the local
4108 * coordinate space for the frame, and sometimes its in the transformed
4109 * coordinate space. If we get it wrong, we'll display incorrectly. Until I
4110 * find a better fix for this problem, we'll invalidate the union of the two
4111 * rectangles (original rectangle and transformed rectangle). At least one of
4112 * these will be correct.
4114 * See bug #452496 for more details.
4116 if ((mState & NS_FRAME_HAS_CONTAINER_LAYER) &&
4117 !(aFlags & INVALIDATE_NO_THEBES_LAYERS)) {
4118 // XXX for now I'm going to assume this is in the local coordinate space
4119 // This only matters for frames with transforms and retained layers,
4120 // which can't happen right now since transforms trigger fallback
4121 // rendering and the display items that trigger layers are nested inside
4122 // the nsDisplayTransform
4123 // XXX need to set INVALIDATE_NO_THEBES_LAYERS for certain kinds of
4124 // invalidation, e.g. video update, 'opacity' change
4125 FrameLayerBuilder::InvalidateThebesLayerContents(this,
4126 aDamageRect + nsPoint(aX, aY));
4127 // Don't need to invalidate any more Thebes layers
4128 aFlags |= INVALIDATE_NO_THEBES_LAYERS;
4129 if (aFlags & INVALIDATE_ONLY_THEBES_LAYERS) {
4130 return;
4133 if (IsTransformed()) {
4134 nsRect newDamageRect;
4135 newDamageRect.UnionRect(nsDisplayTransform::TransformRect
4136 (aDamageRect, this, nsPoint(-aX, -aY)), aDamageRect);
4137 GetParent()->
4138 InvalidateInternal(newDamageRect, aX + mRect.x, aY + mRect.y, this,
4139 aFlags);
4141 else
4142 GetParent()->
4143 InvalidateInternal(aDamageRect, aX + mRect.x, aY + mRect.y, this, aFlags);
4146 void
4147 nsIFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY,
4148 nsIFrame* aForChild, PRUint32 aFlags)
4150 #ifdef MOZ_SVG
4151 nsSVGEffects::InvalidateDirectRenderingObservers(this);
4152 if (nsSVGIntegrationUtils::UsingEffectsForFrame(this)) {
4153 nsRect r = nsSVGIntegrationUtils::GetInvalidAreaForChangedSource(this,
4154 aDamageRect + nsPoint(aX, aY));
4155 /* Rectangle is now in our own local space, so aX and aY are effectively
4156 * zero. Thus we'll pretend that the entire time this was in our own
4157 * local coordinate space and do any remaining processing.
4159 InvalidateInternalAfterResize(r, 0, 0, aFlags);
4160 return;
4162 #endif
4164 InvalidateInternalAfterResize(aDamageRect, aX, aY, aFlags);
4167 gfxMatrix
4168 nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor)
4170 NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!");
4172 /* If we're transformed, the matrix will be relative to our
4173 * cross-doc parent frame.
4175 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this);
4177 /* If we're transformed, we want to hand back the combination
4178 * transform/translate matrix that will apply our current transform, then
4179 * shift us to our parent.
4181 if (IsTransformed()) {
4182 /* Compute the delta to the parent, which we need because we are converting
4183 * coordinates to our parent.
4185 NS_ASSERTION(*aOutAncestor, "Cannot transform the viewport frame!");
4186 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
4187 PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel();
4189 gfxMatrix result =
4190 nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0),
4191 scaleFactor);
4192 /* Combine the raw transform with a translation to our parent. */
4193 result *= gfxMatrix().Translate
4194 (gfxPoint(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
4195 NSAppUnitsToFloatPixels(delta.y, scaleFactor)));
4196 return result;
4199 /* Otherwise, we're not transformed. In that case, we'll walk up the frame
4200 * tree until we either hit the root frame or something that may be
4201 * transformed. We'll then change coordinates into that frame, since we're
4202 * guaranteed that nothing in-between can be transformed. First, however,
4203 * we have to check to see if we have a parent. If not, we'll set the
4204 * outparam to null (indicating that there's nothing left) and will hand back
4205 * the identity matrix.
4207 if (!*aOutAncestor)
4208 return gfxMatrix();
4210 /* Keep iterating while the frame can't possibly be transformed. */
4211 while (!(*aOutAncestor)->IsTransformed()) {
4212 /* If no parent, stop iterating. Otherwise, update the ancestor. */
4213 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
4214 if (!parent)
4215 break;
4217 *aOutAncestor = parent;
4220 NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?");
4222 /* Translate from this frame to our ancestor, if it exists. That's the
4223 * entire transform, so we're done.
4225 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
4226 PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel();
4227 return gfxMatrix().Translate
4228 (gfxPoint(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
4229 NSAppUnitsToFloatPixels(delta.y, scaleFactor)));
4232 void
4233 nsIFrame::InvalidateRectDifference(const nsRect& aR1, const nsRect& aR2)
4235 nsRect sizeHStrip, sizeVStrip;
4236 nsLayoutUtils::GetRectDifferenceStrips(aR1, aR2, &sizeHStrip, &sizeVStrip);
4237 Invalidate(sizeVStrip);
4238 Invalidate(sizeHStrip);
4241 void
4242 nsIFrame::InvalidateFrameSubtree()
4244 Invalidate(GetVisualOverflowRectRelativeToSelf());
4245 FrameLayerBuilder::InvalidateThebesLayersInSubtree(this);
4248 void
4249 nsIFrame::InvalidateOverflowRect()
4251 Invalidate(GetVisualOverflowRectRelativeToSelf());
4254 NS_DECLARE_FRAME_PROPERTY(DeferInvalidatesProperty, nsIFrame::DestroyRegion)
4256 void
4257 nsIFrame::InvalidateRoot(const nsRect& aDamageRect, PRUint32 aFlags)
4259 NS_ASSERTION(nsLayoutUtils::GetDisplayRootFrame(this) == this,
4260 "Can only call this on display roots");
4262 if ((mState & NS_FRAME_HAS_CONTAINER_LAYER) &&
4263 !(aFlags & INVALIDATE_NO_THEBES_LAYERS)) {
4264 FrameLayerBuilder::InvalidateThebesLayerContents(this, aDamageRect);
4265 if (aFlags & INVALIDATE_ONLY_THEBES_LAYERS) {
4266 return;
4270 PRUint32 flags =
4271 (aFlags & INVALIDATE_IMMEDIATE) ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_NO_SYNC;
4273 nsRect rect = aDamageRect;
4274 nsRegion* excludeRegion = static_cast<nsRegion*>
4275 (Properties().Get(DeferInvalidatesProperty()));
4276 if (excludeRegion) {
4277 flags = NS_VMREFRESH_DEFERRED;
4279 if (aFlags & INVALIDATE_EXCLUDE_CURRENT_PAINT) {
4280 nsRegion r;
4281 r.Sub(rect, *excludeRegion);
4282 if (r.IsEmpty())
4283 return;
4284 rect = r.GetBounds();
4288 if (!(aFlags & INVALIDATE_NO_UPDATE_LAYER_TREE)) {
4289 AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
4292 nsIView* view = GetView();
4293 NS_ASSERTION(view, "This can only be called on frames with views");
4294 view->GetViewManager()->UpdateViewNoSuppression(view, rect, flags);
4297 void
4298 nsIFrame::BeginDeferringInvalidatesForDisplayRoot(const nsRegion& aExcludeRegion)
4300 NS_ASSERTION(nsLayoutUtils::GetDisplayRootFrame(this) == this,
4301 "Can only call this on display roots");
4302 Properties().Set(DeferInvalidatesProperty(), new nsRegion(aExcludeRegion));
4305 void
4306 nsIFrame::EndDeferringInvalidatesForDisplayRoot()
4308 NS_ASSERTION(nsLayoutUtils::GetDisplayRootFrame(this) == this,
4309 "Can only call this on display roots");
4310 Properties().Delete(DeferInvalidatesProperty());
4314 * @param aAnyOutlineOrEffects set to true if this frame has any
4315 * outline, SVG effects or box shadows that mean we need to invalidate
4316 * the whole overflow area if the frame's size changes.
4318 static nsRect
4319 ComputeOutlineAndEffectsRect(nsIFrame* aFrame, PRBool* aAnyOutlineOrEffects,
4320 const nsRect& aOverflowRect,
4321 const nsSize& aNewSize,
4322 PRBool aStoreRectProperties) {
4323 nsRect r = aOverflowRect;
4324 *aAnyOutlineOrEffects = PR_FALSE;
4326 // box-shadow
4327 nsCSSShadowArray* boxShadows = aFrame->GetStyleBorder()->mBoxShadow;
4328 if (boxShadows) {
4329 nsRect shadows;
4330 PRInt32 A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
4331 for (PRUint32 i = 0; i < boxShadows->Length(); ++i) {
4332 nsRect tmpRect(nsPoint(0, 0), aNewSize);
4333 nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
4335 // inset shadows are never painted outside the frame
4336 if (shadow->mInset)
4337 continue;
4339 tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset));
4340 tmpRect.Inflate(shadow->mSpread, shadow->mSpread);
4341 tmpRect.Inflate(
4342 nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D));
4344 shadows.UnionRect(shadows, tmpRect);
4346 r.UnionRect(r, shadows);
4347 *aAnyOutlineOrEffects = PR_TRUE;
4350 const nsStyleOutline* outline = aFrame->GetStyleOutline();
4351 PRUint8 outlineStyle = outline->GetOutlineStyle();
4352 if (outlineStyle != NS_STYLE_BORDER_STYLE_NONE) {
4353 nscoord width;
4354 #ifdef DEBUG
4355 PRBool result =
4356 #endif
4357 outline->GetOutlineWidth(width);
4358 NS_ASSERTION(result, "GetOutlineWidth had no cached outline width");
4359 if (width > 0) {
4360 if (aStoreRectProperties) {
4361 aFrame->Properties().
4362 Set(nsIFrame::OutlineInnerRectProperty(), new nsRect(r));
4365 nscoord offset = outline->mOutlineOffset;
4366 nscoord inflateBy = NS_MAX(width + offset, 0);
4367 // FIXME (bug 599652): We probably want outline to be drawn around
4368 // something smaller than the visual overflow rect (perhaps the
4369 // scrollable overflow rect is correct). When we change that, we
4370 // need to keep this code (and the storing of properties just
4371 // above) in sync with GetOutlineInnerRect in nsCSSRendering.cpp.
4372 r.Inflate(inflateBy, inflateBy);
4373 *aAnyOutlineOrEffects = PR_TRUE;
4377 // Note that we don't remove the outlineInnerRect if a frame loses outline
4378 // style. That would require an extra property lookup for every frame,
4379 // or a new frame state bit to track whether a property had been stored,
4380 // or something like that. It's not worth doing that here. At most it's
4381 // only one heap-allocated rect per frame and it will be cleaned up when
4382 // the frame dies.
4384 #ifdef MOZ_SVG
4385 if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
4386 *aAnyOutlineOrEffects = PR_TRUE;
4387 if (aStoreRectProperties) {
4388 aFrame->Properties().
4389 Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
4391 r = nsSVGIntegrationUtils::ComputeFrameEffectsRect(aFrame, r);
4393 #endif
4395 return r;
4398 nsPoint
4399 nsIFrame::GetRelativeOffset(const nsStyleDisplay* aDisplay) const
4401 if (!aDisplay || NS_STYLE_POSITION_RELATIVE == aDisplay->mPosition) {
4402 nsPoint *offsets = static_cast<nsPoint*>
4403 (Properties().Get(ComputedOffsetProperty()));
4404 if (offsets) {
4405 return *offsets;
4408 return nsPoint(0,0);
4411 nsRect
4412 nsIFrame::GetOverflowRect(nsOverflowType aType) const
4414 NS_ABORT_IF_FALSE(aType == eVisualOverflow || aType == eScrollableOverflow,
4415 "unexpected type");
4417 // Note that in some cases the overflow area might not have been
4418 // updated (yet) to reflect any outline set on the frame or the area
4419 // of child frames. That's OK because any reflow that updates these
4420 // areas will invalidate the appropriate area, so any (mis)uses of
4421 // this method will be fixed up.
4423 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
4424 // there is an overflow rect, and it's not stored as deltas but as
4425 // a separately-allocated rect
4426 return static_cast<nsOverflowAreas*>(const_cast<nsIFrame*>(this)->
4427 GetOverflowAreasProperty())->Overflow(aType);
4430 if (aType == eVisualOverflow &&
4431 mOverflow.mType != NS_FRAME_OVERFLOW_NONE) {
4432 return GetVisualOverflowFromDeltas();
4435 return nsRect(nsPoint(0, 0), GetSize());
4438 nsOverflowAreas
4439 nsIFrame::GetOverflowAreas() const
4441 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
4442 // there is an overflow rect, and it's not stored as deltas but as
4443 // a separately-allocated rect
4444 return *const_cast<nsIFrame*>(this)->GetOverflowAreasProperty();
4447 return nsOverflowAreas(GetVisualOverflowFromDeltas(),
4448 nsRect(nsPoint(0, 0), GetSize()));
4451 nsRect
4452 nsIFrame::GetScrollableOverflowRectRelativeToParent() const
4454 return GetScrollableOverflowRect() + mRect.TopLeft();
4457 nsRect
4458 nsIFrame::GetVisualOverflowRectRelativeToSelf() const
4460 if (IsTransformed()) {
4461 nsRect* preTransformBBox = static_cast<nsRect*>
4462 (Properties().Get(PreTransformBBoxProperty()));
4463 if (preTransformBBox)
4464 return *preTransformBBox;
4466 return GetVisualOverflowRect();
4469 void
4470 nsFrame::CheckInvalidateSizeChange(nsHTMLReflowMetrics& aNewDesiredSize)
4472 nsIFrame::CheckInvalidateSizeChange(mRect, GetVisualOverflowRect(),
4473 nsSize(aNewDesiredSize.width, aNewDesiredSize.height));
4476 static void
4477 InvalidateRectForFrameSizeChange(nsIFrame* aFrame, const nsRect& aRect)
4479 nsStyleContext *bgSC;
4480 if (!nsCSSRendering::FindBackground(aFrame->PresContext(), aFrame, &bgSC)) {
4481 nsIFrame* rootFrame =
4482 aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame();
4483 rootFrame->Invalidate(nsRect(nsPoint(0, 0), rootFrame->GetSize()));
4486 aFrame->Invalidate(aRect);
4489 void
4490 nsIFrame::CheckInvalidateSizeChange(const nsRect& aOldRect,
4491 const nsRect& aOldVisualOverflowRect,
4492 const nsSize& aNewDesiredSize)
4494 if (aNewDesiredSize == aOldRect.Size())
4495 return;
4497 // Below, we invalidate the old frame area (or, in the case of
4498 // outline, combined area) if the outline, border or background
4499 // settings indicate that something other than the difference
4500 // between the old and new areas needs to be painted. We are
4501 // assuming that the difference between the old and new areas will
4502 // be invalidated by some other means. That also means invalidating
4503 // the old frame area is the same as invalidating the new frame area
4504 // (since in either case the UNION of old and new areas will be
4505 // invalidated)
4507 // We use InvalidateRectForFrameSizeChange throughout this method, even
4508 // though root-invalidation is technically only needed in the case where
4509 // layer.RenderingMightDependOnFrameSize(). This allows us to simplify the
4510 // code somewhat and return immediately after invalidation in the earlier
4511 // cases.
4513 // Invalidate the entire old frame+outline if the frame has an outline
4514 PRBool anyOutlineOrEffects;
4515 nsRect r = ComputeOutlineAndEffectsRect(this, &anyOutlineOrEffects,
4516 aOldVisualOverflowRect,
4517 aNewDesiredSize,
4518 PR_FALSE);
4519 if (anyOutlineOrEffects) {
4520 r.UnionRect(aOldVisualOverflowRect, r);
4521 InvalidateRectForFrameSizeChange(this, r);
4522 return;
4525 // Invalidate the old frame border box if the frame has borders. Those
4526 // borders may be moving.
4527 const nsStyleBorder* border = GetStyleBorder();
4528 NS_FOR_CSS_SIDES(side) {
4529 if (border->GetActualBorderWidth(side) != 0) {
4530 if ((side == NS_SIDE_LEFT || side == NS_SIDE_TOP) &&
4531 !nsLayoutUtils::HasNonZeroCornerOnSide(border->mBorderRadius, side) &&
4532 !border->GetBorderImage() &&
4533 border->GetBorderStyle(side) == NS_STYLE_BORDER_STYLE_SOLID) {
4534 // We also need to be sure that the bottom-left or top-right
4535 // corner is simple. For example, if the bottom or right border
4536 // has a different color, we would need to invalidate the corner
4537 // area. But that's OK because if there is a right or bottom border,
4538 // we'll invalidate the entire border-box here anyway.
4539 continue;
4541 InvalidateRectForFrameSizeChange(this, nsRect(0, 0, aOldRect.width, aOldRect.height));
4542 return;
4546 const nsStyleBackground *bg = GetStyleBackground();
4547 if (!bg->IsTransparent()) {
4548 // Invalidate the old frame background if the frame has a background
4549 // whose position depends on the size of the frame
4550 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
4551 const nsStyleBackground::Layer &layer = bg->mLayers[i];
4552 if (layer.RenderingMightDependOnFrameSize()) {
4553 InvalidateRectForFrameSizeChange(this, nsRect(0, 0, aOldRect.width, aOldRect.height));
4554 return;
4558 // Invalidate the old frame background if the frame has a background
4559 // that is being clipped by border-radius, since the old or new area
4560 // clipped off by the radius is not necessarily in the area that has
4561 // already been invalidated (even if only the top-left corner has a
4562 // border radius).
4563 if (nsLayoutUtils::HasNonZeroCorner(border->mBorderRadius)) {
4564 InvalidateRectForFrameSizeChange(this, nsRect(0, 0, aOldRect.width, aOldRect.height));
4565 return;
4570 // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus
4571 // 4 for the frames above the document's frames:
4572 // the Viewport, GFXScroll, ScrollPort, and Canvas
4573 #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH+4)
4575 PRBool
4576 nsFrame::IsFrameTreeTooDeep(const nsHTMLReflowState& aReflowState,
4577 nsHTMLReflowMetrics& aMetrics,
4578 nsReflowStatus& aStatus)
4580 if (aReflowState.mReflowDepth > MAX_FRAME_DEPTH) {
4581 NS_WARNING("frame tree too deep; setting zero size and returning");
4582 mState |= NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
4583 ClearOverflowRects();
4584 aMetrics.width = 0;
4585 aMetrics.height = 0;
4586 aMetrics.ascent = 0;
4587 aMetrics.mCarriedOutBottomMargin.Zero();
4588 aMetrics.mOverflowAreas.Clear();
4590 if (GetNextInFlow()) {
4591 // Reflow depth might vary between reflows, so we might have
4592 // successfully reflowed and split this frame before. If so, we
4593 // shouldn't delete its continuations.
4594 aStatus = NS_FRAME_NOT_COMPLETE;
4595 } else {
4596 aStatus = NS_FRAME_COMPLETE;
4599 return PR_TRUE;
4601 mState &= ~NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
4602 return PR_FALSE;
4605 /* virtual */ PRBool nsFrame::IsContainingBlock() const
4607 const nsStyleDisplay* display = GetStyleDisplay();
4609 // Absolute positioning causes |display->mDisplay| to be set to block,
4610 // if needed.
4611 return display->mDisplay == NS_STYLE_DISPLAY_BLOCK ||
4612 display->mDisplay == NS_STYLE_DISPLAY_INLINE_BLOCK ||
4613 display->mDisplay == NS_STYLE_DISPLAY_LIST_ITEM ||
4614 display->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL;
4617 #ifdef NS_DEBUG
4619 PRInt32 nsFrame::ContentIndexInContainer(const nsIFrame* aFrame)
4621 PRInt32 result = -1;
4623 nsIContent* content = aFrame->GetContent();
4624 if (content) {
4625 nsIContent* parentContent = content->GetParent();
4626 if (parentContent) {
4627 result = parentContent->IndexOf(content);
4631 return result;
4635 * List a frame tree to stdout. Meant to be called from gdb.
4637 void
4638 DebugListFrameTree(nsIFrame* aFrame)
4640 ((nsFrame*)aFrame)->List(stdout, 0);
4644 // Debugging
4645 NS_IMETHODIMP
4646 nsFrame::List(FILE* out, PRInt32 aIndent) const
4648 IndentBy(out, aIndent);
4649 ListTag(out);
4650 #ifdef DEBUG_waterson
4651 fprintf(out, " [parent=%p]", static_cast<void*>(mParent));
4652 #endif
4653 if (HasView()) {
4654 fprintf(out, " [view=%p]", static_cast<void*>(GetView()));
4656 fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
4657 if (0 != mState) {
4658 fprintf(out, " [state=%016llx]", mState);
4660 nsIFrame* prevInFlow = GetPrevInFlow();
4661 nsIFrame* nextInFlow = GetNextInFlow();
4662 if (nsnull != prevInFlow) {
4663 fprintf(out, " prev-in-flow=%p", static_cast<void*>(prevInFlow));
4665 if (nsnull != nextInFlow) {
4666 fprintf(out, " next-in-flow=%p", static_cast<void*>(nextInFlow));
4668 fprintf(out, " [content=%p]", static_cast<void*>(mContent));
4669 nsFrame* f = const_cast<nsFrame*>(this);
4670 if (f->HasOverflowAreas()) {
4671 nsRect overflowArea = f->GetVisualOverflowRect();
4672 fprintf(out, " [vis-overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
4673 overflowArea.width, overflowArea.height);
4674 overflowArea = f->GetScrollableOverflowRect();
4675 fprintf(out, " [scr-overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
4676 overflowArea.width, overflowArea.height);
4678 fprintf(out, " [sc=%p]", static_cast<void*>(mStyleContext));
4679 fputs("\n", out);
4680 return NS_OK;
4683 NS_IMETHODIMP
4684 nsFrame::GetFrameName(nsAString& aResult) const
4686 return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult);
4689 NS_IMETHODIMP_(nsFrameState)
4690 nsFrame::GetDebugStateBits() const
4692 // We'll ignore these flags for the purposes of comparing frame state:
4694 // NS_FRAME_EXTERNAL_REFERENCE
4695 // because this is set by the event state manager or the
4696 // caret code when a frame is focused. Depending on whether
4697 // or not the regression tests are run as the focused window
4698 // will make this value vary randomly.
4699 #define IRRELEVANT_FRAME_STATE_FLAGS NS_FRAME_EXTERNAL_REFERENCE
4701 #define FRAME_STATE_MASK (~(IRRELEVANT_FRAME_STATE_FLAGS))
4703 return GetStateBits() & FRAME_STATE_MASK;
4706 nsresult
4707 nsFrame::MakeFrameName(const nsAString& aType, nsAString& aResult) const
4709 aResult = aType;
4710 if (mContent && !mContent->IsNodeOfType(nsINode::eTEXT)) {
4711 nsAutoString buf;
4712 mContent->Tag()->ToString(buf);
4713 aResult.Append(NS_LITERAL_STRING("(") + buf + NS_LITERAL_STRING(")"));
4715 char buf[40];
4716 PR_snprintf(buf, sizeof(buf), "(%d)", ContentIndexInContainer(this));
4717 AppendASCIItoUTF16(buf, aResult);
4718 return NS_OK;
4721 void
4722 nsFrame::XMLQuote(nsString& aString)
4724 PRInt32 i, len = aString.Length();
4725 for (i = 0; i < len; i++) {
4726 PRUnichar ch = aString.CharAt(i);
4727 if (ch == '<') {
4728 nsAutoString tmp(NS_LITERAL_STRING("&lt;"));
4729 aString.Cut(i, 1);
4730 aString.Insert(tmp, i);
4731 len += 3;
4732 i += 3;
4734 else if (ch == '>') {
4735 nsAutoString tmp(NS_LITERAL_STRING("&gt;"));
4736 aString.Cut(i, 1);
4737 aString.Insert(tmp, i);
4738 len += 3;
4739 i += 3;
4741 else if (ch == '\"') {
4742 nsAutoString tmp(NS_LITERAL_STRING("&quot;"));
4743 aString.Cut(i, 1);
4744 aString.Insert(tmp, i);
4745 len += 5;
4746 i += 5;
4750 #endif
4752 PRBool
4753 nsIFrame::IsVisibleForPainting(nsDisplayListBuilder* aBuilder) {
4754 if (!GetStyleVisibility()->IsVisible())
4755 return PR_FALSE;
4756 nsISelection* sel = aBuilder->GetBoundingSelection();
4757 return !sel || IsVisibleInSelection(sel);
4760 PRBool
4761 nsIFrame::IsVisibleForPainting() {
4762 if (!GetStyleVisibility()->IsVisible())
4763 return PR_FALSE;
4765 nsPresContext* pc = PresContext();
4766 if (!pc->IsRenderingOnlySelection())
4767 return PR_TRUE;
4769 nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(pc->PresShell()));
4770 if (selcon) {
4771 nsCOMPtr<nsISelection> sel;
4772 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
4773 getter_AddRefs(sel));
4774 if (sel)
4775 return IsVisibleInSelection(sel);
4777 return PR_TRUE;
4780 PRBool
4781 nsIFrame::IsVisibleInSelection(nsDisplayListBuilder* aBuilder) {
4782 nsISelection* sel = aBuilder->GetBoundingSelection();
4783 return !sel || IsVisibleInSelection(sel);
4786 PRBool
4787 nsIFrame::IsVisibleOrCollapsedForPainting(nsDisplayListBuilder* aBuilder) {
4788 if (!GetStyleVisibility()->IsVisibleOrCollapsed())
4789 return PR_FALSE;
4790 nsISelection* sel = aBuilder->GetBoundingSelection();
4791 return !sel || IsVisibleInSelection(sel);
4794 PRBool
4795 nsIFrame::IsVisibleInSelection(nsISelection* aSelection)
4797 if ((mState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT)
4798 return PR_TRUE;
4800 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
4801 PRBool vis;
4802 nsresult rv = aSelection->ContainsNode(node, PR_TRUE, &vis);
4803 return NS_FAILED(rv) || vis;
4806 /* virtual */ PRBool
4807 nsFrame::IsEmpty()
4809 return PR_FALSE;
4812 PRBool
4813 nsIFrame::CachedIsEmpty()
4815 NS_PRECONDITION(!(GetStateBits() & NS_FRAME_IS_DIRTY),
4816 "Must only be called on reflowed lines");
4817 return IsEmpty();
4820 /* virtual */ PRBool
4821 nsFrame::IsSelfEmpty()
4823 return PR_FALSE;
4826 NS_IMETHODIMP
4827 nsFrame::GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon)
4829 if (!aPresContext || !aSelCon)
4830 return NS_ERROR_INVALID_ARG;
4832 nsIFrame *frame = this;
4833 while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
4834 nsITextControlFrame *tcf = do_QueryFrame(frame);
4835 if (tcf) {
4836 return tcf->GetOwnedSelectionController(aSelCon);
4838 frame = frame->GetParent();
4841 return CallQueryInterface(aPresContext->GetPresShell(), aSelCon);
4844 already_AddRefed<nsFrameSelection>
4845 nsIFrame::GetFrameSelection()
4847 nsFrameSelection* fs =
4848 const_cast<nsFrameSelection*>(GetConstFrameSelection());
4849 NS_IF_ADDREF(fs);
4850 return fs;
4853 const nsFrameSelection*
4854 nsIFrame::GetConstFrameSelection()
4856 nsIFrame *frame = this;
4857 while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
4858 nsITextControlFrame *tcf = do_QueryFrame(frame);
4859 if (tcf) {
4860 return tcf->GetOwnedFrameSelection();
4862 frame = frame->GetParent();
4865 return PresContext()->PresShell()->ConstFrameSelection();
4868 #ifdef NS_DEBUG
4869 NS_IMETHODIMP
4870 nsFrame::DumpRegressionData(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent)
4872 IndentBy(out, aIndent);
4873 fprintf(out, "<frame va=\"%ld\" type=\"", PRUptrdiff(this));
4874 nsAutoString name;
4875 GetFrameName(name);
4876 XMLQuote(name);
4877 fputs(NS_LossyConvertUTF16toASCII(name).get(), out);
4878 fprintf(out, "\" state=\"%016llx\" parent=\"%ld\">\n",
4879 GetDebugStateBits(), PRUptrdiff(mParent));
4881 aIndent++;
4882 DumpBaseRegressionData(aPresContext, out, aIndent);
4883 aIndent--;
4885 IndentBy(out, aIndent);
4886 fprintf(out, "</frame>\n");
4888 return NS_OK;
4891 void
4892 nsFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent)
4894 if (GetNextSibling()) {
4895 IndentBy(out, aIndent);
4896 fprintf(out, "<next-sibling va=\"%ld\"/>\n", PRUptrdiff(GetNextSibling()));
4899 if (HasView()) {
4900 IndentBy(out, aIndent);
4901 fprintf(out, "<view va=\"%ld\">\n", PRUptrdiff(GetView()));
4902 aIndent++;
4903 // XXX add in code to dump out view state too...
4904 aIndent--;
4905 IndentBy(out, aIndent);
4906 fprintf(out, "</view>\n");
4909 IndentBy(out, aIndent);
4910 fprintf(out, "<bbox x=\"%d\" y=\"%d\" w=\"%d\" h=\"%d\"/>\n",
4911 mRect.x, mRect.y, mRect.width, mRect.height);
4913 // Now dump all of the children on all of the child lists
4914 nsIFrame* kid;
4915 nsIAtom* list = nsnull;
4916 PRInt32 listIndex = 0;
4917 do {
4918 kid = GetFirstChild(list);
4919 if (kid) {
4920 IndentBy(out, aIndent);
4921 if (nsnull != list) {
4922 nsAutoString listName;
4923 list->ToString(listName);
4924 fprintf(out, "<child-list name=\"");
4925 XMLQuote(listName);
4926 fputs(NS_LossyConvertUTF16toASCII(listName).get(), out);
4927 fprintf(out, "\">\n");
4929 else {
4930 fprintf(out, "<child-list>\n");
4932 aIndent++;
4933 while (kid) {
4934 kid->DumpRegressionData(aPresContext, out, aIndent);
4935 kid = kid->GetNextSibling();
4937 aIndent--;
4938 IndentBy(out, aIndent);
4939 fprintf(out, "</child-list>\n");
4941 list = GetAdditionalChildListName(listIndex++);
4942 } while (nsnull != list);
4944 #endif
4946 void
4947 nsIFrame::SetSelected(PRBool aSelected, SelectionType aType)
4949 NS_ASSERTION(!GetPrevContinuation(),
4950 "Should only be called on first in flow");
4951 if (aType != nsISelectionController::SELECTION_NORMAL)
4952 return;
4954 // check whether style allows selection
4955 PRBool selectable;
4956 IsSelectable(&selectable, nsnull);
4957 if (!selectable)
4958 return;
4960 for (nsIFrame* f = this; f; f = f->GetNextContinuation()) {
4961 if (aSelected) {
4962 AddStateBits(NS_FRAME_SELECTED_CONTENT);
4963 } else {
4964 RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
4967 // Repaint this frame subtree's entire area
4968 InvalidateFrameSubtree();
4972 NS_IMETHODIMP
4973 nsFrame::GetSelected(PRBool *aSelected) const
4975 if (!aSelected )
4976 return NS_ERROR_NULL_POINTER;
4977 *aSelected = !!(mState & NS_FRAME_SELECTED_CONTENT);
4978 return NS_OK;
4981 NS_IMETHODIMP
4982 nsFrame::GetPointFromOffset(PRInt32 inOffset, nsPoint* outPoint)
4984 NS_PRECONDITION(outPoint != nsnull, "Null parameter");
4985 nsRect contentRect = GetContentRect() - GetPosition();
4986 nsPoint pt = contentRect.TopLeft();
4987 if (mContent)
4989 nsIContent* newContent = mContent->GetParent();
4990 if (newContent){
4991 PRInt32 newOffset = newContent->IndexOf(mContent);
4993 PRBool isRTL = (NS_GET_EMBEDDING_LEVEL(this) & 1) == 1;
4994 if ((!isRTL && inOffset > newOffset) ||
4995 (isRTL && inOffset <= newOffset)) {
4996 pt = contentRect.TopRight();
5000 *outPoint = pt;
5001 return NS_OK;
5004 NS_IMETHODIMP
5005 nsFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset, PRBool inHint, PRInt32* outFrameContentOffset, nsIFrame **outChildFrame)
5007 NS_PRECONDITION(outChildFrame && outFrameContentOffset, "Null parameter");
5008 *outFrameContentOffset = (PRInt32)inHint;
5009 //the best frame to reflect any given offset would be a visible frame if possible
5010 //i.e. we are looking for a valid frame to place the blinking caret
5011 nsRect rect = GetRect();
5012 if (!rect.width || !rect.height)
5014 //if we have a 0 width or height then lets look for another frame that possibly has
5015 //the same content. If we have no frames in flow then just let us return 'this' frame
5016 nsIFrame* nextFlow = GetNextInFlow();
5017 if (nextFlow)
5018 return nextFlow->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
5020 *outChildFrame = this;
5021 return NS_OK;
5025 // What I've pieced together about this routine:
5026 // Starting with a block frame (from which a line frame can be gotten)
5027 // and a line number, drill down and get the first/last selectable
5028 // frame on that line, depending on aPos->mDirection.
5029 // aOutSideLimit != 0 means ignore aLineStart, instead work from
5030 // the end (if > 0) or beginning (if < 0).
5032 nsresult
5033 nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
5034 nsPeekOffsetStruct *aPos,
5035 nsIFrame *aBlockFrame,
5036 PRInt32 aLineStart,
5037 PRInt8 aOutSideLimit
5040 //magic numbers aLineStart will be -1 for end of block 0 will be start of block
5041 if (!aBlockFrame || !aPos)
5042 return NS_ERROR_NULL_POINTER;
5044 aPos->mResultFrame = nsnull;
5045 aPos->mResultContent = nsnull;
5046 aPos->mAttachForward = (aPos->mDirection == eDirNext);
5048 nsAutoLineIterator it = aBlockFrame->GetLineIterator();
5049 if (!it)
5050 return NS_ERROR_FAILURE;
5051 PRInt32 searchingLine = aLineStart;
5052 PRInt32 countLines = it->GetNumLines();
5053 if (aOutSideLimit > 0) //start at end
5054 searchingLine = countLines;
5055 else if (aOutSideLimit <0)//start at beginning
5056 searchingLine = -1;//"next" will be 0
5057 else
5058 if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
5059 (aPos->mDirection == eDirNext && searchingLine >= (countLines -1) )){
5060 //we need to jump to new block frame.
5061 return NS_ERROR_FAILURE;
5063 PRInt32 lineFrameCount;
5064 nsIFrame *resultFrame = nsnull;
5065 nsIFrame *farStoppingFrame = nsnull; //we keep searching until we find a "this" frame then we go to next line
5066 nsIFrame *nearStoppingFrame = nsnull; //if we are backing up from edge, stop here
5067 nsIFrame *firstFrame;
5068 nsIFrame *lastFrame;
5069 nsRect rect;
5070 PRBool isBeforeFirstFrame, isAfterLastFrame;
5071 PRBool found = PR_FALSE;
5073 nsresult result = NS_OK;
5074 while (!found)
5076 if (aPos->mDirection == eDirPrevious)
5077 searchingLine --;
5078 else
5079 searchingLine ++;
5080 if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
5081 (aPos->mDirection == eDirNext && searchingLine >= countLines ))
5083 //we need to jump to new block frame.
5084 return NS_ERROR_FAILURE;
5086 PRUint32 lineFlags;
5087 result = it->GetLine(searchingLine, &firstFrame, &lineFrameCount,
5088 rect, &lineFlags);
5089 if (!lineFrameCount)
5090 continue;
5091 if (NS_SUCCEEDED(result)){
5092 lastFrame = firstFrame;
5093 for (;lineFrameCount > 1;lineFrameCount --){
5094 //result = lastFrame->GetNextSibling(&lastFrame, searchingLine);
5095 result = it->GetNextSiblingOnLine(lastFrame, searchingLine);
5096 if (NS_FAILED(result) || !lastFrame){
5097 NS_ERROR("GetLine promised more frames than could be found");
5098 return NS_ERROR_FAILURE;
5101 GetLastLeaf(aPresContext, &lastFrame);
5103 if (aPos->mDirection == eDirNext){
5104 nearStoppingFrame = firstFrame;
5105 farStoppingFrame = lastFrame;
5107 else{
5108 nearStoppingFrame = lastFrame;
5109 farStoppingFrame = firstFrame;
5111 nsPoint offset;
5112 nsIView * view; //used for call of get offset from view
5113 aBlockFrame->GetOffsetFromView(offset,&view);
5114 nscoord newDesiredX = aPos->mDesiredX - offset.x;//get desired x into blockframe coordinates!
5115 result = it->FindFrameAt(searchingLine, newDesiredX, &resultFrame, &isBeforeFirstFrame, &isAfterLastFrame);
5116 if(NS_FAILED(result))
5117 continue;
5120 if (NS_SUCCEEDED(result) && resultFrame)
5122 //check to see if this is ANOTHER blockframe inside the other one if so then call into its lines
5123 nsAutoLineIterator newIt = resultFrame->GetLineIterator();
5124 if (newIt)
5126 aPos->mResultFrame = resultFrame;
5127 return NS_OK;
5129 //resultFrame is not a block frame
5130 result = NS_ERROR_FAILURE;
5132 nsCOMPtr<nsIFrameEnumerator> frameTraversal;
5133 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
5134 aPresContext, resultFrame,
5135 ePostOrder,
5136 PR_FALSE, // aVisual
5137 aPos->mScrollViewStop,
5138 PR_FALSE // aFollowOOFs
5140 if (NS_FAILED(result))
5141 return result;
5142 nsIFrame *storeOldResultFrame = resultFrame;
5143 while ( !found ){
5144 nsPoint point;
5145 point.x = aPos->mDesiredX;
5147 nsRect tempRect = resultFrame->GetRect();
5148 nsPoint offset;
5149 nsIView * view; //used for call of get offset from view
5150 result = resultFrame->GetOffsetFromView(offset, &view);
5151 if (NS_FAILED(result))
5152 return result;
5153 point.y = tempRect.height + offset.y;
5155 //special check. if we allow non-text selection then we can allow a hit location to fall before a table.
5156 //otherwise there is no way to get and click signal to fall before a table (it being a line iterator itself)
5157 nsIPresShell *shell = aPresContext->GetPresShell();
5158 if (!shell)
5159 return NS_ERROR_FAILURE;
5160 PRInt16 isEditor = shell->GetSelectionFlags();
5161 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
5162 if ( isEditor )
5164 if (resultFrame->GetType() == nsGkAtoms::tableOuterFrame)
5166 if (((point.x - offset.x + tempRect.x)<0) || ((point.x - offset.x+ tempRect.x)>tempRect.width))//off left/right side
5168 nsIContent* content = resultFrame->GetContent();
5169 if (content)
5171 nsIContent* parent = content->GetParent();
5172 if (parent)
5174 aPos->mResultContent = parent;
5175 aPos->mContentOffset = parent->IndexOf(content);
5176 aPos->mAttachForward = PR_FALSE;
5177 if ((point.x - offset.x+ tempRect.x)>tempRect.width)
5179 aPos->mContentOffset++;//go to end of this frame
5180 aPos->mAttachForward = PR_TRUE;
5182 //result frame is the result frames parent.
5183 aPos->mResultFrame = resultFrame->GetParent();
5184 return NS_POSITION_BEFORE_TABLE;
5191 if (!resultFrame->HasView())
5193 nsIView* view;
5194 nsPoint offset;
5195 resultFrame->GetOffsetFromView(offset, &view);
5196 ContentOffsets offsets =
5197 resultFrame->GetContentOffsetsFromPoint(point - offset);
5198 aPos->mResultContent = offsets.content;
5199 aPos->mContentOffset = offsets.offset;
5200 aPos->mAttachForward = offsets.associateWithNext;
5201 if (offsets.content)
5203 PRBool selectable;
5204 resultFrame->IsSelectable(&selectable, nsnull);
5205 if (selectable)
5207 found = PR_TRUE;
5208 break;
5213 if (aPos->mDirection == eDirPrevious && (resultFrame == farStoppingFrame))
5214 break;
5215 if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame))
5216 break;
5217 //always try previous on THAT line if that fails go the other way
5218 frameTraversal->Prev();
5219 resultFrame = frameTraversal->CurrentItem();
5220 if (!resultFrame)
5221 return NS_ERROR_FAILURE;
5224 if (!found){
5225 resultFrame = storeOldResultFrame;
5226 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
5227 aPresContext, resultFrame,
5228 eLeaf,
5229 PR_FALSE, // aVisual
5230 aPos->mScrollViewStop,
5231 PR_FALSE // aFollowOOFs
5234 while ( !found ){
5235 nsPoint point(aPos->mDesiredX, 0);
5236 nsIView* view;
5237 nsPoint offset;
5238 resultFrame->GetOffsetFromView(offset, &view);
5239 ContentOffsets offsets =
5240 resultFrame->GetContentOffsetsFromPoint(point - offset);
5241 aPos->mResultContent = offsets.content;
5242 aPos->mContentOffset = offsets.offset;
5243 aPos->mAttachForward = offsets.associateWithNext;
5244 if (offsets.content)
5246 PRBool selectable;
5247 resultFrame->IsSelectable(&selectable, nsnull);
5248 if (selectable)
5250 found = PR_TRUE;
5251 if (resultFrame == farStoppingFrame)
5252 aPos->mAttachForward = PR_FALSE;
5253 else
5254 aPos->mAttachForward = PR_TRUE;
5255 break;
5258 if (aPos->mDirection == eDirPrevious && (resultFrame == nearStoppingFrame))
5259 break;
5260 if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame))
5261 break;
5262 //previous didnt work now we try "next"
5263 frameTraversal->Next();
5264 nsIFrame *tempFrame = frameTraversal->CurrentItem();
5265 if (!tempFrame)
5266 break;
5267 resultFrame = tempFrame;
5269 aPos->mResultFrame = resultFrame;
5271 else {
5272 //we need to jump to new block frame.
5273 aPos->mAmount = eSelectLine;
5274 aPos->mStartOffset = 0;
5275 aPos->mAttachForward = !(aPos->mDirection == eDirNext);
5276 if (aPos->mDirection == eDirPrevious)
5277 aPos->mStartOffset = -1;//start from end
5278 return aBlockFrame->PeekOffset(aPos);
5281 return NS_OK;
5284 nsPeekOffsetStruct nsIFrame::GetExtremeCaretPosition(PRBool aStart)
5286 nsPeekOffsetStruct result;
5288 FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart);
5289 FrameContentRange range = GetRangeForFrame(targetFrame.frame);
5290 result.mResultContent = range.content;
5291 result.mContentOffset = aStart ? range.start : range.end;
5292 result.mAttachForward = (result.mContentOffset == range.start);
5293 return result;
5296 // Find the first (or last) descendant of the given frame
5297 // which is either a block frame or a BRFrame.
5298 static nsContentAndOffset
5299 FindBlockFrameOrBR(nsIFrame* aFrame, nsDirection aDirection)
5301 nsContentAndOffset result;
5302 result.mContent = nsnull;
5303 result.mOffset = 0;
5305 if (aFrame->IsGeneratedContentFrame())
5306 return result;
5308 // Treat form controls as inline leaves
5309 // XXX we really need a way to determine whether a frame is inline-level
5310 nsIFormControlFrame* fcf = do_QueryFrame(aFrame);
5311 if (fcf)
5312 return result;
5314 // Check the frame itself
5315 // Fall through "special" block frames because their mContent is the content
5316 // of the inline frames they were created from. The first/last child of
5317 // such frames is the real block frame we're looking for.
5318 if ((nsLayoutUtils::GetAsBlock(aFrame) && !(aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL)) ||
5319 aFrame->GetType() == nsGkAtoms::brFrame) {
5320 nsIContent* content = aFrame->GetContent();
5321 result.mContent = content->GetParent();
5322 // In some cases (bug 310589, bug 370174) we end up here with a null content.
5323 // This probably shouldn't ever happen, but since it sometimes does, we want
5324 // to avoid crashing here.
5325 NS_ASSERTION(result.mContent, "Unexpected orphan content");
5326 if (result.mContent)
5327 result.mOffset = result.mContent->IndexOf(content) +
5328 (aDirection == eDirPrevious ? 1 : 0);
5329 return result;
5332 // If this is a preformatted text frame, see if it ends with a newline
5333 if (aFrame->HasTerminalNewline() &&
5334 aFrame->GetStyleContext()->GetStyleText()->NewlineIsSignificant()) {
5335 PRInt32 startOffset, endOffset;
5336 aFrame->GetOffsets(startOffset, endOffset);
5337 result.mContent = aFrame->GetContent();
5338 result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
5339 return result;
5342 // Iterate over children and call ourselves recursively
5343 if (aDirection == eDirPrevious) {
5344 nsIFrame* child = aFrame->GetChildList(nsnull).LastChild();
5345 while(child && !result.mContent) {
5346 result = FindBlockFrameOrBR(child, aDirection);
5347 child = child->GetPrevSibling();
5349 } else { // eDirNext
5350 nsIFrame* child = aFrame->GetFirstChild(nsnull);
5351 while(child && !result.mContent) {
5352 result = FindBlockFrameOrBR(child, aDirection);
5353 child = child->GetNextSibling();
5356 return result;
5359 nsresult
5360 nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos)
5362 nsIFrame* frame = this;
5363 nsContentAndOffset blockFrameOrBR;
5364 blockFrameOrBR.mContent = nsnull;
5365 PRBool reachedBlockAncestor = PR_FALSE;
5367 // Go through containing frames until reaching a block frame.
5368 // In each step, search the previous (or next) siblings for the closest
5369 // "stop frame" (a block frame or a BRFrame).
5370 // If found, set it to be the selection boundray and abort.
5372 if (aPos->mDirection == eDirPrevious) {
5373 while (!reachedBlockAncestor) {
5374 nsIFrame* parent = frame->GetParent();
5375 // Treat a frame associated with the root content as if it were a block frame.
5376 if (!frame->mContent || !frame->mContent->GetParent()) {
5377 reachedBlockAncestor = PR_TRUE;
5378 break;
5380 nsIFrame* sibling = frame->GetPrevSibling();
5381 while (sibling && !blockFrameOrBR.mContent) {
5382 blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirPrevious);
5383 sibling = sibling->GetPrevSibling();
5385 if (blockFrameOrBR.mContent) {
5386 aPos->mResultContent = blockFrameOrBR.mContent;
5387 aPos->mContentOffset = blockFrameOrBR.mOffset;
5388 break;
5390 frame = parent;
5391 reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nsnull);
5393 if (reachedBlockAncestor) { // no "stop frame" found
5394 aPos->mResultContent = frame->GetContent();
5395 aPos->mContentOffset = 0;
5397 } else { // eDirNext
5398 while (!reachedBlockAncestor) {
5399 nsIFrame* parent = frame->GetParent();
5400 // Treat a frame associated with the root content as if it were a block frame.
5401 if (!frame->mContent || !frame->mContent->GetParent()) {
5402 reachedBlockAncestor = PR_TRUE;
5403 break;
5405 nsIFrame* sibling = frame;
5406 while (sibling && !blockFrameOrBR.mContent) {
5407 blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirNext);
5408 sibling = sibling->GetNextSibling();
5410 if (blockFrameOrBR.mContent) {
5411 aPos->mResultContent = blockFrameOrBR.mContent;
5412 aPos->mContentOffset = blockFrameOrBR.mOffset;
5413 break;
5415 frame = parent;
5416 reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nsnull);
5418 if (reachedBlockAncestor) { // no "stop frame" found
5419 aPos->mResultContent = frame->GetContent();
5420 if (aPos->mResultContent)
5421 aPos->mContentOffset = aPos->mResultContent->GetChildCount();
5424 return NS_OK;
5427 // Determine movement direction relative to frame
5428 static PRBool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection, PRBool aVisual)
5430 PRBool isReverseDirection = aVisual ?
5431 (NS_GET_EMBEDDING_LEVEL(frame) & 1) != (NS_GET_BASE_LEVEL(frame) & 1) : PR_FALSE;
5432 return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
5435 NS_IMETHODIMP
5436 nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos)
5438 if (!aPos)
5439 return NS_ERROR_NULL_POINTER;
5440 nsresult result = NS_ERROR_FAILURE;
5442 if (mState & NS_FRAME_IS_DIRTY)
5443 return NS_ERROR_UNEXPECTED;
5445 // Translate content offset to be relative to frame
5446 FrameContentRange range = GetRangeForFrame(this);
5447 PRInt32 offset = aPos->mStartOffset - range.start;
5448 nsIFrame* current = this;
5450 switch (aPos->mAmount) {
5451 case eSelectCharacter:
5452 case eSelectCluster:
5454 PRBool eatingNonRenderableWS = PR_FALSE;
5455 PRBool done = PR_FALSE;
5456 PRBool jumpedLine = PR_FALSE;
5458 while (!done) {
5459 PRBool movingInFrameDirection =
5460 IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
5462 if (eatingNonRenderableWS)
5463 done = current->PeekOffsetNoAmount(movingInFrameDirection, &offset);
5464 else
5465 done = current->PeekOffsetCharacter(movingInFrameDirection, &offset,
5466 aPos->mAmount == eSelectCluster);
5468 if (!done) {
5469 result =
5470 current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual,
5471 aPos->mJumpLines, aPos->mScrollViewStop,
5472 &current, &offset, &jumpedLine);
5473 if (NS_FAILED(result))
5474 return result;
5476 // If we jumped lines, it's as if we found a character, but we still need
5477 // to eat non-renderable content on the new line.
5478 if (jumpedLine)
5479 eatingNonRenderableWS = PR_TRUE;
5483 // Set outputs
5484 range = GetRangeForFrame(current);
5485 aPos->mResultFrame = current;
5486 aPos->mResultContent = range.content;
5487 // Output offset is relative to content, not frame
5488 aPos->mContentOffset = offset < 0 ? range.end : range.start + offset;
5489 // If we're dealing with a text frame and moving backward positions us at
5490 // the end of that line, decrease the offset by one to make sure that
5491 // we're placed before the linefeed character on the previous line.
5492 if (offset < 0 && jumpedLine &&
5493 aPos->mDirection == eDirPrevious &&
5494 current->GetStyleText()->NewlineIsSignificant() &&
5495 current->HasTerminalNewline()) {
5496 --aPos->mContentOffset;
5499 break;
5501 case eSelectWord:
5503 // wordSelectEatSpace means "are we looking for a boundary between whitespace
5504 // and non-whitespace (in the direction we're moving in)".
5505 // It is true when moving forward and looking for a beginning of a word, or
5506 // when moving backwards and looking for an end of a word.
5507 PRBool wordSelectEatSpace;
5508 if (aPos->mWordMovementType != eDefaultBehavior) {
5509 // aPos->mWordMovementType possible values:
5510 // eEndWord: eat the space if we're moving backwards
5511 // eStartWord: eat the space if we're moving forwards
5512 wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) == (aPos->mDirection == eDirPrevious));
5514 else {
5515 // Use the hidden preference which is based on operating system behavior.
5516 // This pref only affects whether moving forward by word should go to the end of this word or start of the next word.
5517 // When going backwards, the start of the word is always used, on every operating system.
5518 wordSelectEatSpace = aPos->mDirection == eDirNext &&
5519 nsContentUtils::GetBoolPref("layout.word_select.eat_space_to_next_word");
5522 // mSawBeforeType means "we already saw characters of the type
5523 // before the boundary we're looking for". Examples:
5524 // 1. If we're moving forward, looking for a word beginning (i.e. a boundary
5525 // between whitespace and non-whitespace), then eatingWS==PR_TRUE means
5526 // "we already saw some whitespace".
5527 // 2. If we're moving backward, looking for a word beginning (i.e. a boundary
5528 // between non-whitespace and whitespace), then eatingWS==PR_TRUE means
5529 // "we already saw some non-whitespace".
5530 PeekWordState state;
5531 PRBool done = PR_FALSE;
5532 while (!done) {
5533 PRBool movingInFrameDirection =
5534 IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
5536 done = current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace,
5537 aPos->mIsKeyboardSelect, &offset, &state);
5539 if (!done) {
5540 nsIFrame* nextFrame;
5541 PRInt32 nextFrameOffset;
5542 PRBool jumpedLine;
5543 result =
5544 current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual,
5545 aPos->mJumpLines, aPos->mScrollViewStop,
5546 &nextFrame, &nextFrameOffset, &jumpedLine);
5547 // We can't jump lines if we're looking for whitespace following
5548 // non-whitespace, and we already encountered non-whitespace.
5549 if (NS_FAILED(result) ||
5550 (jumpedLine && !wordSelectEatSpace && state.mSawBeforeType)) {
5551 done = PR_TRUE;
5552 } else {
5553 if (jumpedLine) {
5554 state.mContext.Truncate();
5556 current = nextFrame;
5557 offset = nextFrameOffset;
5558 // Jumping a line is equivalent to encountering whitespace
5559 if (wordSelectEatSpace && jumpedLine)
5560 state.SetSawBeforeType();
5565 // Set outputs
5566 range = GetRangeForFrame(current);
5567 aPos->mResultFrame = current;
5568 aPos->mResultContent = range.content;
5569 // Output offset is relative to content, not frame
5570 aPos->mContentOffset = offset < 0 ? range.end : range.start + offset;
5571 break;
5573 case eSelectLine :
5575 nsAutoLineIterator iter;
5576 nsIFrame *blockFrame = this;
5578 while (NS_FAILED(result)){
5579 PRInt32 thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame);
5580 if (thisLine < 0)
5581 return NS_ERROR_FAILURE;
5582 iter = blockFrame->GetLineIterator();
5583 NS_ASSERTION(iter, "GetLineNumber() succeeded but no block frame?");
5584 result = NS_OK;
5586 int edgeCase = 0;//no edge case. this should look at thisLine
5588 PRBool doneLooping = PR_FALSE;//tells us when no more block frames hit.
5589 //this part will find a frame or a block frame. if it's a block frame
5590 //it will "drill down" to find a viable frame or it will return an error.
5591 nsIFrame *lastFrame = this;
5592 do {
5593 result = nsFrame::GetNextPrevLineFromeBlockFrame(PresContext(),
5594 aPos,
5595 blockFrame,
5596 thisLine,
5597 edgeCase //start from thisLine
5599 if (NS_SUCCEEDED(result) && (!aPos->mResultFrame || aPos->mResultFrame == lastFrame))//we came back to same spot! keep going
5601 aPos->mResultFrame = nsnull;
5602 if (aPos->mDirection == eDirPrevious)
5603 thisLine--;
5604 else
5605 thisLine++;
5607 else //if failure or success with different frame.
5608 doneLooping = PR_TRUE; //do not continue with while loop
5610 lastFrame = aPos->mResultFrame; //set last frame
5612 if (NS_SUCCEEDED(result) && aPos->mResultFrame
5613 && blockFrame != aPos->mResultFrame)// make sure block element is not the same as the one we had before
5615 /* SPECIAL CHECK FOR TABLE NAVIGATION
5616 tables need to navigate also and the frame that supports it is nsTableRowGroupFrame which is INSIDE
5617 nsTableOuterFrame. if we have stumbled onto an nsTableOuter we need to drill into nsTableRowGroup
5618 if we hit a header or footer that's ok just go into them,
5620 PRBool searchTableBool = PR_FALSE;
5621 if (aPos->mResultFrame->GetType() == nsGkAtoms::tableOuterFrame ||
5622 aPos->mResultFrame->GetType() == nsGkAtoms::tableCellFrame)
5624 nsIFrame *frame = aPos->mResultFrame->GetFirstChild(nsnull);
5625 //got the table frame now
5626 while(frame) //ok time to drill down to find iterator
5628 iter = frame->GetLineIterator();
5629 if (iter)
5631 aPos->mResultFrame = frame;
5632 searchTableBool = PR_TRUE;
5633 result = NS_OK;
5634 break; //while(frame)
5636 result = NS_ERROR_FAILURE;
5637 frame = frame->GetFirstChild(nsnull);
5641 if (!searchTableBool) {
5642 iter = aPos->mResultFrame->GetLineIterator();
5643 result = iter ? NS_OK : NS_ERROR_FAILURE;
5645 if (NS_SUCCEEDED(result) && iter)//we've struck another block element!
5647 doneLooping = PR_FALSE;
5648 if (aPos->mDirection == eDirPrevious)
5649 edgeCase = 1;//far edge, search from end backwards
5650 else
5651 edgeCase = -1;//near edge search from beginning onwards
5652 thisLine=0;//this line means nothing now.
5653 //everything else means something so keep looking "inside" the block
5654 blockFrame = aPos->mResultFrame;
5657 else
5659 result = NS_OK;//THIS is to mean that everything is ok to the containing while loop
5660 break;
5663 } while (!doneLooping);
5665 return result;
5668 case eSelectParagraph:
5669 return PeekOffsetParagraph(aPos);
5671 case eSelectBeginLine:
5672 case eSelectEndLine:
5674 // Adjusted so that the caret can't get confused when content changes
5675 nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this);
5676 PRInt32 thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame);
5677 if (thisLine < 0)
5678 return NS_ERROR_FAILURE;
5679 nsAutoLineIterator it = blockFrame->GetLineIterator();
5680 NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
5682 PRInt32 lineFrameCount;
5683 nsIFrame *firstFrame;
5684 nsRect usedRect;
5685 PRUint32 lineFlags;
5686 nsIFrame* baseFrame = nsnull;
5687 PRBool endOfLine = (eSelectEndLine == aPos->mAmount);
5689 #ifdef IBMBIDI
5690 if (aPos->mVisual && PresContext()->BidiEnabled()) {
5691 PRBool lineIsRTL = it->GetDirection();
5692 PRBool isReordered;
5693 nsIFrame *lastFrame;
5694 result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
5695 baseFrame = endOfLine ? lastFrame : firstFrame;
5696 if (baseFrame) {
5697 nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(baseFrame);
5698 // If the direction of the frame on the edge is opposite to that of the line,
5699 // we'll need to drill down to its opposite end, so reverse endOfLine.
5700 if ((embeddingLevel & 1) == !lineIsRTL)
5701 endOfLine = !endOfLine;
5703 } else
5704 #endif
5706 it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect, &lineFlags);
5708 nsIFrame* frame = firstFrame;
5709 for (PRInt32 count = lineFrameCount; count;
5710 --count, frame = frame->GetNextSibling()) {
5711 if (!frame->IsGeneratedContentFrame()) {
5712 baseFrame = frame;
5713 if (!endOfLine)
5714 break;
5718 if (!baseFrame)
5719 return NS_ERROR_FAILURE;
5720 FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame,
5721 endOfLine);
5722 FrameContentRange range = GetRangeForFrame(targetFrame.frame);
5723 aPos->mResultContent = range.content;
5724 aPos->mContentOffset = endOfLine ? range.end : range.start;
5725 if (endOfLine && targetFrame.frame->HasTerminalNewline()) {
5726 // Do not position the caret after the terminating newline if we're
5727 // trying to move to the end of line (see bug 596506)
5728 --aPos->mContentOffset;
5730 aPos->mResultFrame = targetFrame.frame;
5731 aPos->mAttachForward = (aPos->mContentOffset == range.start);
5732 if (!range.content)
5733 return NS_ERROR_FAILURE;
5734 return NS_OK;
5737 default:
5739 NS_ASSERTION(PR_FALSE, "Invalid amount");
5740 return NS_ERROR_FAILURE;
5743 return NS_OK;
5746 PRBool
5747 nsFrame::PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset)
5749 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
5750 // Sure, we can stop right here.
5751 return PR_TRUE;
5754 PRBool
5755 nsFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset,
5756 PRBool aRespectClusters)
5758 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
5759 PRInt32 startOffset = *aOffset;
5760 // A negative offset means "end of frame", which in our case means offset 1.
5761 if (startOffset < 0)
5762 startOffset = 1;
5763 if (aForward == (startOffset == 0)) {
5764 // We're before the frame and moving forward, or after it and moving backwards:
5765 // skip to the other side and we're done.
5766 *aOffset = 1 - startOffset;
5767 return PR_TRUE;
5769 return PR_FALSE;
5772 PRBool
5773 nsFrame::PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect,
5774 PRInt32* aOffset, PeekWordState* aState)
5776 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
5777 PRInt32 startOffset = *aOffset;
5778 // This isn't text, so truncate the context
5779 aState->mContext.Truncate();
5780 if (startOffset < 0)
5781 startOffset = 1;
5782 if (aForward == (startOffset == 0)) {
5783 // We're before the frame and moving forward, or after it and moving backwards.
5784 // If we're looking for non-whitespace, we found it (without skipping this frame).
5785 if (!aState->mAtStart) {
5786 if (aState->mLastCharWasPunctuation) {
5787 // We're not punctuation, so this is a punctuation boundary.
5788 if (BreakWordBetweenPunctuation(aState, aForward, PR_FALSE, PR_FALSE, aIsKeyboardSelect))
5789 return PR_TRUE;
5790 } else {
5791 // This is not a punctuation boundary.
5792 if (aWordSelectEatSpace && aState->mSawBeforeType)
5793 return PR_TRUE;
5796 // Otherwise skip to the other side and note that we encountered non-whitespace.
5797 *aOffset = 1 - startOffset;
5798 aState->Update(PR_FALSE, // not punctuation
5799 PR_FALSE // not whitespace
5801 if (!aWordSelectEatSpace)
5802 aState->SetSawBeforeType();
5804 return PR_FALSE;
5807 PRBool
5808 nsFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
5809 PRBool aForward,
5810 PRBool aPunctAfter, PRBool aWhitespaceAfter,
5811 PRBool aIsKeyboardSelect)
5813 NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation,
5814 "Call this only at punctuation boundaries");
5815 if (aState->mLastCharWasWhitespace) {
5816 // We always stop between whitespace and punctuation
5817 return PR_TRUE;
5819 if (!nsContentUtils::GetBoolPref("layout.word_select.stop_at_punctuation")) {
5820 // When this pref is false, we never stop at a punctuation boundary unless
5821 // it's after whitespace
5822 return PR_FALSE;
5824 if (!aIsKeyboardSelect) {
5825 // mouse caret movement (e.g. word selection) always stops at every punctuation boundary
5826 return PR_TRUE;
5828 PRBool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
5829 if (!afterPunct) {
5830 // keyboard caret movement only stops after punctuation (in content order)
5831 return PR_FALSE;
5833 // Stop only if we've seen some non-punctuation since the last whitespace;
5834 // don't stop after punctuation that follows whitespace.
5835 return aState->mSeenNonPunctuationSinceWhitespace;
5838 NS_IMETHODIMP
5839 nsFrame::CheckVisibility(nsPresContext* , PRInt32 , PRInt32 , PRBool , PRBool *, PRBool *)
5841 return NS_ERROR_NOT_IMPLEMENTED;
5845 PRInt32
5846 nsFrame::GetLineNumber(nsIFrame *aFrame, PRBool aLockScroll, nsIFrame** aContainingBlock)
5848 NS_ASSERTION(aFrame, "null aFrame");
5849 nsFrameManager* frameManager = aFrame->PresContext()->FrameManager();
5850 nsIFrame *blockFrame = aFrame;
5851 nsIFrame *thisBlock;
5852 nsAutoLineIterator it;
5853 nsresult result = NS_ERROR_FAILURE;
5854 while (NS_FAILED(result) && blockFrame)
5856 thisBlock = blockFrame;
5857 if (thisBlock->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
5858 //if we are searching for a frame that is not in flow we will not find it.
5859 //we must instead look for its placeholder
5860 if (thisBlock->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
5861 // abspos continuations don't have placeholders, get the fif
5862 thisBlock = thisBlock->GetFirstInFlow();
5864 thisBlock = frameManager->GetPlaceholderFrameFor(thisBlock);
5865 if (!thisBlock)
5866 return -1;
5868 blockFrame = thisBlock->GetParent();
5869 result = NS_OK;
5870 if (blockFrame) {
5871 if (aLockScroll && blockFrame->GetType() == nsGkAtoms::scrollFrame)
5872 return -1;
5873 it = blockFrame->GetLineIterator();
5874 if (!it)
5875 result = NS_ERROR_FAILURE;
5878 if (!blockFrame || !it)
5879 return -1;
5881 if (aContainingBlock)
5882 *aContainingBlock = blockFrame;
5883 return it->FindLineContaining(thisBlock);
5886 nsresult
5887 nsIFrame::GetFrameFromDirection(nsDirection aDirection, PRBool aVisual,
5888 PRBool aJumpLines, PRBool aScrollViewStop,
5889 nsIFrame** aOutFrame, PRInt32* aOutOffset, PRBool* aOutJumpedLine)
5891 nsresult result;
5893 if (!aOutFrame || !aOutOffset || !aOutJumpedLine)
5894 return NS_ERROR_NULL_POINTER;
5896 nsPresContext* presContext = PresContext();
5897 *aOutFrame = nsnull;
5898 *aOutOffset = 0;
5899 *aOutJumpedLine = PR_FALSE;
5901 // Find the prev/next selectable frame
5902 PRBool selectable = PR_FALSE;
5903 nsIFrame *traversedFrame = this;
5904 while (!selectable) {
5905 nsIFrame *blockFrame;
5907 PRInt32 thisLine = nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, &blockFrame);
5908 if (thisLine < 0)
5909 return NS_ERROR_FAILURE;
5911 nsAutoLineIterator it = blockFrame->GetLineIterator();
5912 NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
5914 PRBool atLineEdge;
5915 nsIFrame *firstFrame;
5916 nsIFrame *lastFrame;
5917 #ifdef IBMBIDI
5918 if (aVisual && presContext->BidiEnabled()) {
5919 PRBool lineIsRTL = it->GetDirection();
5920 PRBool isReordered;
5921 result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
5922 nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
5923 if (*framePtr) {
5924 nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr);
5925 if ((((embeddingLevel & 1) && lineIsRTL) || (!(embeddingLevel & 1) && !lineIsRTL)) ==
5926 (aDirection == eDirPrevious)) {
5927 nsFrame::GetFirstLeaf(presContext, framePtr);
5928 } else {
5929 nsFrame::GetLastLeaf(presContext, framePtr);
5931 atLineEdge = *framePtr == traversedFrame;
5932 } else {
5933 atLineEdge = PR_TRUE;
5935 } else
5936 #endif
5938 nsRect nonUsedRect;
5939 PRInt32 lineFrameCount;
5940 PRUint32 lineFlags;
5941 result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
5942 &lineFlags);
5943 if (NS_FAILED(result))
5944 return result;
5946 if (aDirection == eDirPrevious) {
5947 nsFrame::GetFirstLeaf(presContext, &firstFrame);
5948 atLineEdge = firstFrame == traversedFrame;
5949 } else { // eDirNext
5950 lastFrame = firstFrame;
5951 for (;lineFrameCount > 1;lineFrameCount --){
5952 result = it->GetNextSiblingOnLine(lastFrame, thisLine);
5953 if (NS_FAILED(result) || !lastFrame){
5954 NS_ERROR("should not be reached nsFrame");
5955 return NS_ERROR_FAILURE;
5958 nsFrame::GetLastLeaf(presContext, &lastFrame);
5959 atLineEdge = lastFrame == traversedFrame;
5963 if (atLineEdge) {
5964 *aOutJumpedLine = PR_TRUE;
5965 if (!aJumpLines)
5966 return NS_ERROR_FAILURE; //we are done. cannot jump lines
5969 nsCOMPtr<nsIFrameEnumerator> frameTraversal;
5970 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
5971 presContext, traversedFrame,
5972 eLeaf,
5973 aVisual && presContext->BidiEnabled(),
5974 aScrollViewStop,
5975 PR_TRUE // aFollowOOFs
5977 if (NS_FAILED(result))
5978 return result;
5980 if (aDirection == eDirNext)
5981 frameTraversal->Next();
5982 else
5983 frameTraversal->Prev();
5985 traversedFrame = frameTraversal->CurrentItem();
5986 if (!traversedFrame)
5987 return NS_ERROR_FAILURE;
5988 traversedFrame->IsSelectable(&selectable, nsnull);
5989 } // while (!selectable)
5991 *aOutOffset = (aDirection == eDirNext) ? 0 : -1;
5993 #ifdef IBMBIDI
5994 if (aVisual) {
5995 PRUint8 newLevel = NS_GET_EMBEDDING_LEVEL(traversedFrame);
5996 PRUint8 newBaseLevel = NS_GET_BASE_LEVEL(traversedFrame);
5997 if ((newLevel & 1) != (newBaseLevel & 1)) // The new frame is reverse-direction, go to the other end
5998 *aOutOffset = -1 - *aOutOffset;
6000 #endif
6001 *aOutFrame = traversedFrame;
6002 return NS_OK;
6005 nsIView* nsIFrame::GetClosestView(nsPoint* aOffset) const
6007 nsPoint offset(0,0);
6008 for (const nsIFrame *f = this; f; f = f->GetParent()) {
6009 if (f->HasView()) {
6010 if (aOffset)
6011 *aOffset = offset;
6012 return f->GetView();
6014 offset += f->GetPosition();
6017 NS_NOTREACHED("No view on any parent? How did that happen?");
6018 return nsnull;
6022 /* virtual */ void
6023 nsFrame::ChildIsDirty(nsIFrame* aChild)
6025 NS_NOTREACHED("should never be called on a frame that doesn't inherit from "
6026 "nsContainerFrame");
6030 #ifdef ACCESSIBILITY
6031 already_AddRefed<nsAccessible>
6032 nsFrame::CreateAccessible()
6034 return nsnull;
6036 #endif
6038 NS_DECLARE_FRAME_PROPERTY(OverflowAreasProperty,
6039 nsIFrame::DestroyOverflowAreas)
6041 void
6042 nsIFrame::ClearOverflowRects()
6044 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
6045 Properties().Delete(OverflowAreasProperty());
6047 mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
6050 /** Create or retrieve the previously stored overflow area, if the frame does
6051 * not overflow and no creation is required return nsnull.
6052 * @return pointer to the overflow area rectangle
6054 nsOverflowAreas*
6055 nsIFrame::GetOverflowAreasProperty()
6057 FrameProperties props = Properties();
6058 nsOverflowAreas *overflow =
6059 static_cast<nsOverflowAreas*>(props.Get(OverflowAreasProperty()));
6061 if (overflow) {
6062 return overflow; // the property already exists
6065 // The property isn't set yet, so allocate a new rect, set the property,
6066 // and return the newly allocated rect
6067 overflow = new nsOverflowAreas;
6068 props.Set(OverflowAreasProperty(), overflow);
6069 return overflow;
6072 /** Set the overflowArea rect, storing it as deltas or a separate rect
6073 * depending on its size in relation to the primary frame rect.
6075 void
6076 nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
6078 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
6079 nsOverflowAreas *overflow =
6080 static_cast<nsOverflowAreas*>(Properties().Get(OverflowAreasProperty()));
6081 *overflow = aOverflowAreas;
6083 // Don't bother with converting to the deltas form if we already
6084 // have a property.
6085 return;
6088 const nsRect& vis = aOverflowAreas.VisualOverflow();
6089 PRUint32 l = -vis.x, // left edge: positive delta is leftwards
6090 t = -vis.y, // top: positive is upwards
6091 r = vis.XMost() - mRect.width, // right: positive is rightwards
6092 b = vis.YMost() - mRect.height; // bottom: positive is downwards
6093 if (aOverflowAreas.ScrollableOverflow() == nsRect(nsPoint(0, 0), GetSize()) &&
6094 l <= NS_FRAME_OVERFLOW_DELTA_MAX &&
6095 t <= NS_FRAME_OVERFLOW_DELTA_MAX &&
6096 r <= NS_FRAME_OVERFLOW_DELTA_MAX &&
6097 b <= NS_FRAME_OVERFLOW_DELTA_MAX &&
6098 // we have to check these against zero because we *never* want to
6099 // set a frame as having no overflow in this function. This is
6100 // because FinishAndStoreOverflow calls this function prior to
6101 // SetRect based on whether the overflow areas match aNewSize.
6102 // In the case where the overflow areas exactly match mRect but
6103 // do not match aNewSize, we need to store overflow in a property
6104 // so that our eventual SetRect/SetSize will know that it has to
6105 // reset our overflow areas.
6106 (l | t | r | b) != 0) {
6107 // It's a "small" overflow area so we store the deltas for each edge
6108 // directly in the frame, rather than allocating a separate rect.
6109 // If they're all zero, that's fine; we're setting things to
6110 // no-overflow.
6111 mOverflow.mVisualDeltas.mLeft = l;
6112 mOverflow.mVisualDeltas.mTop = t;
6113 mOverflow.mVisualDeltas.mRight = r;
6114 mOverflow.mVisualDeltas.mBottom = b;
6115 } else {
6116 // it's a large overflow area that we need to store as a property
6117 mOverflow.mType = NS_FRAME_OVERFLOW_LARGE;
6118 nsOverflowAreas* overflow = GetOverflowAreasProperty();
6119 NS_ASSERTION(overflow, "should have created areas");
6120 *overflow = aOverflowAreas;
6124 inline PRBool
6125 IsInlineFrame(nsIFrame *aFrame)
6127 nsIAtom *type = aFrame->GetType();
6128 return type == nsGkAtoms::inlineFrame ||
6129 type == nsGkAtoms::positionedInlineFrame;
6132 void
6133 nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
6134 nsSize aNewSize)
6136 nsRect bounds(nsPoint(0, 0), aNewSize);
6138 // This is now called FinishAndStoreOverflow() instead of
6139 // StoreOverflow() because frame-generic ways of adding overflow
6140 // can happen here, e.g. CSS2 outline and native theme.
6141 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
6142 NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||
6143 aOverflowAreas.Overflow(otype).Contains(nsRect(nsPoint(0,0), aNewSize)),
6144 "Computed overflow area must contain frame bounds");
6147 // If we clip our children, clear accumulated overflow area. The
6148 // children are actually clipped to the padding-box, but since the
6149 // overflow area should include the entire border-box, just set it to
6150 // the border-box here.
6151 const nsStyleDisplay *disp = GetStyleDisplay();
6152 NS_ASSERTION((disp->mOverflowY == NS_STYLE_OVERFLOW_CLIP) ==
6153 (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP),
6154 "If one overflow is clip, the other should be too");
6155 if (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP ||
6156 nsFrame::ApplyPaginatedOverflowClipping(this)) {
6157 // The contents are actually clipped to the padding area
6158 aOverflowAreas.SetAllTo(bounds);
6161 // Overflow area must always include the frame's top-left and bottom-right,
6162 // even if the frame rect is empty.
6163 // Pending a real fix for bug 426879, don't do this for inline frames
6164 // with zero width.
6165 if (aNewSize.width != 0 || !IsInlineFrame(this)) {
6166 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
6167 nsRect& o = aOverflowAreas.Overflow(otype);
6168 o.UnionRectIncludeEmpty(o, bounds);
6172 // Note that NS_STYLE_OVERFLOW_CLIP doesn't clip the frame background,
6173 // so we add theme background overflow here so it's not clipped.
6174 if (!IsBoxWrapped() && IsThemed(disp)) {
6175 nsRect r(bounds);
6176 nsPresContext *presContext = PresContext();
6177 if (presContext->GetTheme()->
6178 GetWidgetOverflow(presContext->DeviceContext(), this,
6179 disp->mAppearance, &r)) {
6180 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
6181 nsRect& o = aOverflowAreas.Overflow(otype);
6182 o.UnionRectIncludeEmpty(o, r);
6187 // Nothing in here should affect scrollable overflow.
6188 PRBool hasOutlineOrEffects;
6189 aOverflowAreas.VisualOverflow() =
6190 ComputeOutlineAndEffectsRect(this, &hasOutlineOrEffects,
6191 aOverflowAreas.VisualOverflow(), aNewSize,
6192 PR_TRUE);
6194 // Absolute position clipping
6195 PRBool didHaveAbsPosClip = (GetStateBits() & NS_FRAME_HAS_CLIP) != 0;
6196 nsRect absPosClipRect;
6197 PRBool hasAbsPosClip = GetAbsPosClipRect(disp, &absPosClipRect, aNewSize);
6198 if (hasAbsPosClip) {
6199 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
6200 nsRect& o = aOverflowAreas.Overflow(otype);
6201 o.IntersectRect(o, absPosClipRect);
6203 AddStateBits(NS_FRAME_HAS_CLIP);
6204 } else {
6205 RemoveStateBits(NS_FRAME_HAS_CLIP);
6208 /* If we're transformed, transform the overflow rect by the current transformation. */
6209 PRBool hasTransform = IsTransformed();
6210 if (hasTransform) {
6211 Properties().Set(nsIFrame::PreTransformBBoxProperty(),
6212 new nsRect(aOverflowAreas.VisualOverflow()));
6213 /* Since our size might not actually have been computed yet, we need to make sure that we use the
6214 * correct dimensions by overriding the stored bounding rectangle with the value the caller has
6215 * ensured us we'll use.
6217 nsRect newBounds(nsPoint(0, 0), aNewSize);
6218 // Transform affects both overflow areas.
6219 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
6220 nsRect& o = aOverflowAreas.Overflow(otype);
6221 o = nsDisplayTransform::TransformRect(o, this, nsPoint(0, 0), &newBounds);
6225 PRBool visualOverflowChanged =
6226 GetVisualOverflowRect() != aOverflowAreas.VisualOverflow();
6228 if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) {
6229 SetOverflowAreas(aOverflowAreas);
6230 } else {
6231 ClearOverflowRects();
6234 if (visualOverflowChanged) {
6235 if (hasOutlineOrEffects) {
6236 // When there's an outline or box-shadow or SVG effects,
6237 // changes to those styles might require repainting of the old and new
6238 // overflow areas. Repainting of the old overflow area is handled in
6239 // nsCSSFrameConstructor::DoApplyRenderingChangeToTree in response
6240 // to nsChangeHint_RepaintFrame. Since the new overflow area is not
6241 // known at that time, we have to handle it here.
6242 // If the overflow area hasn't changed, then we don't have to do
6243 // anything here since repainting the old overflow area was enough.
6244 // If there is no outline or other effects now, then we don't have
6245 // to do anything here since removing those styles can't require
6246 // repainting of areas that weren't in the old overflow area.
6247 Invalidate(aOverflowAreas.VisualOverflow());
6248 } else if (hasAbsPosClip || didHaveAbsPosClip) {
6249 // If we are (or were) clipped by the 'clip' property, and our
6250 // overflow area changes, it might be because the clipping changed.
6251 // The nsChangeHint_RepaintFrame for the style change will only
6252 // repaint the old overflow area, so if the overflow area has
6253 // changed (in particular, if it grows), we have to repaint the
6254 // new area here.
6255 Invalidate(aOverflowAreas.VisualOverflow());
6256 } else if (hasTransform) {
6257 // When there's a transform, changes to that style might require
6258 // repainting of the old and new overflow areas in the widget.
6259 // Repainting of the frame itself will not be required if there's
6260 // a retained layer, so we can call InvalidateLayer here
6261 // which will avoid repainting ThebesLayers if possible.
6262 // nsCSSFrameConstructor::DoApplyRenderingChangeToTree repaints
6263 // the old overflow area in the widget in response to
6264 // nsChangeHint_UpdateTransformLayer. But since the new overflow
6265 // area is not known at that time, we have to handle it here.
6266 // If the overflow area hasn't changed, then it doesn't matter that
6267 // we didn't reach here since repainting the old overflow area was enough.
6268 // If there is no transform now, then the container layer for
6269 // the transform will go away and the frame contents will change
6270 // ThebesLayers, forcing it to be invalidated, so it doesn't matter
6271 // that we didn't reach here.
6272 InvalidateLayer(aOverflowAreas.VisualOverflow(),
6273 nsDisplayItem::TYPE_TRANSFORM);
6278 void
6279 nsFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas,
6280 nsIFrame* aChildFrame)
6282 const nsStyleDisplay* disp = GetStyleDisplay();
6283 // check here also for hidden as table frames (table, tr and td) currently
6284 // don't wrap their content into a scrollable frame if overflow is specified
6285 // FIXME: Why do we check this here rather than in
6286 // FinishAndStoreOverflow (where we check NS_STYLE_OVERFLOW_CLIP)?
6287 if (!disp->IsTableClip()) {
6288 aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() +
6289 aChildFrame->GetPosition());
6293 NS_IMETHODIMP
6294 nsFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
6295 nsIFrame** aProviderFrame,
6296 PRBool* aIsChild)
6298 return DoGetParentStyleContextFrame(aPresContext, aProviderFrame, aIsChild);
6303 * This function takes a "special" frame and _if_ that frame is an anonymous
6304 * block created by an ib split it returns the block's preceding inline. This
6305 * is needed because the split inline's style context is the parent of the
6306 * anonymous block's style context.
6308 * If aFrame is not ananonymous block, null is returned.
6310 static nsIFrame*
6311 GetIBSpecialSiblingForAnonymousBlock(nsIFrame* aFrame)
6313 NS_PRECONDITION(aFrame, "Must have a non-null frame!");
6314 NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL,
6315 "GetIBSpecialSibling should not be called on a non-special frame");
6317 nsIAtom* type = aFrame->GetStyleContext()->GetPseudo();
6318 if (type != nsCSSAnonBoxes::mozAnonymousBlock &&
6319 type != nsCSSAnonBoxes::mozAnonymousPositionedBlock) {
6320 // it's not an anonymous block
6321 return nsnull;
6324 // Find the first continuation of the frame. (Ugh. This ends up
6325 // being O(N^2) when it is called O(N) times.)
6326 aFrame = aFrame->GetFirstContinuation();
6329 * Now look up the nsGkAtoms::IBSplitSpecialPrevSibling
6330 * property.
6332 nsIFrame *specialSibling = static_cast<nsIFrame*>
6333 (aFrame->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
6334 NS_ASSERTION(specialSibling, "Broken frame tree?");
6335 return specialSibling;
6339 * Get the parent, corrected for the mangled frame tree resulting from
6340 * having a block within an inline. The result only differs from the
6341 * result of |GetParent| when |GetParent| returns an anonymous block
6342 * that was created for an element that was 'display: inline' because
6343 * that element contained a block.
6345 * Also skip anonymous scrolled-content parents; inherit directly from the
6346 * outer scroll frame.
6348 static nsresult
6349 GetCorrectedParent(nsPresContext* aPresContext, nsIFrame* aFrame,
6350 nsIFrame** aSpecialParent)
6352 nsIFrame *parent = aFrame->GetParent();
6353 if (!parent) {
6354 *aSpecialParent = nsnull;
6355 } else {
6356 nsIAtom* pseudo = aFrame->GetStyleContext()->GetPseudo();
6357 // Outer tables are always anon boxes; if we're in here for an outer
6358 // table, that actually means its the _inner_ table that wants to
6359 // know its parent. So get the pseudo of the inner in that case.
6360 if (pseudo == nsCSSAnonBoxes::tableOuter) {
6361 pseudo =
6362 aFrame->GetFirstChild(nsnull)->GetStyleContext()->GetPseudo();
6364 *aSpecialParent = nsFrame::CorrectStyleParentFrame(parent, pseudo);
6367 return NS_OK;
6370 /* static */
6371 nsIFrame*
6372 nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
6373 nsIAtom* aChildPseudo)
6375 NS_PRECONDITION(aProspectiveParent, "Must have a prospective parent");
6377 // Anon boxes are parented to their actual parent already, except
6378 // for non-elements. Those should not be treated as an anon box.
6379 if (aChildPseudo && aChildPseudo != nsCSSAnonBoxes::mozNonElement &&
6380 nsCSSAnonBoxes::IsAnonBox(aChildPseudo)) {
6381 NS_ASSERTION(aChildPseudo != nsCSSAnonBoxes::mozAnonymousBlock &&
6382 aChildPseudo != nsCSSAnonBoxes::mozAnonymousPositionedBlock,
6383 "Should have dealt with kids that have NS_FRAME_IS_SPECIAL "
6384 "elsewhere");
6385 return aProspectiveParent;
6388 // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out
6389 // of all pseudo-elements as well. Otherwise ReparentStyleContext could cause
6390 // style data to be out of sync with the frame tree.
6391 nsIFrame* parent = aProspectiveParent;
6392 do {
6393 if (parent->GetStateBits() & NS_FRAME_IS_SPECIAL) {
6394 nsIFrame* sibling = GetIBSpecialSiblingForAnonymousBlock(parent);
6396 if (sibling) {
6397 // |parent| was a block in an {ib} split; use the inline as
6398 // |the style parent.
6399 parent = sibling;
6403 nsIAtom* parentPseudo = parent->GetStyleContext()->GetPseudo();
6404 if (!parentPseudo ||
6405 (!nsCSSAnonBoxes::IsAnonBox(parentPseudo) &&
6406 // nsPlaceholderFrame pases in nsGkAtoms::placeholderFrame for
6407 // aChildPseudo (even though that's not a valid pseudo-type) just to
6408 // trigger this behavior of walking up to the nearest non-pseudo
6409 // ancestor.
6410 aChildPseudo != nsGkAtoms::placeholderFrame)) {
6411 return parent;
6414 parent = parent->GetParent();
6415 } while (parent);
6417 if (aProspectiveParent->GetStyleContext()->GetPseudo() ==
6418 nsCSSAnonBoxes::viewportScroll) {
6419 // aProspectiveParent is the scrollframe for a viewport
6420 // and the kids are the anonymous scrollbars
6421 return aProspectiveParent;
6424 // We can get here if the root element is absolutely positioned.
6425 // We can't test for this very accurately, but it can only happen
6426 // when the prospective parent is a canvas frame.
6427 NS_ASSERTION(aProspectiveParent->GetType() == nsGkAtoms::canvasFrame,
6428 "Should have found a parent before this");
6429 return nsnull;
6432 nsresult
6433 nsFrame::DoGetParentStyleContextFrame(nsPresContext* aPresContext,
6434 nsIFrame** aProviderFrame,
6435 PRBool* aIsChild)
6437 *aIsChild = PR_FALSE;
6438 *aProviderFrame = nsnull;
6439 if (mContent && !mContent->GetParent() &&
6440 !GetStyleContext()->GetPseudo()) {
6441 // we're a frame for the root. We have no style context parent.
6442 return NS_OK;
6445 if (!(mState & NS_FRAME_OUT_OF_FLOW)) {
6447 * If this frame is an anonymous block created when an inline with a block
6448 * inside it got split, then the parent style context is on its preceding
6449 * inline. We can get to it using GetIBSpecialSiblingForAnonymousBlock.
6451 if (mState & NS_FRAME_IS_SPECIAL) {
6452 *aProviderFrame = GetIBSpecialSiblingForAnonymousBlock(this);
6454 if (*aProviderFrame) {
6455 return NS_OK;
6459 // If this frame is one of the blocks that split an inline, we must
6460 // return the "special" inline parent, i.e., the parent that this
6461 // frame would have if we didn't mangle the frame structure.
6462 return GetCorrectedParent(aPresContext, this, aProviderFrame);
6465 // For out-of-flow frames, we must resolve underneath the
6466 // placeholder's parent.
6467 nsIFrame* oofFrame = this;
6468 if ((oofFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
6469 GetPrevInFlow()) {
6470 // Out of flows that are continuations do not
6471 // have placeholders. Use their first-in-flow's placeholder.
6472 oofFrame = oofFrame->GetFirstInFlow();
6474 nsIFrame *placeholder =
6475 aPresContext->FrameManager()->GetPlaceholderFrameFor(oofFrame);
6476 if (!placeholder) {
6477 NS_NOTREACHED("no placeholder frame for out-of-flow frame");
6478 GetCorrectedParent(aPresContext, this, aProviderFrame);
6479 return NS_ERROR_FAILURE;
6481 return static_cast<nsFrame*>(placeholder)->
6482 GetParentStyleContextFrame(aPresContext, aProviderFrame, aIsChild);
6485 //-----------------------------------------------------------------------------------
6490 void
6491 nsFrame::GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame)
6493 if (!aFrame || !*aFrame)
6494 return;
6495 nsIFrame *child = *aFrame;
6496 //if we are a block frame then go for the last line of 'this'
6497 while (1){
6498 child = child->GetFirstChild(nsnull);
6499 if (!child)
6500 return;//nothing to do
6501 nsIFrame* siblingFrame;
6502 nsIContent* content;
6503 //ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
6504 //see bug 278197 comment #12 #13 for details
6505 while ((siblingFrame = child->GetNextSibling()) &&
6506 (content = siblingFrame->GetContent()) &&
6507 !content->IsRootOfNativeAnonymousSubtree())
6508 child = siblingFrame;
6509 *aFrame = child;
6513 void
6514 nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame)
6516 if (!aFrame || !*aFrame)
6517 return;
6518 nsIFrame *child = *aFrame;
6519 while (1){
6520 child = child->GetFirstChild(nsnull);
6521 if (!child)
6522 return;//nothing to do
6523 *aFrame = child;
6527 /* virtual */ const void*
6528 nsFrame::GetStyleDataExternal(nsStyleStructID aSID) const
6530 NS_ASSERTION(mStyleContext, "unexpected null pointer");
6531 return mStyleContext->GetStyleData(aSID);
6534 /* virtual */ PRBool
6535 nsIFrame::IsFocusable(PRInt32 *aTabIndex, PRBool aWithMouse)
6537 PRInt32 tabIndex = -1;
6538 if (aTabIndex) {
6539 *aTabIndex = -1; // Default for early return is not focusable
6541 PRBool isFocusable = PR_FALSE;
6543 if (mContent && mContent->IsElement() && AreAncestorViewsVisible()) {
6544 const nsStyleVisibility* vis = GetStyleVisibility();
6545 if (vis->mVisible != NS_STYLE_VISIBILITY_COLLAPSE &&
6546 vis->mVisible != NS_STYLE_VISIBILITY_HIDDEN) {
6547 const nsStyleUserInterface* ui = GetStyleUserInterface();
6548 if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
6549 ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
6550 // Pass in default tabindex of -1 for nonfocusable and 0 for focusable
6551 tabIndex = 0;
6553 isFocusable = mContent->IsFocusable(&tabIndex, aWithMouse);
6554 if (!isFocusable && !aWithMouse &&
6555 GetType() == nsGkAtoms::scrollFrame &&
6556 mContent->IsHTML() &&
6557 !mContent->IsRootOfNativeAnonymousSubtree() &&
6558 mContent->GetParent() &&
6559 !mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
6560 // Elements with scrollable view are focusable with script & tabbable
6561 // Otherwise you couldn't scroll them with keyboard, which is
6562 // an accessibility issue (e.g. Section 508 rules)
6563 // However, we don't make them to be focusable with the mouse,
6564 // because the extra focus outlines are considered unnecessarily ugly.
6565 // When clicked on, the selection position within the element
6566 // will be enough to make them keyboard scrollable.
6567 nsIScrollableFrame *scrollFrame = do_QueryFrame(this);
6568 if (scrollFrame) {
6569 nsIScrollableFrame::ScrollbarStyles styles =
6570 scrollFrame->GetScrollbarStyles();
6571 if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL ||
6572 styles.mVertical == NS_STYLE_OVERFLOW_AUTO ||
6573 styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL ||
6574 styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO) {
6575 // Scroll bars will be used for overflow
6576 isFocusable = PR_TRUE;
6577 tabIndex = 0;
6584 if (aTabIndex) {
6585 *aTabIndex = tabIndex;
6587 return isFocusable;
6591 * @return PR_TRUE if this text frame ends with a newline character. It
6592 * should return PR_FALSE if this is not a text frame.
6594 PRBool
6595 nsIFrame::HasTerminalNewline() const
6597 return PR_FALSE;
6600 /* static */
6601 void nsFrame::FillCursorInformationFromStyle(const nsStyleUserInterface* ui,
6602 nsIFrame::Cursor& aCursor)
6604 aCursor.mCursor = ui->mCursor;
6605 aCursor.mHaveHotspot = PR_FALSE;
6606 aCursor.mHotspotX = aCursor.mHotspotY = 0.0f;
6608 for (nsCursorImage *item = ui->mCursorArray,
6609 *item_end = ui->mCursorArray + ui->mCursorArrayLength;
6610 item < item_end; ++item) {
6611 PRUint32 status;
6612 nsresult rv = item->GetImage()->GetImageStatus(&status);
6613 if (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_LOAD_COMPLETE)) {
6614 // This is the one we want
6615 item->GetImage()->GetImage(getter_AddRefs(aCursor.mContainer));
6616 aCursor.mHaveHotspot = item->mHaveHotspot;
6617 aCursor.mHotspotX = item->mHotspotX;
6618 aCursor.mHotspotY = item->mHotspotY;
6619 break;
6624 NS_IMETHODIMP
6625 nsFrame::RefreshSizeCache(nsBoxLayoutState& aState)
6627 // XXXbz this comment needs some rewriting to make sense in the
6628 // post-reflow-branch world.
6630 // Ok we need to compute our minimum, preferred, and maximum sizes.
6631 // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS.
6632 // 2) Preferred size. This is a little harder. This is the size the block would be
6633 // if it were laid out on an infinite canvas. So we can get this by reflowing
6634 // the block with and INTRINSIC width and height. We can also do a nice optimization
6635 // for incremental reflow. If the reflow is incremental then we can pass a flag to
6636 // have the block compute the preferred width for us! Preferred height can just be
6637 // the minimum height;
6638 // 3) Minimum size. This is a toughy. We can pass the block a flag asking for the max element
6639 // size. That would give us the width. Unfortunately you can only ask for a maxElementSize
6640 // during an incremental reflow. So on other reflows we will just have to use 0.
6641 // The min height on the other hand is fairly easy we need to get the largest
6642 // line height. This can be done with the line iterator.
6644 // if we do have a rendering context
6645 nsresult rv = NS_OK;
6646 nsIRenderingContext* rendContext = aState.GetRenderingContext();
6647 if (rendContext) {
6648 nsPresContext* presContext = aState.PresContext();
6650 // If we don't have any HTML constraints and it's a resize, then nothing in the block
6651 // could have changed, so no refresh is necessary.
6652 nsBoxLayoutMetrics* metrics = BoxMetrics();
6653 if (!DoesNeedRecalc(metrics->mBlockPrefSize))
6654 return NS_OK;
6656 // get the old rect.
6657 nsRect oldRect = GetRect();
6659 // the rect we plan to size to.
6660 nsRect rect(oldRect);
6662 nsMargin bp(0,0,0,0);
6663 GetBorderAndPadding(bp);
6665 metrics->mBlockPrefSize.width = GetPrefWidth(rendContext) + bp.LeftRight();
6666 metrics->mBlockMinSize.width = GetMinWidth(rendContext) + bp.LeftRight();
6668 // do the nasty.
6669 nsHTMLReflowMetrics desiredSize;
6670 rv = BoxReflow(aState, presContext, desiredSize, rendContext,
6671 rect.x, rect.y,
6672 metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE);
6674 nsRect newRect = GetRect();
6676 // make sure we draw any size change
6677 if (oldRect.width != newRect.width || oldRect.height != newRect.height) {
6678 newRect.x = 0;
6679 newRect.y = 0;
6680 Redraw(aState, &newRect);
6683 metrics->mBlockMinSize.height = 0;
6684 // ok we need the max ascent of the items on the line. So to do this
6685 // ask the block for its line iterator. Get the max ascent.
6686 nsAutoLineIterator lines = GetLineIterator();
6687 if (lines)
6689 metrics->mBlockMinSize.height = 0;
6690 int count = 0;
6691 nsIFrame* firstFrame = nsnull;
6692 PRInt32 framesOnLine;
6693 nsRect lineBounds;
6694 PRUint32 lineFlags;
6696 do {
6697 lines->GetLine(count, &firstFrame, &framesOnLine, lineBounds, &lineFlags);
6699 if (lineBounds.height > metrics->mBlockMinSize.height)
6700 metrics->mBlockMinSize.height = lineBounds.height;
6702 count++;
6703 } while(firstFrame);
6704 } else {
6705 metrics->mBlockMinSize.height = desiredSize.height;
6708 metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height;
6710 if (desiredSize.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
6711 if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mBlockAscent))
6712 metrics->mBlockAscent = GetBaseline();
6713 } else {
6714 metrics->mBlockAscent = desiredSize.ascent;
6717 #ifdef DEBUG_adaptor
6718 printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n", metrics->mBlockMinSize.width,
6719 metrics->mBlockMinSize.height,
6720 metrics->mBlockPrefSize.width,
6721 metrics->mBlockPrefSize.height,
6722 metrics->mBlockAscent);
6723 #endif
6726 return rv;
6729 /* virtual */ nsILineIterator*
6730 nsFrame::GetLineIterator()
6732 return nsnull;
6735 nsSize
6736 nsFrame::GetPrefSize(nsBoxLayoutState& aState)
6738 nsSize size(0,0);
6739 DISPLAY_PREF_SIZE(this, size);
6740 // If the size is cached, and there are no HTML constraints that we might
6741 // be depending on, then we just return the cached size.
6742 nsBoxLayoutMetrics *metrics = BoxMetrics();
6743 if (!DoesNeedRecalc(metrics->mPrefSize)) {
6744 return metrics->mPrefSize;
6747 if (IsCollapsed(aState))
6748 return size;
6750 // get our size in CSS.
6751 PRBool widthSet, heightSet;
6752 PRBool completelyRedefined = nsIBox::AddCSSPrefSize(this, size, widthSet, heightSet);
6754 // Refresh our caches with new sizes.
6755 if (!completelyRedefined) {
6756 RefreshSizeCache(aState);
6757 nsSize blockSize = metrics->mBlockPrefSize;
6759 // notice we don't need to add our borders or padding
6760 // in. That's because the block did it for us.
6761 if (!widthSet)
6762 size.width = blockSize.width;
6763 if (!heightSet)
6764 size.height = blockSize.height;
6767 metrics->mPrefSize = size;
6768 return size;
6771 nsSize
6772 nsFrame::GetMinSize(nsBoxLayoutState& aState)
6774 nsSize size(0,0);
6775 DISPLAY_MIN_SIZE(this, size);
6776 // Don't use the cache if we have HTMLReflowState constraints --- they might have changed
6777 nsBoxLayoutMetrics *metrics = BoxMetrics();
6778 if (!DoesNeedRecalc(metrics->mMinSize)) {
6779 size = metrics->mMinSize;
6780 return size;
6783 if (IsCollapsed(aState))
6784 return size;
6786 // get our size in CSS.
6787 PRBool widthSet, heightSet;
6788 PRBool completelyRedefined =
6789 nsIBox::AddCSSMinSize(aState, this, size, widthSet, heightSet);
6791 // Refresh our caches with new sizes.
6792 if (!completelyRedefined) {
6793 RefreshSizeCache(aState);
6794 nsSize blockSize = metrics->mBlockMinSize;
6796 if (!widthSet)
6797 size.width = blockSize.width;
6798 if (!heightSet)
6799 size.height = blockSize.height;
6802 metrics->mMinSize = size;
6803 return size;
6806 nsSize
6807 nsFrame::GetMaxSize(nsBoxLayoutState& aState)
6809 nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
6810 DISPLAY_MAX_SIZE(this, size);
6811 // Don't use the cache if we have HTMLReflowState constraints --- they might have changed
6812 nsBoxLayoutMetrics *metrics = BoxMetrics();
6813 if (!DoesNeedRecalc(metrics->mMaxSize)) {
6814 size = metrics->mMaxSize;
6815 return size;
6818 if (IsCollapsed(aState))
6819 return size;
6821 size = nsBox::GetMaxSize(aState);
6822 metrics->mMaxSize = size;
6824 return size;
6827 nscoord
6828 nsFrame::GetFlex(nsBoxLayoutState& aState)
6830 nsBoxLayoutMetrics *metrics = BoxMetrics();
6831 if (!DoesNeedRecalc(metrics->mFlex))
6832 return metrics->mFlex;
6834 metrics->mFlex = nsBox::GetFlex(aState);
6836 return metrics->mFlex;
6839 nscoord
6840 nsFrame::GetBoxAscent(nsBoxLayoutState& aState)
6842 nsBoxLayoutMetrics *metrics = BoxMetrics();
6843 if (!DoesNeedRecalc(metrics->mAscent))
6844 return metrics->mAscent;
6846 if (IsCollapsed(aState)) {
6847 metrics->mAscent = 0;
6848 } else {
6849 // Refresh our caches with new sizes.
6850 RefreshSizeCache(aState);
6851 metrics->mAscent = metrics->mBlockAscent;
6854 return metrics->mAscent;
6857 nsresult
6858 nsFrame::DoLayout(nsBoxLayoutState& aState)
6860 nsRect ourRect(mRect);
6862 nsIRenderingContext* rendContext = aState.GetRenderingContext();
6863 nsPresContext* presContext = aState.PresContext();
6864 nsHTMLReflowMetrics desiredSize;
6865 nsresult rv = NS_OK;
6867 if (rendContext) {
6869 rv = BoxReflow(aState, presContext, desiredSize, rendContext,
6870 ourRect.x, ourRect.y, ourRect.width, ourRect.height);
6872 if (IsCollapsed(aState)) {
6873 SetSize(nsSize(0, 0));
6874 } else {
6876 // if our child needs to be bigger. This might happend with
6877 // wrapping text. There is no way to predict its height until we
6878 // reflow it. Now that we know the height reshuffle upward.
6879 if (desiredSize.width > ourRect.width ||
6880 desiredSize.height > ourRect.height) {
6882 #ifdef DEBUG_GROW
6883 DumpBox(stdout);
6884 printf(" GREW from (%d,%d) -> (%d,%d)\n",
6885 ourRect.width, ourRect.height,
6886 desiredSize.width, desiredSize.height);
6887 #endif
6889 if (desiredSize.width > ourRect.width)
6890 ourRect.width = desiredSize.width;
6892 if (desiredSize.height > ourRect.height)
6893 ourRect.height = desiredSize.height;
6896 // ensure our size is what we think is should be. Someone could have
6897 // reset the frame to be smaller or something dumb like that.
6898 SetSize(nsSize(ourRect.width, ourRect.height));
6902 // Should we do this if IsCollapsed() is true?
6903 nsSize size(GetSize());
6904 desiredSize.width = size.width;
6905 desiredSize.height = size.height;
6906 desiredSize.UnionOverflowAreasWithDesiredBounds();
6907 FinishAndStoreOverflow(desiredSize.mOverflowAreas, size);
6909 SyncLayout(aState);
6911 return rv;
6914 nsresult
6915 nsFrame::BoxReflow(nsBoxLayoutState& aState,
6916 nsPresContext* aPresContext,
6917 nsHTMLReflowMetrics& aDesiredSize,
6918 nsIRenderingContext* aRenderingContext,
6919 nscoord aX,
6920 nscoord aY,
6921 nscoord aWidth,
6922 nscoord aHeight,
6923 PRBool aMoveFrame)
6925 DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor");
6927 #ifdef DEBUG_REFLOW
6928 nsAdaptorAddIndents();
6929 printf("Reflowing: ");
6930 nsFrame::ListTag(stdout, mFrame);
6931 printf("\n");
6932 gIndent2++;
6933 #endif
6935 //printf("width=%d, height=%d\n", aWidth, aHeight);
6937 nsIBox* parent;
6938 GetParentBox(&parent);
6940 // if (parent->GetStateBits() & NS_STATE_CURRENTLY_IN_DEBUG)
6941 // printf("In debug\n");
6944 nsBoxLayoutMetrics *metrics = BoxMetrics();
6945 nsReflowStatus status = NS_FRAME_COMPLETE;
6947 PRBool redrawAfterReflow = PR_FALSE;
6948 PRBool redrawNow = PR_FALSE;
6950 PRBool needsReflow = NS_SUBTREE_DIRTY(this);
6952 if (redrawNow)
6953 Redraw(aState);
6955 // if we don't need a reflow then
6956 // lets see if we are already that size. Yes? then don't even reflow. We are done.
6957 if (!needsReflow) {
6959 if (aWidth != NS_INTRINSICSIZE && aHeight != NS_INTRINSICSIZE) {
6961 // if the new calculated size has a 0 width or a 0 height
6962 if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) && (aWidth == 0 || aHeight == 0)) {
6963 needsReflow = PR_FALSE;
6964 aDesiredSize.width = aWidth;
6965 aDesiredSize.height = aHeight;
6966 SetSize(nsSize(aDesiredSize.width, aDesiredSize.height));
6967 } else {
6968 aDesiredSize.width = metrics->mLastSize.width;
6969 aDesiredSize.height = metrics->mLastSize.height;
6971 // remove the margin. The rect of our child does not include it but our calculated size does.
6972 // don't reflow if we are already the right size
6973 if (metrics->mLastSize.width == aWidth && metrics->mLastSize.height == aHeight)
6974 needsReflow = PR_FALSE;
6975 else
6976 needsReflow = PR_TRUE;
6979 } else {
6980 // if the width or height are intrinsic alway reflow because
6981 // we don't know what it should be.
6982 needsReflow = PR_TRUE;
6986 // ok now reflow the child into the spacers calculated space
6987 if (needsReflow) {
6989 aDesiredSize.width = 0;
6990 aDesiredSize.height = 0;
6992 // create a reflow state to tell our child to flow at the given size.
6994 // Construct a bogus parent reflow state so that there's a usable
6995 // containing block reflow state.
6996 nsMargin margin(0,0,0,0);
6997 GetMargin(margin);
6999 nsSize parentSize(aWidth, aHeight);
7000 if (parentSize.height != NS_INTRINSICSIZE)
7001 parentSize.height += margin.TopBottom();
7002 if (parentSize.width != NS_INTRINSICSIZE)
7003 parentSize.width += margin.LeftRight();
7005 nsIFrame *parentFrame = GetParent();
7006 nsFrameState savedState = parentFrame->GetStateBits();
7007 nsHTMLReflowState parentReflowState(aPresContext, parentFrame,
7008 aRenderingContext,
7009 parentSize);
7010 parentFrame->RemoveStateBits(~nsFrameState(0));
7011 parentFrame->AddStateBits(savedState);
7013 // This may not do very much useful, but it's probably worth trying.
7014 if (parentSize.width != NS_INTRINSICSIZE)
7015 parentReflowState.SetComputedWidth(NS_MAX(parentSize.width, 0));
7016 if (parentSize.height != NS_INTRINSICSIZE)
7017 parentReflowState.SetComputedHeight(NS_MAX(parentSize.height, 0));
7018 parentReflowState.mComputedMargin.SizeTo(0, 0, 0, 0);
7019 // XXX use box methods
7020 parentFrame->GetPadding(parentReflowState.mComputedPadding);
7021 parentFrame->GetBorder(parentReflowState.mComputedBorderPadding);
7022 parentReflowState.mComputedBorderPadding +=
7023 parentReflowState.mComputedPadding;
7025 // XXX Is it OK that this reflow state has no parent reflow state?
7026 // (It used to have a bogus parent, skipping all the boxes).
7027 nsSize availSize(aWidth, NS_INTRINSICSIZE);
7028 nsHTMLReflowState reflowState(aPresContext, this, aRenderingContext,
7029 availSize);
7031 // Construct the parent chain manually since constructing it normally
7032 // messes up dimensions.
7033 reflowState.parentReflowState = &parentReflowState;
7034 reflowState.mCBReflowState = &parentReflowState;
7035 reflowState.mReflowDepth = aState.GetReflowDepth();
7037 // mComputedWidth and mComputedHeight are content-box, not
7038 // border-box
7039 if (aWidth != NS_INTRINSICSIZE) {
7040 nscoord computedWidth =
7041 aWidth - reflowState.mComputedBorderPadding.LeftRight();
7042 computedWidth = NS_MAX(computedWidth, 0);
7043 reflowState.SetComputedWidth(computedWidth);
7046 // Most child frames of box frames (e.g. subdocument or scroll frames)
7047 // need to be constrained to the provided size and overflow as necessary.
7048 // The one exception are block frames, because we need to know their
7049 // natural height excluding any overflow area which may be caused by
7050 // various CSS effects such as shadow or outline.
7051 if (!IsFrameOfType(eBlockFrame)) {
7052 if (aHeight != NS_INTRINSICSIZE) {
7053 nscoord computedHeight =
7054 aHeight - reflowState.mComputedBorderPadding.TopBottom();
7055 computedHeight = NS_MAX(computedHeight, 0);
7056 reflowState.SetComputedHeight(computedHeight);
7057 } else {
7058 reflowState.SetComputedHeight(
7059 ComputeSize(aRenderingContext, availSize, availSize.width,
7060 nsSize(reflowState.mComputedMargin.LeftRight(),
7061 reflowState.mComputedMargin.TopBottom()),
7062 nsSize(reflowState.mComputedBorderPadding.LeftRight() -
7063 reflowState.mComputedPadding.LeftRight(),
7064 reflowState.mComputedBorderPadding.TopBottom() -
7065 reflowState.mComputedPadding.TopBottom()),
7066 nsSize(reflowState.mComputedPadding.LeftRight(),
7067 reflowState.mComputedPadding.TopBottom()),
7068 PR_FALSE).height
7073 // Box layout calls SetRect before Layout, whereas non-box layout
7074 // calls SetRect after Reflow.
7075 // XXX Perhaps we should be doing this by twiddling the rect back to
7076 // mLastSize before calling Reflow and then switching it back, but
7077 // However, mLastSize can also be the size passed to BoxReflow by
7078 // RefreshSizeCache, so that doesn't really make sense.
7079 if (metrics->mLastSize.width != aWidth)
7080 reflowState.mFlags.mHResize = PR_TRUE;
7081 if (metrics->mLastSize.height != aHeight)
7082 reflowState.mFlags.mVResize = PR_TRUE;
7084 #ifdef DEBUG_REFLOW
7085 nsAdaptorAddIndents();
7086 printf("Size=(%d,%d)\n",reflowState.ComputedWidth(),
7087 reflowState.ComputedHeight());
7088 nsAdaptorAddIndents();
7089 nsAdaptorPrintReason(reflowState);
7090 printf("\n");
7091 #endif
7093 // place the child and reflow
7094 WillReflow(aPresContext);
7096 Reflow(aPresContext, aDesiredSize, reflowState, status);
7098 NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status");
7100 if (redrawAfterReflow) {
7101 nsRect r = GetRect();
7102 r.width = aDesiredSize.width;
7103 r.height = aDesiredSize.height;
7104 Redraw(aState, &r);
7107 PRUint32 layoutFlags = aState.LayoutFlags();
7108 nsContainerFrame::FinishReflowChild(this, aPresContext, &reflowState,
7109 aDesiredSize, aX, aY, layoutFlags | NS_FRAME_NO_MOVE_FRAME);
7111 // Save the ascent. (bug 103925)
7112 if (IsCollapsed(aState)) {
7113 metrics->mAscent = 0;
7114 } else {
7115 if (aDesiredSize.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
7116 if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mAscent))
7117 metrics->mAscent = GetBaseline();
7118 } else
7119 metrics->mAscent = aDesiredSize.ascent;
7122 } else {
7123 aDesiredSize.ascent = metrics->mBlockAscent;
7126 #ifdef DEBUG_REFLOW
7127 if (aHeight != NS_INTRINSICSIZE && aDesiredSize.height != aHeight)
7129 nsAdaptorAddIndents();
7130 printf("*****got taller!*****\n");
7133 if (aWidth != NS_INTRINSICSIZE && aDesiredSize.width != aWidth)
7135 nsAdaptorAddIndents();
7136 printf("*****got wider!******\n");
7139 #endif
7141 if (aWidth == NS_INTRINSICSIZE)
7142 aWidth = aDesiredSize.width;
7144 if (aHeight == NS_INTRINSICSIZE)
7145 aHeight = aDesiredSize.height;
7147 metrics->mLastSize.width = aDesiredSize.width;
7148 metrics->mLastSize.height = aDesiredSize.height;
7150 #ifdef DEBUG_REFLOW
7151 gIndent2--;
7152 #endif
7154 return NS_OK;
7157 static void
7158 DestroyBoxMetrics(void* aPropertyValue)
7160 delete static_cast<nsBoxLayoutMetrics*>(aPropertyValue);
7163 NS_DECLARE_FRAME_PROPERTY(BoxMetricsProperty, DestroyBoxMetrics)
7165 nsBoxLayoutMetrics*
7166 nsFrame::BoxMetrics() const
7168 nsBoxLayoutMetrics* metrics =
7169 static_cast<nsBoxLayoutMetrics*>(Properties().Get(BoxMetricsProperty()));
7170 NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called");
7171 return metrics;
7174 void
7175 nsFrame::SetParent(nsIFrame* aParent)
7177 PRBool wasBoxWrapped = IsBoxWrapped();
7178 mParent = aParent;
7179 if (!wasBoxWrapped && IsBoxWrapped()) {
7180 InitBoxMetrics(PR_TRUE);
7181 } else if (wasBoxWrapped && !IsBoxWrapped()) {
7182 Properties().Delete(BoxMetricsProperty());
7185 if (GetStateBits() & (NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) {
7186 for (nsIFrame* f = aParent;
7187 f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
7188 f = f->GetParent()) {
7189 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
7193 if (GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT) {
7194 for (nsIFrame* f = aParent;
7195 f && !(f->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT);
7196 f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
7197 f->AddStateBits(NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT);
7202 void
7203 nsFrame::InitBoxMetrics(PRBool aClear)
7205 FrameProperties props = Properties();
7206 if (aClear) {
7207 props.Delete(BoxMetricsProperty());
7210 nsBoxLayoutMetrics *metrics = new nsBoxLayoutMetrics();
7211 props.Set(BoxMetricsProperty(), metrics);
7213 nsFrame::MarkIntrinsicWidthsDirty();
7214 metrics->mBlockAscent = 0;
7215 metrics->mLastSize.SizeTo(0, 0);
7218 // Box layout debugging
7219 #ifdef DEBUG_REFLOW
7220 PRInt32 gIndent2 = 0;
7222 void
7223 nsAdaptorAddIndents()
7225 for(PRInt32 i=0; i < gIndent2; i++)
7227 printf(" ");
7231 void
7232 nsAdaptorPrintReason(nsHTMLReflowState& aReflowState)
7234 char* reflowReasonString;
7236 switch(aReflowState.reason)
7238 case eReflowReason_Initial:
7239 reflowReasonString = "initial";
7240 break;
7242 case eReflowReason_Resize:
7243 reflowReasonString = "resize";
7244 break;
7245 case eReflowReason_Dirty:
7246 reflowReasonString = "dirty";
7247 break;
7248 case eReflowReason_StyleChange:
7249 reflowReasonString = "stylechange";
7250 break;
7251 case eReflowReason_Incremental:
7253 switch (aReflowState.reflowCommand->Type()) {
7254 case eReflowType_StyleChanged:
7255 reflowReasonString = "incremental (StyleChanged)";
7256 break;
7257 case eReflowType_ReflowDirty:
7258 reflowReasonString = "incremental (ReflowDirty)";
7259 break;
7260 default:
7261 reflowReasonString = "incremental (Unknown)";
7264 break;
7265 default:
7266 reflowReasonString = "unknown";
7267 break;
7270 printf("%s",reflowReasonString);
7273 #endif
7274 #ifdef DEBUG_LAYOUT
7275 void
7276 nsFrame::GetBoxName(nsAutoString& aName)
7278 GetFrameName(aName);
7280 #endif
7282 #ifdef NS_DEBUG
7283 static void
7284 GetTagName(nsFrame* aFrame, nsIContent* aContent, PRIntn aResultSize,
7285 char* aResult)
7287 if (aContent) {
7288 PR_snprintf(aResult, aResultSize, "%s@%p",
7289 nsAtomCString(aContent->Tag()).get(), aFrame);
7291 else {
7292 PR_snprintf(aResult, aResultSize, "@%p", aFrame);
7296 void
7297 nsFrame::Trace(const char* aMethod, PRBool aEnter)
7299 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
7300 char tagbuf[40];
7301 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
7302 PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
7306 void
7307 nsFrame::Trace(const char* aMethod, PRBool aEnter, nsReflowStatus aStatus)
7309 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
7310 char tagbuf[40];
7311 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
7312 PR_LogPrint("%s: %s %s, status=%scomplete%s",
7313 tagbuf, aEnter ? "enter" : "exit", aMethod,
7314 NS_FRAME_IS_NOT_COMPLETE(aStatus) ? "not" : "",
7315 (NS_FRAME_REFLOW_NEXTINFLOW & aStatus) ? "+reflow" : "");
7319 void
7320 nsFrame::TraceMsg(const char* aFormatString, ...)
7322 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
7323 // Format arguments into a buffer
7324 char argbuf[200];
7325 va_list ap;
7326 va_start(ap, aFormatString);
7327 PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap);
7328 va_end(ap);
7330 char tagbuf[40];
7331 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
7332 PR_LogPrint("%s: %s", tagbuf, argbuf);
7336 void
7337 nsFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList)
7339 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
7340 NS_ASSERTION(e.get()->GetStateBits() & NS_FRAME_IS_DIRTY,
7341 "dirty bit not set");
7345 // Start Display Reflow
7346 #ifdef DEBUG
7348 DR_cookie::DR_cookie(nsPresContext* aPresContext,
7349 nsIFrame* aFrame,
7350 const nsHTMLReflowState& aReflowState,
7351 nsHTMLReflowMetrics& aMetrics,
7352 nsReflowStatus& aStatus)
7353 :mPresContext(aPresContext), mFrame(aFrame), mReflowState(aReflowState), mMetrics(aMetrics), mStatus(aStatus)
7355 MOZ_COUNT_CTOR(DR_cookie);
7356 mValue = nsFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowState);
7359 DR_cookie::~DR_cookie()
7361 MOZ_COUNT_DTOR(DR_cookie);
7362 nsFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue);
7365 DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame)
7366 : mFrame(aFrame)
7368 MOZ_COUNT_CTOR(DR_layout_cookie);
7369 mValue = nsFrame::DisplayLayoutEnter(mFrame);
7372 DR_layout_cookie::~DR_layout_cookie()
7374 MOZ_COUNT_DTOR(DR_layout_cookie);
7375 nsFrame::DisplayLayoutExit(mFrame, mValue);
7378 DR_intrinsic_width_cookie::DR_intrinsic_width_cookie(
7379 nsIFrame* aFrame,
7380 const char* aType,
7381 nscoord& aResult)
7382 : mFrame(aFrame)
7383 , mType(aType)
7384 , mResult(aResult)
7386 MOZ_COUNT_CTOR(DR_intrinsic_width_cookie);
7387 mValue = nsFrame::DisplayIntrinsicWidthEnter(mFrame, mType);
7390 DR_intrinsic_width_cookie::~DR_intrinsic_width_cookie()
7392 MOZ_COUNT_DTOR(DR_intrinsic_width_cookie);
7393 nsFrame::DisplayIntrinsicWidthExit(mFrame, mType, mResult, mValue);
7396 DR_intrinsic_size_cookie::DR_intrinsic_size_cookie(
7397 nsIFrame* aFrame,
7398 const char* aType,
7399 nsSize& aResult)
7400 : mFrame(aFrame)
7401 , mType(aType)
7402 , mResult(aResult)
7404 MOZ_COUNT_CTOR(DR_intrinsic_size_cookie);
7405 mValue = nsFrame::DisplayIntrinsicSizeEnter(mFrame, mType);
7408 DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie()
7410 MOZ_COUNT_DTOR(DR_intrinsic_size_cookie);
7411 nsFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue);
7414 DR_init_constraints_cookie::DR_init_constraints_cookie(
7415 nsIFrame* aFrame,
7416 nsHTMLReflowState* aState,
7417 nscoord aCBWidth,
7418 nscoord aCBHeight,
7419 const nsMargin* aMargin,
7420 const nsMargin* aPadding)
7421 : mFrame(aFrame)
7422 , mState(aState)
7424 MOZ_COUNT_CTOR(DR_init_constraints_cookie);
7425 mValue = nsHTMLReflowState::DisplayInitConstraintsEnter(mFrame, mState,
7426 aCBWidth, aCBHeight,
7427 aMargin, aPadding);
7430 DR_init_constraints_cookie::~DR_init_constraints_cookie()
7432 MOZ_COUNT_DTOR(DR_init_constraints_cookie);
7433 nsHTMLReflowState::DisplayInitConstraintsExit(mFrame, mState, mValue);
7436 DR_init_offsets_cookie::DR_init_offsets_cookie(
7437 nsIFrame* aFrame,
7438 nsCSSOffsetState* aState,
7439 nscoord aCBWidth,
7440 const nsMargin* aMargin,
7441 const nsMargin* aPadding)
7442 : mFrame(aFrame)
7443 , mState(aState)
7445 MOZ_COUNT_CTOR(DR_init_offsets_cookie);
7446 mValue = nsCSSOffsetState::DisplayInitOffsetsEnter(mFrame, mState, aCBWidth,
7447 aMargin, aPadding);
7450 DR_init_offsets_cookie::~DR_init_offsets_cookie()
7452 MOZ_COUNT_DTOR(DR_init_offsets_cookie);
7453 nsCSSOffsetState::DisplayInitOffsetsExit(mFrame, mState, mValue);
7456 DR_init_type_cookie::DR_init_type_cookie(
7457 nsIFrame* aFrame,
7458 nsHTMLReflowState* aState)
7459 : mFrame(aFrame)
7460 , mState(aState)
7462 MOZ_COUNT_CTOR(DR_init_type_cookie);
7463 mValue = nsHTMLReflowState::DisplayInitFrameTypeEnter(mFrame, mState);
7466 DR_init_type_cookie::~DR_init_type_cookie()
7468 MOZ_COUNT_DTOR(DR_init_type_cookie);
7469 nsHTMLReflowState::DisplayInitFrameTypeExit(mFrame, mState, mValue);
7472 struct DR_FrameTypeInfo;
7473 struct DR_FrameTreeNode;
7474 struct DR_Rule;
7476 struct DR_State
7478 DR_State();
7479 ~DR_State();
7480 void Init();
7481 void AddFrameTypeInfo(nsIAtom* aFrameType,
7482 const char* aFrameNameAbbrev,
7483 const char* aFrameName);
7484 DR_FrameTypeInfo* GetFrameTypeInfo(nsIAtom* aFrameType);
7485 DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName);
7486 void InitFrameTypeTable();
7487 DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame,
7488 const nsHTMLReflowState* aReflowState);
7489 void FindMatchingRule(DR_FrameTreeNode& aNode);
7490 PRBool RuleMatches(DR_Rule& aRule,
7491 DR_FrameTreeNode& aNode);
7492 PRBool GetToken(FILE* aFile,
7493 char* aBuf,
7494 size_t aBufSize);
7495 DR_Rule* ParseRule(FILE* aFile);
7496 void ParseRulesFile();
7497 void AddRule(nsTArray<DR_Rule*>& aRules,
7498 DR_Rule& aRule);
7499 PRBool IsWhiteSpace(int c);
7500 PRBool GetNumber(char* aBuf,
7501 PRInt32& aNumber);
7502 void PrettyUC(nscoord aSize,
7503 char* aBuf);
7504 void PrintMargin(const char* tag, const nsMargin* aMargin);
7505 void DisplayFrameTypeInfo(nsIFrame* aFrame,
7506 PRInt32 aIndent);
7507 void DeleteTreeNode(DR_FrameTreeNode& aNode);
7509 PRBool mInited;
7510 PRBool mActive;
7511 PRInt32 mCount;
7512 PRInt32 mAssert;
7513 PRInt32 mIndent;
7514 PRBool mIndentUndisplayedFrames;
7515 PRBool mDisplayPixelErrors;
7516 nsTArray<DR_Rule*> mWildRules;
7517 nsTArray<DR_FrameTypeInfo> mFrameTypeTable;
7518 // reflow specific state
7519 nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves;
7522 static DR_State *DR_state; // the one and only DR_State
7524 struct DR_RulePart
7526 DR_RulePart(nsIAtom* aFrameType) : mFrameType(aFrameType), mNext(0) {}
7527 void Destroy();
7529 nsIAtom* mFrameType;
7530 DR_RulePart* mNext;
7533 void DR_RulePart::Destroy()
7535 if (mNext) {
7536 mNext->Destroy();
7538 delete this;
7541 struct DR_Rule
7543 DR_Rule() : mLength(0), mTarget(nsnull), mDisplay(PR_FALSE) {
7544 MOZ_COUNT_CTOR(DR_Rule);
7546 ~DR_Rule() {
7547 if (mTarget) mTarget->Destroy();
7548 MOZ_COUNT_DTOR(DR_Rule);
7550 void AddPart(nsIAtom* aFrameType);
7552 PRUint32 mLength;
7553 DR_RulePart* mTarget;
7554 PRBool mDisplay;
7557 void DR_Rule::AddPart(nsIAtom* aFrameType)
7559 DR_RulePart* newPart = new DR_RulePart(aFrameType);
7560 newPart->mNext = mTarget;
7561 mTarget = newPart;
7562 mLength++;
7565 struct DR_FrameTypeInfo
7567 DR_FrameTypeInfo(nsIAtom* aFrmeType, const char* aFrameNameAbbrev, const char* aFrameName);
7568 ~DR_FrameTypeInfo() {
7569 PRInt32 numElements;
7570 numElements = mRules.Length();
7571 for (PRInt32 i = numElements - 1; i >= 0; i--) {
7572 delete mRules.ElementAt(i);
7576 nsIAtom* mType;
7577 char mNameAbbrev[16];
7578 char mName[32];
7579 nsTArray<DR_Rule*> mRules;
7580 private:
7581 DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&); // NOT USED
7584 DR_FrameTypeInfo::DR_FrameTypeInfo(nsIAtom* aFrameType,
7585 const char* aFrameNameAbbrev,
7586 const char* aFrameName)
7588 mType = aFrameType;
7589 strcpy(mNameAbbrev, aFrameNameAbbrev);
7590 strcpy(mName, aFrameName);
7593 struct DR_FrameTreeNode
7595 DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent) : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0)
7597 MOZ_COUNT_CTOR(DR_FrameTreeNode);
7600 ~DR_FrameTreeNode()
7602 MOZ_COUNT_DTOR(DR_FrameTreeNode);
7605 nsIFrame* mFrame;
7606 DR_FrameTreeNode* mParent;
7607 PRBool mDisplay;
7608 PRUint32 mIndent;
7611 // DR_State implementation
7613 DR_State::DR_State()
7614 : mInited(PR_FALSE), mActive(PR_FALSE), mCount(0), mAssert(-1), mIndent(0),
7615 mIndentUndisplayedFrames(PR_FALSE), mDisplayPixelErrors(PR_FALSE)
7617 MOZ_COUNT_CTOR(DR_State);
7620 void DR_State::Init()
7622 char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT");
7623 PRInt32 num;
7624 if (env) {
7625 if (GetNumber(env, num))
7626 mAssert = num;
7627 else
7628 printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env);
7631 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START");
7632 if (env) {
7633 if (GetNumber(env, num))
7634 mIndent = num;
7635 else
7636 printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env);
7639 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES");
7640 if (env) {
7641 if (GetNumber(env, num))
7642 mIndentUndisplayedFrames = num;
7643 else
7644 printf("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s", env);
7647 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS");
7648 if (env) {
7649 if (GetNumber(env, num))
7650 mDisplayPixelErrors = num;
7651 else
7652 printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s", env);
7655 InitFrameTypeTable();
7656 ParseRulesFile();
7657 mInited = PR_TRUE;
7660 DR_State::~DR_State()
7662 MOZ_COUNT_DTOR(DR_State);
7663 PRInt32 numElements, i;
7664 numElements = mWildRules.Length();
7665 for (i = numElements - 1; i >= 0; i--) {
7666 delete mWildRules.ElementAt(i);
7668 numElements = mFrameTreeLeaves.Length();
7669 for (i = numElements - 1; i >= 0; i--) {
7670 delete mFrameTreeLeaves.ElementAt(i);
7674 PRBool DR_State::GetNumber(char* aBuf,
7675 PRInt32& aNumber)
7677 if (sscanf(aBuf, "%d", &aNumber) > 0)
7678 return PR_TRUE;
7679 else
7680 return PR_FALSE;
7683 PRBool DR_State::IsWhiteSpace(int c) {
7684 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
7687 PRBool DR_State::GetToken(FILE* aFile,
7688 char* aBuf,
7689 size_t aBufSize)
7691 PRBool haveToken = PR_FALSE;
7692 aBuf[0] = 0;
7693 // get the 1st non whitespace char
7694 int c = -1;
7695 for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) {
7698 if (c > 0) {
7699 haveToken = PR_TRUE;
7700 aBuf[0] = c;
7701 // get everything up to the next whitespace char
7702 size_t cX;
7703 for (cX = 1; cX + 1 < aBufSize ; cX++) {
7704 c = getc(aFile);
7705 if (c < 0) { // EOF
7706 ungetc(' ', aFile);
7707 break;
7709 else {
7710 if (IsWhiteSpace(c)) {
7711 break;
7713 else {
7714 aBuf[cX] = c;
7718 aBuf[cX] = 0;
7720 return haveToken;
7723 DR_Rule* DR_State::ParseRule(FILE* aFile)
7725 char buf[128];
7726 PRInt32 doDisplay;
7727 DR_Rule* rule = nsnull;
7728 while (GetToken(aFile, buf, sizeof(buf))) {
7729 if (GetNumber(buf, doDisplay)) {
7730 if (rule) {
7731 rule->mDisplay = !!doDisplay;
7732 break;
7734 else {
7735 printf("unexpected token - %s \n", buf);
7738 else {
7739 if (!rule) {
7740 rule = new DR_Rule;
7742 if (strcmp(buf, "*") == 0) {
7743 rule->AddPart(nsnull);
7745 else {
7746 DR_FrameTypeInfo* info = GetFrameTypeInfo(buf);
7747 if (info) {
7748 rule->AddPart(info->mType);
7750 else {
7751 printf("invalid frame type - %s \n", buf);
7756 return rule;
7759 void DR_State::AddRule(nsTArray<DR_Rule*>& aRules,
7760 DR_Rule& aRule)
7762 PRInt32 numRules = aRules.Length();
7763 for (PRInt32 ruleX = 0; ruleX < numRules; ruleX++) {
7764 DR_Rule* rule = aRules.ElementAt(ruleX);
7765 NS_ASSERTION(rule, "program error");
7766 if (aRule.mLength > rule->mLength) {
7767 aRules.InsertElementAt(ruleX, &aRule);
7768 return;
7771 aRules.AppendElement(&aRule);
7774 void DR_State::ParseRulesFile()
7776 char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE");
7777 if (path) {
7778 FILE* inFile = fopen(path, "r");
7779 if (inFile) {
7780 for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) {
7781 if (rule->mTarget) {
7782 nsIAtom* fType = rule->mTarget->mFrameType;
7783 if (fType) {
7784 DR_FrameTypeInfo* info = GetFrameTypeInfo(fType);
7785 if (info) {
7786 AddRule(info->mRules, *rule);
7789 else {
7790 AddRule(mWildRules, *rule);
7792 mActive = PR_TRUE;
7800 void DR_State::AddFrameTypeInfo(nsIAtom* aFrameType,
7801 const char* aFrameNameAbbrev,
7802 const char* aFrameName)
7804 mFrameTypeTable.AppendElement(DR_FrameTypeInfo(aFrameType, aFrameNameAbbrev, aFrameName));
7807 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(nsIAtom* aFrameType)
7809 PRInt32 numEntries = mFrameTypeTable.Length();
7810 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
7811 for (PRInt32 i = 0; i < numEntries; i++) {
7812 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
7813 if (info.mType == aFrameType) {
7814 return &info;
7817 return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type
7820 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName)
7822 PRInt32 numEntries = mFrameTypeTable.Length();
7823 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
7824 for (PRInt32 i = 0; i < numEntries; i++) {
7825 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
7826 if ((strcmp(aFrameName, info.mName) == 0) || (strcmp(aFrameName, info.mNameAbbrev) == 0)) {
7827 return &info;
7830 return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type
7833 void DR_State::InitFrameTypeTable()
7835 AddFrameTypeInfo(nsGkAtoms::blockFrame, "block", "block");
7836 AddFrameTypeInfo(nsGkAtoms::brFrame, "br", "br");
7837 AddFrameTypeInfo(nsGkAtoms::bulletFrame, "bullet", "bullet");
7838 AddFrameTypeInfo(nsGkAtoms::gfxButtonControlFrame, "button", "gfxButtonControl");
7839 AddFrameTypeInfo(nsGkAtoms::HTMLButtonControlFrame, "HTMLbutton", "HTMLButtonControl");
7840 AddFrameTypeInfo(nsGkAtoms::HTMLCanvasFrame, "HTMLCanvas","HTMLCanvas");
7841 AddFrameTypeInfo(nsGkAtoms::subDocumentFrame, "subdoc", "subDocument");
7842 AddFrameTypeInfo(nsGkAtoms::imageFrame, "img", "image");
7843 AddFrameTypeInfo(nsGkAtoms::inlineFrame, "inline", "inline");
7844 AddFrameTypeInfo(nsGkAtoms::letterFrame, "letter", "letter");
7845 AddFrameTypeInfo(nsGkAtoms::lineFrame, "line", "line");
7846 AddFrameTypeInfo(nsGkAtoms::listControlFrame, "select", "select");
7847 AddFrameTypeInfo(nsGkAtoms::objectFrame, "obj", "object");
7848 AddFrameTypeInfo(nsGkAtoms::pageFrame, "page", "page");
7849 AddFrameTypeInfo(nsGkAtoms::placeholderFrame, "place", "placeholder");
7850 AddFrameTypeInfo(nsGkAtoms::positionedInlineFrame, "posInline", "positionedInline");
7851 AddFrameTypeInfo(nsGkAtoms::canvasFrame, "canvas", "canvas");
7852 AddFrameTypeInfo(nsGkAtoms::rootFrame, "root", "root");
7853 AddFrameTypeInfo(nsGkAtoms::scrollFrame, "scroll", "scroll");
7854 AddFrameTypeInfo(nsGkAtoms::tableCaptionFrame, "caption", "tableCaption");
7855 AddFrameTypeInfo(nsGkAtoms::tableCellFrame, "cell", "tableCell");
7856 AddFrameTypeInfo(nsGkAtoms::bcTableCellFrame, "bcCell", "bcTableCell");
7857 AddFrameTypeInfo(nsGkAtoms::tableColFrame, "col", "tableCol");
7858 AddFrameTypeInfo(nsGkAtoms::tableColGroupFrame, "colG", "tableColGroup");
7859 AddFrameTypeInfo(nsGkAtoms::tableFrame, "tbl", "table");
7860 AddFrameTypeInfo(nsGkAtoms::tableOuterFrame, "tblO", "tableOuter");
7861 AddFrameTypeInfo(nsGkAtoms::tableRowGroupFrame, "rowG", "tableRowGroup");
7862 AddFrameTypeInfo(nsGkAtoms::tableRowFrame, "row", "tableRow");
7863 AddFrameTypeInfo(nsGkAtoms::textInputFrame, "textCtl", "textInput");
7864 AddFrameTypeInfo(nsGkAtoms::textFrame, "text", "text");
7865 AddFrameTypeInfo(nsGkAtoms::viewportFrame, "VP", "viewport");
7866 #ifdef MOZ_XUL
7867 AddFrameTypeInfo(nsGkAtoms::XULLabelFrame, "XULLabel", "XULLabel");
7868 AddFrameTypeInfo(nsGkAtoms::boxFrame, "Box", "Box");
7869 AddFrameTypeInfo(nsGkAtoms::sliderFrame, "Slider", "Slider");
7870 AddFrameTypeInfo(nsGkAtoms::popupSetFrame, "PopupSet", "PopupSet");
7871 #endif
7872 AddFrameTypeInfo(nsnull, "unknown", "unknown");
7876 void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame,
7877 PRInt32 aIndent)
7879 DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->GetType());
7880 if (frameTypeInfo) {
7881 for (PRInt32 i = 0; i < aIndent; i++) {
7882 printf(" ");
7884 if(!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) {
7885 if (aFrame) {
7886 nsAutoString name;
7887 aFrame->GetFrameName(name);
7888 printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)aFrame);
7890 else {
7891 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
7894 else {
7895 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
7900 PRBool DR_State::RuleMatches(DR_Rule& aRule,
7901 DR_FrameTreeNode& aNode)
7903 NS_ASSERTION(aRule.mTarget, "program error");
7905 DR_RulePart* rulePart;
7906 DR_FrameTreeNode* parentNode;
7907 for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent;
7908 rulePart && parentNode;
7909 rulePart = rulePart->mNext, parentNode = parentNode->mParent) {
7910 if (rulePart->mFrameType) {
7911 if (parentNode->mFrame) {
7912 if (rulePart->mFrameType != parentNode->mFrame->GetType()) {
7913 return PR_FALSE;
7916 else NS_ASSERTION(PR_FALSE, "program error");
7918 // else wild card match
7920 return PR_TRUE;
7923 void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode)
7925 if (!aNode.mFrame) {
7926 NS_ASSERTION(PR_FALSE, "invalid DR_FrameTreeNode \n");
7927 return;
7930 PRBool matchingRule = PR_FALSE;
7932 DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->GetType());
7933 NS_ASSERTION(info, "program error");
7934 PRInt32 numRules = info->mRules.Length();
7935 for (PRInt32 ruleX = 0; ruleX < numRules; ruleX++) {
7936 DR_Rule* rule = info->mRules.ElementAt(ruleX);
7937 if (rule && RuleMatches(*rule, aNode)) {
7938 aNode.mDisplay = rule->mDisplay;
7939 matchingRule = PR_TRUE;
7940 break;
7943 if (!matchingRule) {
7944 PRInt32 numWildRules = mWildRules.Length();
7945 for (PRInt32 ruleX = 0; ruleX < numWildRules; ruleX++) {
7946 DR_Rule* rule = mWildRules.ElementAt(ruleX);
7947 if (rule && RuleMatches(*rule, aNode)) {
7948 aNode.mDisplay = rule->mDisplay;
7949 break;
7955 DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame,
7956 const nsHTMLReflowState* aReflowState)
7958 // find the frame of the parent reflow state (usually just the parent of aFrame)
7959 nsIFrame* parentFrame;
7960 if (aReflowState) {
7961 const nsHTMLReflowState* parentRS = aReflowState->parentReflowState;
7962 parentFrame = (parentRS) ? parentRS->frame : nsnull;
7963 } else {
7964 parentFrame = aFrame->GetParent();
7967 // find the parent tree node leaf
7968 DR_FrameTreeNode* parentNode = nsnull;
7970 DR_FrameTreeNode* lastLeaf = nsnull;
7971 if(mFrameTreeLeaves.Length())
7972 lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1);
7973 if (lastLeaf) {
7974 for (parentNode = lastLeaf; parentNode && (parentNode->mFrame != parentFrame); parentNode = parentNode->mParent) {
7977 DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode);
7978 FindMatchingRule(*newNode);
7980 newNode->mIndent = mIndent;
7981 if (newNode->mDisplay || mIndentUndisplayedFrames) {
7982 ++mIndent;
7985 if (lastLeaf && (lastLeaf == parentNode)) {
7986 mFrameTreeLeaves.RemoveElementAt(mFrameTreeLeaves.Length() - 1);
7988 mFrameTreeLeaves.AppendElement(newNode);
7989 mCount++;
7991 return newNode;
7994 void DR_State::PrettyUC(nscoord aSize,
7995 char* aBuf)
7997 if (NS_UNCONSTRAINEDSIZE == aSize) {
7998 strcpy(aBuf, "UC");
8000 else {
8001 if ((nscoord)0xdeadbeefU == aSize)
8003 strcpy(aBuf, "deadbeef");
8005 else {
8006 sprintf(aBuf, "%d", aSize);
8011 void DR_State::PrintMargin(const char *tag, const nsMargin* aMargin)
8013 if (aMargin) {
8014 char t[16], r[16], b[16], l[16];
8015 PrettyUC(aMargin->top, t);
8016 PrettyUC(aMargin->right, r);
8017 PrettyUC(aMargin->bottom, b);
8018 PrettyUC(aMargin->left, l);
8019 printf(" %s=%s,%s,%s,%s", tag, t, r, b, l);
8020 } else {
8021 // use %p here for consistency with other null-pointer printouts
8022 printf(" %s=%p", tag, (void*)aMargin);
8026 void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode)
8028 mFrameTreeLeaves.RemoveElement(&aNode);
8029 PRInt32 numLeaves = mFrameTreeLeaves.Length();
8030 if ((0 == numLeaves) || (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) {
8031 mFrameTreeLeaves.AppendElement(aNode.mParent);
8034 if (aNode.mDisplay || mIndentUndisplayedFrames) {
8035 --mIndent;
8037 // delete the tree node
8038 delete &aNode;
8041 static void
8042 CheckPixelError(nscoord aSize,
8043 PRInt32 aPixelToTwips)
8045 if (NS_UNCONSTRAINEDSIZE != aSize) {
8046 if ((aSize % aPixelToTwips) > 0) {
8047 printf("VALUE %d is not a whole pixel \n", aSize);
8052 static void DisplayReflowEnterPrint(nsPresContext* aPresContext,
8053 nsIFrame* aFrame,
8054 const nsHTMLReflowState& aReflowState,
8055 DR_FrameTreeNode& aTreeNode,
8056 PRBool aChanged)
8058 if (aTreeNode.mDisplay) {
8059 DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent);
8061 char width[16];
8062 char height[16];
8064 DR_state->PrettyUC(aReflowState.availableWidth, width);
8065 DR_state->PrettyUC(aReflowState.availableHeight, height);
8066 printf("Reflow a=%s,%s ", width, height);
8068 DR_state->PrettyUC(aReflowState.ComputedWidth(), width);
8069 DR_state->PrettyUC(aReflowState.ComputedHeight(), height);
8070 printf("c=%s,%s ", width, height);
8072 if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY)
8073 printf("dirty ");
8075 if (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)
8076 printf("dirty-children ");
8078 if (aReflowState.mFlags.mSpecialHeightReflow)
8079 printf("special-height ");
8081 if (aReflowState.mFlags.mHResize)
8082 printf("h-resize ");
8084 if (aReflowState.mFlags.mVResize)
8085 printf("v-resize ");
8087 nsIFrame* inFlow = aFrame->GetPrevInFlow();
8088 if (inFlow) {
8089 printf("pif=%p ", (void*)inFlow);
8091 inFlow = aFrame->GetNextInFlow();
8092 if (inFlow) {
8093 printf("nif=%p ", (void*)inFlow);
8095 if (aChanged)
8096 printf("CHANGED \n");
8097 else
8098 printf("cnt=%d \n", DR_state->mCount);
8099 if (DR_state->mDisplayPixelErrors) {
8100 PRInt32 p2t = aPresContext->AppUnitsPerDevPixel();
8101 CheckPixelError(aReflowState.availableWidth, p2t);
8102 CheckPixelError(aReflowState.availableHeight, p2t);
8103 CheckPixelError(aReflowState.ComputedWidth(), p2t);
8104 CheckPixelError(aReflowState.ComputedHeight(), p2t);
8109 void* nsFrame::DisplayReflowEnter(nsPresContext* aPresContext,
8110 nsIFrame* aFrame,
8111 const nsHTMLReflowState& aReflowState)
8113 if (!DR_state->mInited) DR_state->Init();
8114 if (!DR_state->mActive) return nsnull;
8116 NS_ASSERTION(aFrame, "invalid call");
8118 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowState);
8119 if (treeNode) {
8120 DisplayReflowEnterPrint(aPresContext, aFrame, aReflowState, *treeNode, PR_FALSE);
8122 return treeNode;
8125 void* nsFrame::DisplayLayoutEnter(nsIFrame* aFrame)
8127 if (!DR_state->mInited) DR_state->Init();
8128 if (!DR_state->mActive) return nsnull;
8130 NS_ASSERTION(aFrame, "invalid call");
8132 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nsnull);
8133 if (treeNode && treeNode->mDisplay) {
8134 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8135 printf("Layout\n");
8137 return treeNode;
8140 void* nsFrame::DisplayIntrinsicWidthEnter(nsIFrame* aFrame,
8141 const char* aType)
8143 if (!DR_state->mInited) DR_state->Init();
8144 if (!DR_state->mActive) return nsnull;
8146 NS_ASSERTION(aFrame, "invalid call");
8148 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nsnull);
8149 if (treeNode && treeNode->mDisplay) {
8150 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8151 printf("Get%sWidth\n", aType);
8153 return treeNode;
8156 void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame,
8157 const char* aType)
8159 if (!DR_state->mInited) DR_state->Init();
8160 if (!DR_state->mActive) return nsnull;
8162 NS_ASSERTION(aFrame, "invalid call");
8164 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nsnull);
8165 if (treeNode && treeNode->mDisplay) {
8166 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8167 printf("Get%sSize\n", aType);
8169 return treeNode;
8172 void nsFrame::DisplayReflowExit(nsPresContext* aPresContext,
8173 nsIFrame* aFrame,
8174 nsHTMLReflowMetrics& aMetrics,
8175 nsReflowStatus aStatus,
8176 void* aFrameTreeNode)
8178 if (!DR_state->mActive) return;
8180 NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call");
8181 if (!aFrameTreeNode) return;
8183 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
8184 if (treeNode->mDisplay) {
8185 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8187 char width[16];
8188 char height[16];
8189 char x[16];
8190 char y[16];
8191 DR_state->PrettyUC(aMetrics.width, width);
8192 DR_state->PrettyUC(aMetrics.height, height);
8193 printf("Reflow d=%s,%s", width, height);
8195 if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
8196 printf(" status=0x%x", aStatus);
8198 if (aFrame->HasOverflowAreas()) {
8199 DR_state->PrettyUC(aMetrics.VisualOverflow().x, x);
8200 DR_state->PrettyUC(aMetrics.VisualOverflow().y, y);
8201 DR_state->PrettyUC(aMetrics.VisualOverflow().width, width);
8202 DR_state->PrettyUC(aMetrics.VisualOverflow().height, height);
8203 printf(" vis-o=(%s,%s) %s x %s", x, y, width, height);
8205 nsRect storedOverflow = aFrame->GetVisualOverflowRect();
8206 DR_state->PrettyUC(storedOverflow.x, x);
8207 DR_state->PrettyUC(storedOverflow.y, y);
8208 DR_state->PrettyUC(storedOverflow.width, width);
8209 DR_state->PrettyUC(storedOverflow.height, height);
8210 printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height);
8212 DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x);
8213 DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y);
8214 DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width);
8215 DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height);
8216 printf(" scr-o=(%s,%s) %s x %s", x, y, width, height);
8218 storedOverflow = aFrame->GetScrollableOverflowRect();
8219 DR_state->PrettyUC(storedOverflow.x, x);
8220 DR_state->PrettyUC(storedOverflow.y, y);
8221 DR_state->PrettyUC(storedOverflow.width, width);
8222 DR_state->PrettyUC(storedOverflow.height, height);
8223 printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height);
8225 printf("\n");
8226 if (DR_state->mDisplayPixelErrors) {
8227 PRInt32 p2t = aPresContext->AppUnitsPerDevPixel();
8228 CheckPixelError(aMetrics.width, p2t);
8229 CheckPixelError(aMetrics.height, p2t);
8232 DR_state->DeleteTreeNode(*treeNode);
8235 void nsFrame::DisplayLayoutExit(nsIFrame* aFrame,
8236 void* aFrameTreeNode)
8238 if (!DR_state->mActive) return;
8240 NS_ASSERTION(aFrame, "non-null frame required");
8241 if (!aFrameTreeNode) return;
8243 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
8244 if (treeNode->mDisplay) {
8245 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8246 nsRect rect = aFrame->GetRect();
8247 printf("Layout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height);
8249 DR_state->DeleteTreeNode(*treeNode);
8252 void nsFrame::DisplayIntrinsicWidthExit(nsIFrame* aFrame,
8253 const char* aType,
8254 nscoord aResult,
8255 void* aFrameTreeNode)
8257 if (!DR_state->mActive) return;
8259 NS_ASSERTION(aFrame, "non-null frame required");
8260 if (!aFrameTreeNode) return;
8262 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
8263 if (treeNode->mDisplay) {
8264 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8265 char width[16];
8266 DR_state->PrettyUC(aResult, width);
8267 printf("Get%sWidth=%s\n", aType, width);
8269 DR_state->DeleteTreeNode(*treeNode);
8272 void nsFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame,
8273 const char* aType,
8274 nsSize aResult,
8275 void* aFrameTreeNode)
8277 if (!DR_state->mActive) return;
8279 NS_ASSERTION(aFrame, "non-null frame required");
8280 if (!aFrameTreeNode) return;
8282 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
8283 if (treeNode->mDisplay) {
8284 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8286 char width[16];
8287 char height[16];
8288 DR_state->PrettyUC(aResult.width, width);
8289 DR_state->PrettyUC(aResult.height, height);
8290 printf("Get%sSize=%s,%s\n", aType, width, height);
8292 DR_state->DeleteTreeNode(*treeNode);
8295 /* static */ void
8296 nsFrame::DisplayReflowStartup()
8298 DR_state = new DR_State();
8301 /* static */ void
8302 nsFrame::DisplayReflowShutdown()
8304 delete DR_state;
8305 DR_state = nsnull;
8308 void DR_cookie::Change() const
8310 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue;
8311 if (treeNode && treeNode->mDisplay) {
8312 DisplayReflowEnterPrint(mPresContext, mFrame, mReflowState, *treeNode, PR_TRUE);
8316 /* static */ void*
8317 nsHTMLReflowState::DisplayInitConstraintsEnter(nsIFrame* aFrame,
8318 nsHTMLReflowState* aState,
8319 nscoord aContainingBlockWidth,
8320 nscoord aContainingBlockHeight,
8321 const nsMargin* aBorder,
8322 const nsMargin* aPadding)
8324 NS_PRECONDITION(aFrame, "non-null frame required");
8325 NS_PRECONDITION(aState, "non-null state required");
8327 if (!DR_state->mInited) DR_state->Init();
8328 if (!DR_state->mActive) return nsnull;
8330 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState);
8331 if (treeNode && treeNode->mDisplay) {
8332 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8334 printf("InitConstraints parent=%p",
8335 (void*)aState->parentReflowState);
8337 char width[16];
8338 char height[16];
8340 DR_state->PrettyUC(aContainingBlockWidth, width);
8341 DR_state->PrettyUC(aContainingBlockHeight, height);
8342 printf(" cb=%s,%s", width, height);
8344 DR_state->PrettyUC(aState->availableWidth, width);
8345 DR_state->PrettyUC(aState->availableHeight, height);
8346 printf(" as=%s,%s", width, height);
8348 DR_state->PrintMargin("b", aBorder);
8349 DR_state->PrintMargin("p", aPadding);
8350 putchar('\n');
8352 return treeNode;
8355 /* static */ void
8356 nsHTMLReflowState::DisplayInitConstraintsExit(nsIFrame* aFrame,
8357 nsHTMLReflowState* aState,
8358 void* aValue)
8360 NS_PRECONDITION(aFrame, "non-null frame required");
8361 NS_PRECONDITION(aState, "non-null state required");
8363 if (!DR_state->mActive) return;
8364 if (!aValue) return;
8366 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
8367 if (treeNode->mDisplay) {
8368 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8369 char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16];
8370 DR_state->PrettyUC(aState->mComputedMinWidth, cmiw);
8371 DR_state->PrettyUC(aState->mComputedWidth, cw);
8372 DR_state->PrettyUC(aState->mComputedMaxWidth, cmxw);
8373 DR_state->PrettyUC(aState->mComputedMinHeight, cmih);
8374 DR_state->PrettyUC(aState->mComputedHeight, ch);
8375 DR_state->PrettyUC(aState->mComputedMaxHeight, cmxh);
8376 printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)",
8377 cmiw, cw, cmxw, cmih, ch, cmxh);
8378 DR_state->PrintMargin("co", &aState->mComputedOffsets);
8379 putchar('\n');
8381 DR_state->DeleteTreeNode(*treeNode);
8385 /* static */ void*
8386 nsCSSOffsetState::DisplayInitOffsetsEnter(nsIFrame* aFrame,
8387 nsCSSOffsetState* aState,
8388 nscoord aContainingBlockWidth,
8389 const nsMargin* aBorder,
8390 const nsMargin* aPadding)
8392 NS_PRECONDITION(aFrame, "non-null frame required");
8393 NS_PRECONDITION(aState, "non-null state required");
8395 if (!DR_state->mInited) DR_state->Init();
8396 if (!DR_state->mActive) return nsnull;
8398 // aState is not necessarily a nsHTMLReflowState
8399 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nsnull);
8400 if (treeNode && treeNode->mDisplay) {
8401 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8403 char width[16];
8404 DR_state->PrettyUC(aContainingBlockWidth, width);
8405 printf("InitOffsets cbw=%s", width);
8406 DR_state->PrintMargin("b", aBorder);
8407 DR_state->PrintMargin("p", aPadding);
8408 putchar('\n');
8410 return treeNode;
8413 /* static */ void
8414 nsCSSOffsetState::DisplayInitOffsetsExit(nsIFrame* aFrame,
8415 nsCSSOffsetState* aState,
8416 void* aValue)
8418 NS_PRECONDITION(aFrame, "non-null frame required");
8419 NS_PRECONDITION(aState, "non-null state required");
8421 if (!DR_state->mActive) return;
8422 if (!aValue) return;
8424 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
8425 if (treeNode->mDisplay) {
8426 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8427 printf("InitOffsets=");
8428 DR_state->PrintMargin("m", &aState->mComputedMargin);
8429 DR_state->PrintMargin("p", &aState->mComputedPadding);
8430 DR_state->PrintMargin("p+b", &aState->mComputedBorderPadding);
8431 putchar('\n');
8433 DR_state->DeleteTreeNode(*treeNode);
8436 /* static */ void*
8437 nsHTMLReflowState::DisplayInitFrameTypeEnter(nsIFrame* aFrame,
8438 nsHTMLReflowState* aState)
8440 NS_PRECONDITION(aFrame, "non-null frame required");
8441 NS_PRECONDITION(aState, "non-null state required");
8443 if (!DR_state->mInited) DR_state->Init();
8444 if (!DR_state->mActive) return nsnull;
8446 // we don't print anything here
8447 return DR_state->CreateTreeNode(aFrame, aState);
8450 /* static */ void
8451 nsHTMLReflowState::DisplayInitFrameTypeExit(nsIFrame* aFrame,
8452 nsHTMLReflowState* aState,
8453 void* aValue)
8455 NS_PRECONDITION(aFrame, "non-null frame required");
8456 NS_PRECONDITION(aState, "non-null state required");
8458 if (!DR_state->mActive) return;
8459 if (!aValue) return;
8461 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
8462 if (treeNode->mDisplay) {
8463 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
8464 printf("InitFrameType");
8466 const nsStyleDisplay *disp = aState->mStyleDisplay;
8468 if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
8469 printf(" out-of-flow");
8470 if (aFrame->GetPrevInFlow())
8471 printf(" prev-in-flow");
8472 if (disp->IsAbsolutelyPositioned())
8473 printf(" abspos");
8474 if (disp->IsFloating())
8475 printf(" float");
8477 // This array must exactly match the NS_STYLE_DISPLAY constants.
8478 const char *const displayTypes[] = {
8479 "none", "block", "inline", "inline-block", "list-item", "marker",
8480 "run-in", "compact", "table", "inline-table", "table-row-group",
8481 "table-column", "table-column-group", "table-header-group",
8482 "table-footer-group", "table-row", "table-cell", "table-caption",
8483 "box", "inline-box",
8484 #ifdef MOZ_XUL
8485 "grid", "inline-grid", "grid-group", "grid-line", "stack",
8486 "inline-stack", "deck", "popup", "groupbox",
8487 #endif
8489 if (disp->mDisplay >= NS_ARRAY_LENGTH(displayTypes))
8490 printf(" display=%u", disp->mDisplay);
8491 else
8492 printf(" display=%s", displayTypes[disp->mDisplay]);
8494 // This array must exactly match the NS_CSS_FRAME_TYPE constants.
8495 const char *const cssFrameTypes[] = {
8496 "unknown", "inline", "block", "floating", "absolute", "internal-table"
8498 nsCSSFrameType bareType = NS_FRAME_GET_TYPE(aState->mFrameType);
8499 bool repNoBlock = NS_FRAME_IS_REPLACED_NOBLOCK(aState->mFrameType);
8500 bool repBlock = NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(aState->mFrameType);
8502 if (bareType >= NS_ARRAY_LENGTH(cssFrameTypes)) {
8503 printf(" result=type %u", bareType);
8504 } else {
8505 printf(" result=%s", cssFrameTypes[bareType]);
8507 printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : "");
8509 DR_state->DeleteTreeNode(*treeNode);
8512 #endif
8513 // End Display Reflow
8515 #endif