Draw the caret in the normal path of frame painting instead of doing it directly to the current view. bug 287813, r+sr=roc

This commit is contained in:
mrbkap%gmail.com 2006-04-17 23:16:46 +00:00
parent f15a96ed13
commit e27b36efcc
15 changed files with 648 additions and 354 deletions

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=4 sw=2 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -530,6 +531,8 @@ nsTextEditorDragListener::DragEnter(nsIDOMEvent* aDragEvent)
{
mCaret->Init(presShell);
mCaret->SetCaretReadOnly(PR_TRUE);
mOtherCaret = presShell->SetCaret(mCaret);
}
mCaretDrawn = PR_FALSE;
}
@ -631,7 +634,15 @@ nsTextEditorDragListener::DragDrop(nsIDOMEvent* aMouseEvent)
mCaretDrawn = PR_FALSE;
}
mCaret->SetCaretVisible(PR_FALSE); // hide it, so that it turns off its timer
mCaret = nsnull; // release it
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
if (presShell)
{
NS_ASSERTION(mOtherCaret, "Where'd my other caret go?");
mCaret = presShell->SetCaret(mOtherCaret);
}
mOtherCaret = mCaret = nsnull;
}
if (!mEditor)

View File

@ -235,8 +235,8 @@ protected:
nsWeakPtr mPresShell;
nsCOMPtr<nsICaret> mCaret;
nsCOMPtr<nsICaret> mOtherCaret;
PRBool mCaretDrawn;
};
/** editor Implementation of the FocusListener interface

View File

@ -670,28 +670,15 @@ nsThebesRenderingContext::FillRect(nscoord aX, nscoord aY, nscoord aWidth, nscoo
return NS_OK;
}
/**
* XXX awful invert rect hack
* idea by mrbkap
*/
static unsigned int gInvertRect = 0;
NS_IMETHODIMP
nsThebesRenderingContext::InvertRect(const nsRect& aRect)
{
gfxContext::GraphicsOperator lastOp = mThebes->CurrentOperator();
gfxRGBA newColor(0,0,0,1);
if (gInvertRect++ % 2)
newColor = gfxRGBA(1,1,1,1);
mThebes->Save();
mThebes->SetColor(newColor);
mThebes->SetOperator(gfxContext::OPERATOR_OVER);
mThebes->SetOperator(gfxContext::OPERATOR_XOR);
nsresult rv = FillRect(aRect);
mThebes->SetOperator(lastOp);
mThebes->Restore();
return rv;
}

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -64,7 +65,7 @@
#include "nsILookAndFeel.h"
#include "nsBlockFrame.h"
#include "nsISelectionController.h"
#include "nsDisplayList.h"
#include "nsCaret.h"
// The bidi indicator hangs off the caret to one side, to show which
@ -72,15 +73,6 @@
// an insignificant dot
static const PRUint32 kMinBidiIndicatorPixels = 2;
#if !defined(MOZ_WIDGET_GTK2)
// Because of drawing issues, we currently always make a new RC. See bug 28068
// Before removing this, stuff will need to be fixed and tested on all platforms.
// For example, turning this off on Mac right now causes drawing problems on pages
// with form elements.
// Also turning this off caused problems on GTK1. See bug 254049.
#define DONT_REUSE_RENDERING_CONTEXT
#endif
#ifdef IBMBIDI
//-------------------------------IBM BIDI--------------------------------------
// Mamdouh : Modifiaction of the caret to work with Bidi in the LTR and RTL
@ -97,7 +89,6 @@ nsCaret::nsCaret()
, mDrawn(PR_FALSE)
, mReadOnly(PR_FALSE)
, mShowDuringSelection(PR_FALSE)
, mLastCaretView(nsnull)
, mLastContentOffset(0)
, mLastHint(nsIFrameSelection::HINTLEFT)
#ifdef IBMBIDI
@ -107,7 +98,6 @@ nsCaret::nsCaret()
{
}
//-----------------------------------------------------------------------------
nsCaret::~nsCaret()
{
@ -153,7 +143,8 @@ NS_IMETHODIMP nsCaret::Init(nsIPresShell *inPresShell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsISelection> domSelection;
nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSelection));
nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(domSelection));
if (NS_FAILED(rv))
return rv;
if (!domSelection)
@ -187,13 +178,11 @@ NS_IMETHODIMP nsCaret::Init(nsIPresShell *inPresShell)
//-----------------------------------------------------------------------------
NS_IMETHODIMP nsCaret::Terminate()
{
// this doesn't erase the caret if it's drawn. Should it? We might not have a good
// drawing environment during teardown.
// this doesn't erase the caret if it's drawn. Should it? We might not have
// a good drawing environment during teardown.
KillTimer();
mBlinkTimer = nsnull;
mRendContext = nsnull;
// unregiser ourselves as a selection listener
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
@ -204,7 +193,6 @@ NS_IMETHODIMP nsCaret::Terminate()
mPresShell = nsnull;
mLastContent = nsnull;
mLastCaretView = nsnull;
#ifdef IBMBIDI
mBidiKeyboard = nsnull;
@ -276,7 +264,11 @@ NS_IMETHODIMP nsCaret::SetCaretReadOnly(PRBool inMakeReadonly)
//-----------------------------------------------------------------------------
NS_IMETHODIMP nsCaret::GetCaretCoordinates(EViewCoordinates aRelativeToType, nsISelection *aDOMSel, nsRect *outCoordinates, PRBool *outIsCollapsed, nsIView **outView)
NS_IMETHODIMP nsCaret::GetCaretCoordinates(EViewCoordinates aRelativeToType,
nsISelection *aDOMSel,
nsRect *outCoordinates,
PRBool *outIsCollapsed,
nsIView **outView)
{
if (!mPresShell)
return NS_ERROR_NOT_INITIALIZED;
@ -315,15 +307,7 @@ NS_IMETHODIMP nsCaret::GetCaretCoordinates(EViewCoordinates aRelativeToType, nsI
if (NS_FAILED(err))
return err;
/*
// is this a text node?
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(focusNode);
// note that we only work with text nodes here, unlike when drawing the caret.
// this is because this routine is intended for IME support, which only cares about text.
if (!nodeAsText)
return NS_ERROR_UNEXPECTED;
*/
nsCOMPtr<nsIContent>contentNode = do_QueryInterface(focusNode);
nsCOMPtr<nsIContent> contentNode = do_QueryInterface(focusNode);
if (!contentNode)
return NS_ERROR_FAILURE;
@ -350,10 +334,9 @@ NS_IMETHODIMP nsCaret::GetCaretCoordinates(EViewCoordinates aRelativeToType, nsI
return err;
nsPoint viewOffset(0, 0);
nsRect clipRect;
nsIView *drawingView; // views are not refcounted
GetViewForRendering(theFrame, aRelativeToType, viewOffset, clipRect, &drawingView, outView);
GetViewForRendering(theFrame, aRelativeToType, viewOffset, &drawingView, outView);
if (!drawingView)
return NS_ERROR_UNEXPECTED;
// ramp up to make a rendering context for measuring text.
@ -434,10 +417,55 @@ NS_IMETHODIMP nsCaret::DrawAtPosition(nsIDOMNode* aNode, PRInt32 aOffset)
presShell->GetCaretBidiLevel(&bidiLevel);
// XXX we need to do more work here to get the correct hint.
return DrawAtPositionWithHint(aNode, aOffset, nsIFrameSelection::HINTLEFT, bidiLevel) ?
nsresult rv = DrawAtPositionWithHint(aNode, aOffset, nsIFrameSelection::HINTLEFT, bidiLevel) ?
NS_OK : NS_ERROR_FAILURE;
ToggleDrawnStatus();
return rv;
}
nsIFrame * nsCaret::GetCaretFrame()
{
// Return null if we're not drawn to prevent anybody from trying to draw us.
if (!mDrawn)
return nsnull;
// Recompute the frame that we're supposed to draw in to guarantee that
// we're not going to try to draw into a stale (dead) frame.
PRInt32 unused;
nsIFrame *frame = nsnull;
nsresult rv = GetCaretFrameForNodeOffset(mLastContent, mLastContentOffset,
mLastHint, mLastBidiLevel, &frame,
&unused);
if (NS_FAILED(rv))
return nsnull;
return frame;
}
void nsCaret::InvalidateOutsideCaret()
{
nsIFrame *frame = GetCaretFrame();
// Only invalidate if we are not fully contained by our frame's rect.
if (frame && !frame->GetOverflowRect().Contains(GetCaretRect()))
InvalidateRects(mCaretRect, GetHookRect(), frame);
}
void nsCaret::PaintCaret(nsDisplayListBuilder *aBuilder,
nsIRenderingContext *aCtx,
const nsPoint &aOffset)
{
NS_ASSERTION(mDrawn, "The caret shouldn't be drawing");
if (mReadOnly)
aCtx->SetColor(NS_RGBA(0x85, 0x85, 0x85, 0xff));
else
aCtx->SetColor(NS_RGBA(0x00, 0x00, 0x00, 0xff));
aCtx->InvertRect(mCaretRect + aOffset);
if (!GetHookRect().IsEmpty())
aCtx->InvertRect(GetHookRect() + aOffset);
}
#ifdef XP_MAC
#pragma mark -
@ -543,11 +571,11 @@ nsCaret::DrawAtPositionWithHint(nsIDOMNode* aNode,
PRInt32 aOffset,
nsIFrameSelection::HINT aFrameHint,
PRUint8 aBidiLevel)
{
{
nsCOMPtr<nsIContent> contentNode = do_QueryInterface(aNode);
if (!contentNode)
return PR_FALSE;
nsIFrame* theFrame = nsnull;
PRInt32 theFrameOffset = 0;
@ -569,32 +597,35 @@ nsCaret::DrawAtPositionWithHint(nsIDOMNode* aNode,
return PR_FALSE;
}
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
if (!mDrawn)
{
// save stuff so we can erase the caret later
// save stuff so we can figure out what frame we're in later.
mLastContent = contentNode;
mLastContentOffset = aOffset;
mLastHint = aFrameHint;
mLastBidiLevel = aBidiLevel;
// If there has been a reflow, set the caret Bidi level to the level of the current frame
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
if (aBidiLevel & BIDI_LEVEL_UNDEFINED)
presShell->SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame));
}
GetCaretRectAndInvert(theFrame, theFrameOffset);
rv = UpdateCaretRects(theFrame, theFrameOffset);
if (NS_FAILED(rv))
return PR_FALSE;
InvalidateRects(mCaretRect, mHookRect, theFrame);
return PR_TRUE;
}
NS_IMETHODIMP
nsCaret::GetCaretFrameForNodeOffset (nsIContent* aContentNode,
PRInt32 aOffset,
nsIFrameSelection::HINT aFrameHint,
PRUint8 aBidiLevel,
nsIFrame** aReturnFrame,
PRInt32* aReturnOffset)
nsCaret::GetCaretFrameForNodeOffset(nsIContent* aContentNode,
PRInt32 aOffset,
nsIFrameSelection::HINT aFrameHint,
PRUint8 aBidiLevel,
nsIFrame** aReturnFrame,
PRInt32* aReturnOffset)
{
//get frame selection and find out what frame to use...
@ -612,7 +643,9 @@ nsCaret::GetCaretFrameForNodeOffset (nsIContent* aContentNode,
nsIFrame* theFrame = nsnull;
PRInt32 theFrameOffset = 0;
nsresult rv = frameSelection->GetFrameForNodeOffset(aContentNode, aOffset, aFrameHint, &theFrame, &theFrameOffset);
nsresult rv = frameSelection->GetFrameForNodeOffset(aContentNode, aOffset,
aFrameHint, &theFrame,
&theFrameOffset);
if (NS_FAILED(rv) || !theFrame)
return NS_ERROR_FAILURE;
@ -762,17 +795,20 @@ nsCaret::GetCaretFrameForNodeOffset (nsIContent* aContentNode,
//-----------------------------------------------------------------------------
void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordType, nsPoint &viewOffset, nsRect& outClipRect, nsIView **outRenderingView, nsIView **outRelativeView)
void nsCaret::GetViewForRendering(nsIFrame *caretFrame,
EViewCoordinates coordType,
nsPoint &viewOffset,
nsIView **outRenderingView,
nsIView **outRelativeView)
{
if (!caretFrame || !outRenderingView)
return;
// XXX by Masayuki Nakano:
// Our this code is not good. This is adhoc approach.
// This code is not good. This is adhoc approach.
// Our best approach is to use the event fired widget related view.
// But if we do so, we need large change for editor and this.
if (coordType == eIMECoordinates)
if (coordType == eIMECoordinates) {
#if defined(XP_MAC) || defined(XP_MACOSX) || defined(XP_WIN)
// #59405 and #313918, on Mac and Windows, the coordinate for IME need to be
// root view related.
@ -782,91 +818,63 @@ void nsCaret::GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordTy
// (nearest native window) related.
coordType = eRenderingViewCoordinates;
#endif
}
*outRenderingView = nsnull;
if (outRelativeView)
*outRelativeView = nsnull;
NS_ASSERTION(caretFrame, "Should have frame here");
NS_ASSERTION(caretFrame, "Should have a frame here");
viewOffset.x = 0;
viewOffset.y = 0;
nsPoint withinViewOffset(0, 0);
nsPoint withinViewOffset(0, 0);
// get the offset of this frame from its parent view (walks up frame hierarchy)
nsIView* theView = nsnull;
caretFrame->GetOffsetFromView(withinViewOffset, &theView);
if (theView == nsnull) return;
if (!theView)
return;
if (outRelativeView && coordType == eClosestViewCoordinates)
*outRelativeView = theView;
nsIView* returnView = nsnull; // views are not refcounted
// Note: views are not refcounted.
nsIView* returnView = nsIView::GetViewFor(theView->GetNearestWidget(nsnull));
// coorinates relative to the view we are going to use for drawing
if (coordType == eRenderingViewCoordinates)
{
nsIScrollableView* scrollableView = nsnull;
nsPoint drawViewOffset(0, 0); // offset to the view we are using to draw
// walk up to the first view with a widget
do {
//is this a scrollable view?
if (!scrollableView)
scrollableView = theView->ToScrollableView();
// This gets uses the first view with a widget
if (coordType == eRenderingViewCoordinates) {
if (returnView) {
// Now adjust the view offset for this view.
withinViewOffset += theView->GetOffsetTo(returnView);
// Account for the view's origin not lining up with the widget's
// (bug 190290)
withinViewOffset += returnView->GetPosition() -
returnView->GetBounds().TopLeft();
viewOffset = withinViewOffset;
if (theView->HasWidget())
{
returnView = theView;
// account for the view's origin not lining up with the widget's (bug 190290)
drawViewOffset += theView->GetPosition() - theView->GetBounds().TopLeft();
break;
if (outRelativeView)
*outRelativeView = returnView;
}
}
else {
// window-relative coordinates. Done for us by the view.
withinViewOffset += theView->GetOffsetTo(nsnull);
viewOffset = withinViewOffset;
// Get the relative view for top level window coordinates
if (outRelativeView && coordType == eTopLevelWindowCoordinates) {
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
if (presShell) {
nsIViewManager* vm = presShell->GetViewManager();
if (vm) {
vm->GetRootView(*outRelativeView);
}
}
drawViewOffset += theView->GetPosition();
theView = theView->GetParent();
} while (theView);
viewOffset = withinViewOffset;
viewOffset += drawViewOffset;
if (scrollableView)
{
nsRect bounds = scrollableView->View()->GetBounds();
scrollableView->GetScrollPosition(bounds.x, bounds.y);
bounds += drawViewOffset; // offset to coords of returned view
outClipRect = bounds;
}
else
{
NS_ASSERTION(returnView, "bulletproofing, see bug #24329");
if (returnView)
outClipRect = returnView->GetBounds();
}
if (outRelativeView)
*outRelativeView = returnView;
}
else
{
// window-relative coordinates (walk right to the top of the view hierarchy)
// we don't do anything with clipping here
viewOffset = withinViewOffset;
do {
if (!returnView && theView->HasWidget())
returnView = theView;
// is this right?
viewOffset += theView->GetPosition();
if (outRelativeView && coordType == eTopLevelWindowCoordinates)
*outRelativeView = theView;
theView = theView->GetParent();
} while (theView);
}
*outRenderingView = returnView;
}
@ -972,215 +980,132 @@ void nsCaret::DrawCaret()
}
DrawAtPositionWithHint(node, offset, hint, bidiLevel);
ToggleDrawnStatus();
}
void nsCaret::GetCaretRectAndInvert(nsIFrame* aFrame, PRInt32 aFrameOffset)
nsresult nsCaret::UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset)
{
NS_ASSERTION(aFrame, "Should have a frame here");
nsRect frameRect = aFrame->GetRect();
frameRect.x = 0; // the origin is accounted for in GetViewForRendering()
frameRect.x = 0;
frameRect.y = 0;
nsPoint viewOffset(0, 0);
nsRect clipRect;
nsIView *drawingView;
GetViewForRendering(aFrame, eRenderingViewCoordinates, viewOffset, clipRect, &drawingView, nsnull);
if (!drawingView)
return;
frameRect += viewOffset;
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
if (!presShell) return;
if (!presShell) return NS_ERROR_FAILURE;
nsPresContext *presContext = presShell->GetPresContext();
// if the view changed, or we don't have a rendering context, make one
// because of drawing issues, always make a new RC at the moment. See bug 28068
if (
#ifdef DONT_REUSE_RENDERING_CONTEXT
PR_TRUE ||
#endif
(mLastCaretView != drawingView) || !mRendContext)
{
mRendContext = nsnull; // free existing one if we have one
nsresult rv = presContext->DeviceContext()->
CreateRenderingContext(drawingView, *getter_AddRefs(mRendContext));
if (NS_FAILED(rv) || !mRendContext)
return;
}
// push a known good state
mRendContext->PushState();
// if we got a zero-height frame, it's probably a BR frame at the end of a non-empty line
// (see BRFrame::Reflow). In that case, figure out a height. We have to do this
// after we've got an RC.
if (frameRect.height == 0)
{
const nsStyleFont* fontStyle = aFrame->GetStyleFont();
const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
mRendContext->SetFont(fontStyle->mFont, vis->mLangGroup);
nsIWidget *widget = aFrame->GetWindow();
if (!widget)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIFontMetrics> fm;
mRendContext->GetFontMetrics(*getter_AddRefs(fm));
if (fm)
{
nscoord ascent, descent;
fm->GetMaxAscent(ascent);
fm->GetMaxDescent(descent);
frameRect.height = ascent + descent;
frameRect.y -= ascent; // BR frames sit on the baseline of the text, so we need to subtract
// the ascent to account for the frame height.
}
nsCOMPtr<nsIRenderingContext> rendContext;
nsresult rv = presContext->DeviceContext()->
CreateRenderingContext(widget, *getter_AddRefs(rendContext));
NS_ENSURE_SUCCESS(rv, rv);
if (!rendContext)
return NS_ERROR_UNEXPECTED;
const nsStyleFont* fontStyle = aFrame->GetStyleFont();
const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
rendContext->SetFont(fontStyle->mFont, vis->mLangGroup);
nsCOMPtr<nsIFontMetrics> fm;
rendContext->GetFontMetrics(*getter_AddRefs(fm));
if (fm)
{
nscoord ascent, descent;
fm->GetMaxAscent(ascent);
fm->GetMaxDescent(descent);
frameRect.height = ascent + descent;
frameRect.y -= ascent; // BR frames sit on the baseline of the text, so we need to subtract
// the ascent to account for the frame height.
}
}
// views are not refcounted
mLastCaretView = drawingView;
if (!mDrawn)
{
nsPoint framePos(0, 0);
nsRect caretRect = frameRect;
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
nsCOMPtr<nsISelectionPrivate> privateSelection = do_QueryInterface(domSelection);
mCaretRect = frameRect;
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
nsCOMPtr<nsISelectionPrivate> privateSelection = do_QueryInterface(domSelection);
// if cache in selection is available, apply it, else refresh it
privateSelection->GetCachedFrameOffset(aFrame, aFrameOffset, framePos);
nsPoint framePos;
caretRect += framePos;
// if cache in selection is available, apply it, else refresh it
privateSelection->GetCachedFrameOffset(aFrame, aFrameOffset, framePos);
caretRect.width = mCaretTwipsWidth;
mCaretRect += framePos;
mCaretRect.width = mCaretTwipsWidth;
// Check if the caret intersects with the right edge
// of the frame. If it does, and the frame's right edge
// is close to the right edge of the clipRect, we may
// need to adjust the caret's x position so that it
// remains visible.
return UpdateHookRect(presContext);
}
nscoord caretXMost = caretRect.XMost();
nscoord frameXMost = frameRect.XMost();
nsresult nsCaret::UpdateHookRect(nsPresContext* aPresContext)
{
mHookRect.Empty();
if (caretXMost > frameXMost)
{
nscoord clipXMost = clipRect.XMost();
if (caretRect.x == frameRect.x && caretRect.x <= clipXMost &&
caretXMost > clipXMost)
{
// The left side of the caret is attached to the left edge of
// the frame, and it is wider than the frame itself. It also
// overlaps the right edge of the clipRect so we need to nudge
// it to the left so that it remains visible.
//
// We usually hit this case when the caret is attached to a
// br frame (which is about 1 twip in width) that is positioned
// at the right edge of the content area because it is right aligned
// or the right margin pushed it beyond the width of the view port.
caretRect.x = clipXMost - caretRect.width;
}
else if (caretRect.x == frameXMost && frameXMost == clipXMost)
{
// The left side of the caret is attached to the right edge of
// the frame, but it's going to get clipped because it's positioned
// on the right edge of the clipRect, so nudge it to the
// left so it remains visible.
//
// We usually hit this case when the caret is after the last
// character on the line, and the line exceeds the width of the
// view port.
caretRect.x = clipXMost - caretRect.width;
}
}
mCaretRect.IntersectRect(clipRect, caretRect);
#ifdef IBMBIDI
// Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction
PRBool bidiEnabled;
nsRect hookRect;
PRBool isCaretRTL=PR_FALSE;
if (mBidiKeyboard)
mBidiKeyboard->IsLangRTL(&isCaretRTL);
if (isCaretRTL)
// Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction
PRBool bidiEnabled;
PRBool isCaretRTL=PR_FALSE;
if (mBidiKeyboard)
mBidiKeyboard->IsLangRTL(&isCaretRTL);
if (isCaretRTL)
{
bidiEnabled = PR_TRUE;
aPresContext->SetBidiEnabled(bidiEnabled);
}
else
bidiEnabled = aPresContext->BidiEnabled();
if (bidiEnabled)
{
if (isCaretRTL != mKeyboardRTL)
{
bidiEnabled = PR_TRUE;
presContext->SetBidiEnabled(bidiEnabled);
}
else
bidiEnabled = presContext->BidiEnabled();
if (bidiEnabled)
{
if (isCaretRTL != mKeyboardRTL)
/* if the caret bidi level and the keyboard language direction are not in
* synch, the keyboard language must have been changed by the
* user, and if the caret is in a boundary condition (between left-to-right and
* right-to-left characters) it may have to change position to
* reflect the location in which the next character typed will
* appear. We will call |SelectionLanguageChange| and exit
* without drawing the caret in the old position.
*/
mKeyboardRTL = isCaretRTL;
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
if (domSelection)
{
/* if the caret bidi level and the keyboard language direction are not in
* synch, the keyboard language must have been changed by the
* user, and if the caret is in a boundary condition (between left-to-right and
* right-to-left characters) it may have to change position to
* reflect the location in which the next character typed will
* appear. We will call |SelectionLanguageChange| and exit
* without drawing the caret in the old position.
*/
mKeyboardRTL = isCaretRTL;
nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
if (domSelection)
if (NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL)))
{
if (NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL)))
{
mRendContext->PopState();
#ifdef DONT_REUSE_RENDERING_CONTEXT
mRendContext = nsnull;
#endif
return;
}
return NS_ERROR_FAILURE;
}
}
// If keyboard language is RTL, draw the hook on the left; if LTR, to the right
// The height of the hook rectangle is the same as the width of the caret
// rectangle.
hookRect.SetRect(caretRect.x + ((isCaretRTL) ?
mBidiIndicatorTwipsSize * -1 :
caretRect.width),
caretRect.y + mBidiIndicatorTwipsSize,
mBidiIndicatorTwipsSize,
caretRect.width);
mHookRect.IntersectRect(clipRect, hookRect);
}
// If keyboard language is RTL, draw the hook on the left; if LTR, to the right
// The height of the hook rectangle is the same as the width of the caret
// rectangle.
mHookRect.SetRect(mCaretRect.x + ((isCaretRTL) ?
mBidiIndicatorTwipsSize * -1 :
mCaretRect.width),
mCaretRect.y + mBidiIndicatorTwipsSize,
mBidiIndicatorTwipsSize,
mCaretRect.width);
}
#endif //IBMBIDI
}
if (mReadOnly)
mRendContext->SetColor(NS_RGB(85, 85, 85)); // we are drawing it; gray
else
mRendContext->SetColor(NS_RGB(255,255,255));
mRendContext->InvertRect(mCaretRect);
return NS_OK;
}
// Ensure the buffer is flushed (Cocoa needs this), since we're drawing
// outside the normal painting process.
mRendContext->FlushRect(mCaretRect);
#ifdef IBMBIDI
if (!mHookRect.IsEmpty()) // if Bidi support is disabled, the rectangle remains empty and won't be drawn
mRendContext->InvertRect(mHookRect);
#endif
mRendContext->PopState();
ToggleDrawnStatus();
if (mDrawn) {
aFrame->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
}
#ifdef DONT_REUSE_RENDERING_CONTEXT
mRendContext = nsnull;
#endif
// static
void nsCaret::InvalidateRects(const nsRect &aRect, const nsRect &aHook,
nsIFrame *aFrame)
{
NS_ASSERTION(aFrame, "Must have a frame to invalidate");
nsRect rect;
rect.UnionRect(aRect, aHook);
aFrame->Invalidate(rect, PR_FALSE);
}
#ifdef XP_MAC

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=2 sw=2 et tw=78:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -73,34 +74,84 @@ class nsCaret : public nsICaret,
NS_IMETHOD GetCaretVisible(PRBool *outMakeVisible);
NS_IMETHOD SetCaretVisible(PRBool intMakeVisible);
NS_IMETHOD SetCaretReadOnly(PRBool inMakeReadonly);
NS_IMETHOD GetCaretCoordinates(EViewCoordinates aRelativeToType, nsISelection *inDOMSel, nsRect* outCoordinates, PRBool* outIsCollapsed, nsIView **outView);
virtual PRBool GetCaretReadOnly()
{
return mReadOnly;
}
NS_IMETHOD GetCaretCoordinates(EViewCoordinates aRelativeToType,
nsISelection *inDOMSel,
nsRect* outCoordinates,
PRBool* outIsCollapsed,
nsIView **outView);
NS_IMETHOD EraseCaret();
NS_IMETHOD SetVisibilityDuringSelection(PRBool aVisibility);
NS_IMETHOD DrawAtPosition(nsIDOMNode* aNode, PRInt32 aOffset);
nsIFrame* GetCaretFrame();
nsRect GetCaretRect()
{
nsRect r;
r.UnionRect(mCaretRect, GetHookRect());
return r;
}
nsIContent* GetCaretContent()
{
if (mDrawn)
return mLastContent;
return nsnull;
}
void InvalidateOutsideCaret();
void PaintCaret(nsDisplayListBuilder *aBuilder,
nsIRenderingContext *aCtx,
const nsPoint &aOffset);
//nsISelectionListener interface
NS_DECL_NSISELECTIONLISTENER
static void CaretBlinkCallback(nsITimer *aTimer, void *aClosure);
NS_IMETHOD GetCaretFrameForNodeOffset (nsIContent* aContentNode, PRInt32 aOffset, nsIFrameSelection::HINT aFrameHint, PRUint8 aBidiLevel,
nsIFrame** aReturnFrame, PRInt32* aReturnOffset);
NS_IMETHOD GetCaretFrameForNodeOffset(nsIContent* aContentNode,
PRInt32 aOffset,
nsIFrameSelection::HINT aFrameHint,
PRUint8 aBidiLevel,
nsIFrame** aReturnFrame,
PRInt32* aReturnOffset);
protected:
void KillTimer();
nsresult PrimeTimer();
nsresult StartBlinking();
nsresult StopBlinking();
void GetViewForRendering(nsIFrame *caretFrame, EViewCoordinates coordType, nsPoint &viewOffset, nsRect& outClipRect, nsIView **outRenderingView, nsIView **outRelativeView);
PRBool DrawAtPositionWithHint(nsIDOMNode* aNode, PRInt32 aOffset, nsIFrameSelection::HINT aFrameHint, PRUint8 aBidiLevel);
void GetViewForRendering(nsIFrame *caretFrame,
EViewCoordinates coordType,
nsPoint &viewOffset,
nsIView **outRenderingView,
nsIView **outRelativeView);
PRBool DrawAtPositionWithHint(nsIDOMNode* aNode,
PRInt32 aOffset,
nsIFrameSelection::HINT aFrameHint,
PRUint8 aBidiLevel);
PRBool MustDrawCaret();
void DrawCaret();
void DrawCaretAfterBriefDelay();
void GetCaretRectAndInvert(nsIFrame* aFrame, PRInt32 aFrameOffset);
void ToggleDrawnStatus() { mDrawn = !mDrawn; }
nsresult UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset);
nsresult UpdateHookRect(nsPresContext* aPresContext);
static void InvalidateRects(const nsRect &aRect, const nsRect &aHook,
nsIFrame *aFrame);
nsRect GetHookRect()
{
#ifdef IBMBIDI
return mHookRect;
#else
return nsRect();
#endif
}
void ToggleDrawnStatus() { mDrawn = !mDrawn; }
protected:
@ -120,12 +171,17 @@ protected:
PRPackedBool mReadOnly; // it the caret in readonly state (draws differently)
PRPackedBool mShowDuringSelection; // show when text is selected
nsRect mCaretRect; // the last caret rect
nsIView* mLastCaretView; // last view that we used for drawing. Cached so we can tell when we need to make a new RC
nsCOMPtr<nsIContent> mLastContent; // store the content the caret was last requested to be drawn in (by DrawAtPosition()/DrawCaret()),
// note that this can be different than where it was actually drawn (anon <BR> in text control)
nsRect mCaretRect; // the last caret rect, in the coodinates of the last frame.
nsCOMPtr<nsIContent> mLastContent; // store the content the caret was last requested to be drawn
// in (by DrawAtPosition()/DrawCaret()),
// note that this can be different than where it was
// actually drawn (anon <BR> in text control)
PRInt32 mLastContentOffset; // the offset for the last request
nsIFrameSelection::HINT mLastHint; // the hint associated with the last request, see also mLastBidiLevel below
nsIFrameSelection::HINT mLastHint; // the hint associated with the last request, see also
// mLastBidiLevel below
#ifdef IBMBIDI
nsRect mHookRect; // directional hook on the caret
nsCOMPtr<nsIBidiKeyboard> mBidiKeyboard; // Bidi keyboard object to set and query keyboard language

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=2 sw=2 et tw=78:
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -55,10 +56,11 @@
#endif
nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
PRBool aIsForEvents, nsIFrame* aMovingFrame)
PRBool aIsForEvents, PRBool aBuildCaret, nsIFrame* aMovingFrame)
: mReferenceFrame(aReferenceFrame),
mMovingFrame(aMovingFrame),
mIgnoreScrollFrame(nsnull),
mBuildCaret(aBuildCaret),
mEventDelivery(aIsForEvents),
mIsAtRootOfPseudoStackingContext(PR_FALSE) {
PL_InitArenaPool(&mPool, "displayListArena", 1024, sizeof(void*)-1);
@ -75,6 +77,10 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
getter_AddRefs(mBoundingSelection));
}
}
if (mIsBackgroundOnly) {
mBuildCaret = PR_FALSE;
}
}
nsDisplayListBuilder::~nsDisplayListBuilder() {
@ -82,6 +88,64 @@ nsDisplayListBuilder::~nsDisplayListBuilder() {
PL_FinishArenaPool(&mPool);
}
nsICaret *
nsDisplayListBuilder::GetCaret() {
NS_ASSERTION(mCaretStates.Length() > 0, "Not enough presshells");
nsIFrame* frame = GetCaretFrame();
if (!frame) {
return nsnull;
}
nsIPresShell* shell = frame->GetPresContext()->PresShell();
nsCOMPtr<nsICaret> caret;
shell->GetCaret(getter_AddRefs(caret));
return caret;
}
void
nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
const nsRect& aDirtyRect) {
if (!mBuildCaret) {
return;
}
nsIPresShell* shell = aReferenceFrame->GetPresContext()->PresShell();
nsCOMPtr<nsICaret> caret;
shell->GetCaret(getter_AddRefs(caret));
nsIFrame* frame = caret->GetCaretFrame();
nsLayoutUtils::MarkCaretSubtreeForPainting(this, aReferenceFrame,
frame, caret->GetCaretRect(),
aDirtyRect, PR_TRUE);
mCaretStates.AppendElement(frame);
}
void
nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame,
const nsRect& aDirtyRect)
{
if (!mBuildCaret) {
return;
}
// Pop the state off.
NS_ASSERTION(mCaretStates.Length() > 0, "Leaving too many PresShell");
nsICaret* caret = GetCaret();
if (caret) {
nsLayoutUtils::MarkCaretSubtreeForPainting(this, aReferenceFrame,
GetCaretFrame(),
caret->GetCaretRect(),
aDirtyRect, PR_FALSE);
} else {
NS_ASSERTION(GetCaretFrame() == nsnull,
"GetCaret and LeavePresShell diagree");
}
mCaretStates.SetLength(mCaretStates.Length() - 1);
}
void*
nsDisplayListBuilder::Allocate(size_t aSize) {
void *tmp;
@ -455,6 +519,14 @@ nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder,
mFrame->GetStyleContext(), 0);
}
void
nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
// Note: Because we exist, we know that the caret is visible, so we don't
// need to check for the caret's visibility.
mCaret->PaintCaret(aBuilder, aCtx, aBuilder->ToReferenceFrame(mFrame));
}
void
nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
@ -622,7 +694,6 @@ void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder,
// depth of nested translucent elements. This will be fixed when we move to
// cairo with support for real alpha channels in surfaces, so we don't have
// to do this white/black hack anymore.
nsIViewManager* vm = mFrame->GetPresContext()->GetViewManager();
float opacity = mFrame->GetStyleDisplay()->mOpacity;
nsRect bounds;
@ -661,6 +732,7 @@ void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder,
#elif !defined(XP_MACOSX)
nsIViewManager* vm = mFrame->GetPresContext()->GetViewManager();
nsIViewManager::BlendingBuffers* buffers =
vm->CreateBlendingBuffers(aCtx, PR_FALSE, nsnull, mNeedAlpha, bounds);
if (!buffers) {

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=2 sw=2 et tw=78:
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -50,6 +51,8 @@
#include "nsISelection.h"
#include "plarena.h"
#include "nsLayoutUtils.h"
#include "nsICaret.h"
#include "nsTArray.h"
#include <stdlib.h>
@ -119,6 +122,8 @@ public:
* is the origin of the reference coordinate system for this display list
* @param aIsForEvents PR_TRUE if we're creating this list in order to
* determine which frame is under the mouse position
* @param aBuildCaret whether or not we should include the caret in any
* display lists that we make.
* @param aMovingFrame a frame whose subtree should be regarded as
* moving; moving frames are not allowed to clip or cover (during
* OptimizeVisibility) non-moving frames. E.g. when we're constructing
@ -126,7 +131,7 @@ public:
* operation, we specify the scrolled frame as the moving frame.
*/
nsDisplayListBuilder(nsIFrame* aReferenceFrame, PRBool aIsForEvents,
nsIFrame* aMovingFrame = nsnull);
PRBool aBuildCaret, nsIFrame* aMovingFrame = nsnull);
~nsDisplayListBuilder();
/**
@ -193,6 +198,42 @@ public:
* Get the scrollframe to ignore, if any.
*/
nsIFrame* GetIgnoreScrollFrame() { return mIgnoreScrollFrame; }
/**
* Display the caret if needed.
*/
nsresult DisplayCaret(nsIFrame* aFrame, const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) {
nsIFrame* frame = GetCaretFrame();
if (aFrame != frame) {
return NS_OK;
}
return frame->DisplayCaret(this, aDirtyRect, aLists);
}
/**
* Get the frame that the caret is supposed to draw in.
* If the caret is currently invisible, this will be null.
*/
nsIFrame* GetCaretFrame() {
if (mBuildCaret) {
NS_ASSERTION(mCaretStates.Length() > 0, "Not enough presshells");
return mCaretStates[mCaretStates.Length() - 1];
}
return nsnull;
}
/**
* Get the caret associated with the current presshell.
*/
nsICaret* GetCaret();
/**
* Notify the display list builder that we're entering a presshell.
* aReferenceFrame should be a frame in the new presshell and aDirtyRect
* should be the current dirty rect in aReferenceFrame's coordinate space.
*/
void EnterPresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect);
/**
* Notify the display list builder that we're leaving a presshell.
*/
void LeavePresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect);
/**
* Allocate memory in our arena. It will only be freed when this display list
@ -231,6 +272,8 @@ private:
nsIFrame* mIgnoreScrollFrame;
PLArenaPool mPool;
nsCOMPtr<nsISelection> mBoundingSelection;
nsTArray<nsIFrame *> mCaretStates;
PRPackedBool mBuildCaret;
PRPackedBool mEventDelivery;
PRPackedBool mIsBackgroundOnly;
PRPackedBool mIsAtRootOfPseudoStackingContext;
@ -757,6 +800,30 @@ protected:
#endif
};
MOZ_DECL_CTOR_COUNTER(nsDisplayCaret)
class nsDisplayCaret : public nsDisplayItem {
public:
nsDisplayCaret(nsIFrame* aCaretFrame, nsICaret *aCaret)
: nsDisplayItem(aCaretFrame), mCaret(aCaret) {
MOZ_COUNT_CTOR(nsDisplayCaret);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayCaret() {
MOZ_COUNT_DTOR(nsDisplayCaret);
}
#endif
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
// The caret returns a rect in the coordinates of mFrame.
return mCaret->GetCaretRect() + aBuilder->ToReferenceFrame(mFrame);
}
virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
const nsRect& aDirtyRect);
NS_DISPLAY_DECL_NAME("Caret");
protected:
nsCOMPtr<nsICaret> mCaret;
};
/**
* The standard display item to paint the CSS borders of a frame.
*/

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -56,10 +57,10 @@ class nsISelection;
class nsIDOMNode;
// IID for the nsICaret interface
// dcb01833-509d-4fcb-8f7f-6beb006261b9
// 89ce0f4b-17c0-4362-b641-12ef696134d6
#define NS_ICARET_IID \
{ 0xdcb01833, 0x509d, 0x4fcb, \
{ 0x8f, 0x7f, 0x6b, 0xeb, 0x00, 0x62, 0x61, 0xb9 } }
{ 0x89ce0f4b, 0x17c0, 0x4362, \
{ 0xb6, 0x41, 0x12, 0xef, 0x69, 0x61, 0x34, 0xd6 } }
class nsICaret: public nsISupports
{
@ -95,14 +96,21 @@ public:
*/
NS_IMETHOD SetCaretReadOnly(PRBool inMakeReadonly) = 0;
virtual PRBool GetCaretReadOnly() = 0;
/** GetCaretCoordinates
* Get the position of the caret in coordinates relative to the typed specified (aRelativeToType).
* Get the position of the caret in coordinates relative to the typed
* specified (aRelativeToType).
* If the selection is collapsed, this returns the caret location
* and true in outIsCollapsed.
* If the selection is not collapsed, this returns the location of the focus pos,
* and false in outIsCollapsed.
*/
NS_IMETHOD GetCaretCoordinates(EViewCoordinates aRelativeToType, nsISelection *aDOMSel, nsRect *outCoordinates, PRBool *outIsCollapsed, nsIView **outView) = 0;
NS_IMETHOD GetCaretCoordinates(EViewCoordinates aRelativeToType,
nsISelection *aDOMSel,
nsRect *outCoordinates,
PRBool *outIsCollapsed,
nsIView **outView) = 0;
/** Erase Caret
* this will erase the caret if its drawn and reset drawn status
@ -121,12 +129,47 @@ public:
NS_IMETHOD DrawAtPosition(nsIDOMNode* aNode, PRInt32 aOffset) = 0;
/** GetCaretFrameForNodeOffset
* Get the frame and content offset at which the caret is drawn,
* invoking the bidi caret positioning algorithm if necessary
**/
NS_IMETHOD GetCaretFrameForNodeOffset (nsIContent* aContentNode, PRInt32 aOffset, nsIFrameSelection::HINT aFrameHint, PRUint8 aBidiLevel,
nsIFrame** aReturnFrame, PRInt32* aReturnOffset) = 0;
* Get the frame and content offset at which the caret is drawn,
* invoking the bidi caret positioning algorithm if necessary
**/
NS_IMETHOD GetCaretFrameForNodeOffset(nsIContent* aContentNode,
PRInt32 aOffset,
nsIFrameSelection::HINT aFrameHint,
PRUint8 aBidiLevel,
nsIFrame** aReturnFrame,
PRInt32* aReturnOffset) = 0;
/** GetCaretFrame
* Get the current frame that the caret should be drawn in. If the caret is
* not currently visible (i.e., it is between blinks), then this will
* return null.
*/
virtual nsIFrame *GetCaretFrame() = 0;
/** GetCaretRect
* Get the current caret rect. Only call this when GetCaretFrame returns
* non-null.
*/
virtual nsRect GetCaretRect() = 0;
/** GetCaretContent
* Get the content that the caret was last drawn in.
*/
virtual nsIContent* GetCaretContent() = 0;
/** InvalidateOutsideCaret
* Invalidate the area that the caret currently occupies if the caret is
* outside of its frame's overflow area. This is used when the content that
* the caret is currently drawn is is being deleted or reflowed.
*/
virtual void InvalidateOutsideCaret() = 0;
/** PaintCaret
* Actually paint the caret onto the given rendering context.
*/
virtual void PaintCaret(nsDisplayListBuilder *aBuilder,
nsIRenderingContext *aCtx,
const nsPoint &aOffset) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsICaret, NS_ICARET_IID)

View File

@ -96,9 +96,8 @@ class nsISelection;
template<class E> class nsCOMArray;
#define NS_IPRESSHELL_IID \
{ 0x998cde06, 0x5fa4, 0x4c8b, \
{ 0x94, 0x8d, 0xc7, 0x15, 0x74, 0x75, 0xab, 0x6f } }
{ 0xa736d2fd, 0x0191, 0x42ea, \
{ 0xb1, 0xb0, 0x80, 0x45, 0x1d, 0xfa, 0x03, 0x53 } }
// Constants uses for ScrollFrameIntoView() function
#define NS_PRESSHELL_SCROLL_TOP 0
@ -485,6 +484,11 @@ public:
*/
NS_IMETHOD GetCaret(nsICaret **aOutCaret) = 0;
/**
* Set the current caret to a new caret. Returns the old caret.
*/
virtual already_AddRefed<nsICaret> SetCaret(nsICaret *aNewCaret) = 0;
/**
* Should the images have borders etc. Actual visual effects are determined
* by the frames. Visual effects may not effect layout, only display.

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -617,10 +618,16 @@ static PRBool gDumpRepaintRegionForCopy = PR_FALSE;
nsIFrame*
nsLayoutUtils::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt)
{
nsDisplayListBuilder builder(aFrame, PR_TRUE);
nsDisplayListBuilder builder(aFrame, PR_TRUE, PR_FALSE);
nsDisplayList list;
nsRect target(aPt, nsSize(1, 1));
builder.EnterPresShell(aFrame, target);
nsresult rv =
aFrame->BuildDisplayListForStackingContext(&builder, nsRect(aPt, nsSize(1, 1)), &list);
aFrame->BuildDisplayListForStackingContext(&builder, target, &list);
builder.LeavePresShell(aFrame, target);
NS_ENSURE_SUCCESS(rv, nsnull);
#ifdef DEBUG
@ -669,11 +676,16 @@ nsresult
nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFrame,
const nsRegion& aDirtyRegion, nscolor aBackground)
{
nsDisplayListBuilder builder(aFrame, PR_FALSE);
nsDisplayListBuilder builder(aFrame, PR_FALSE, PR_TRUE);
nsDisplayList list;
nsRect dirtyRect = aDirtyRegion.GetBounds();
builder.EnterPresShell(aFrame, dirtyRect);
nsresult rv =
aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list);
builder.LeavePresShell(aFrame, dirtyRect);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_GET_A(aBackground) > 0) {
@ -791,10 +803,15 @@ nsLayoutUtils::ComputeRepaintRegionForCopy(nsIFrame* aRootFrame,
// hierarchy has already been updated for the move.)
nsRect rect;
rect.UnionRect(aCopyRect, aCopyRect + aDelta);
nsDisplayListBuilder builder(aRootFrame, PR_FALSE, aMovingFrame);
nsDisplayListBuilder builder(aRootFrame, PR_FALSE, PR_TRUE, aMovingFrame);
nsDisplayList list;
builder.EnterPresShell(aRootFrame, rect);
nsresult rv =
aRootFrame->BuildDisplayListForStackingContext(&builder, rect, &list);
builder.LeavePresShell(aRootFrame, rect);
NS_ENSURE_SUCCESS(rv, rv);
#ifdef DEBUG
@ -943,3 +960,40 @@ nsLayoutUtils::ScrollIntoView(nsIFormControlFrame* aFormFrame)
}
}
}
void
nsLayoutUtils::MarkCaretSubtreeForPainting(nsDisplayListBuilder* aBuilder,
nsIFrame* aReferenceFrame,
nsIFrame* aCaretFrame,
const nsRect& aCaretRect,
const nsRect& aRealDirtyRect,
PRBool aMark)
{
// Easy test: If there is no caret, we don't have anything to do.
if (!aCaretFrame) {
return;
}
NS_ASSERTION(aReferenceFrame, "We must have a reference frame");
// Check if the dirty rect intersects with the caret's dirty rect.
nsRect caretRect = aCaretRect + aCaretFrame->GetOffsetTo(aReferenceFrame);
if (!caretRect.Intersects(aRealDirtyRect)) {
return;
}
if (aMark) {
// Okay, our rects intersect, let's mark the frame and all of its ancestors.
do {
aCaretFrame->AddStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
aCaretFrame = aCaretFrame->GetParent();
} while (aCaretFrame);
return;
}
do {
aCaretFrame->RemoveStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
aCaretFrame = aCaretFrame->GetParent();
} while (aCaretFrame);
}

View File

@ -47,6 +47,7 @@ class nsIScrollableView;
class nsIScrollableFrame;
class nsIDOMEvent;
class nsRegion;
class nsDisplayListBuilder;
#include "prtypes.h"
#include "nsStyleContext.h"
@ -412,6 +413,26 @@ public:
* @param aFormFrame Frame to scroll into view.
*/
static void ScrollIntoView(nsIFormControlFrame* aFormFrame);
/**
* Ensure that the caret frame's subtree is painted by the next paint.
* @param aBuilder The display list builder that we're going to be painting
* with.
* @param aCaretFrame The frame that the caret is currently in.
* @param aReferenceFrame The frame whose coodinate space aRealDirtyRect is
* in.
* @param aCaretRect The rect (in aCaretFrame's coordinates) that the caret
* wants to be in.
* @param aRealDirtyRect The rect (in aReferenceFrame's coordinates) that is
* the original dirty rect.
* @param aMark Whether we're marking or unmarking the frames.
*/
static void MarkCaretSubtreeForPainting(nsDisplayListBuilder* aBuilder,
nsIFrame* aReferenceFrame,
nsIFrame* aCaretFrame,
const nsRect& aCaretRect,
const nsRect& aRealDirtyRect,
PRBool aMark);
};
#endif // nsLayoutUtils_h__

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=2 sw=2 et tw=78:
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -1200,6 +1201,7 @@ public:
NS_IMETHOD SetCaretReadOnly(PRBool aReadOnly);
NS_IMETHOD GetCaretEnabled(PRBool *aOutEnabled);
NS_IMETHOD SetCaretVisibilityDuringSelection(PRBool aVisibility);
virtual already_AddRefed<nsICaret> SetCaret(nsICaret *aNewCaret);
NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
@ -3093,6 +3095,14 @@ NS_IMETHODIMP PresShell::GetCaret(nsICaret **outCaret)
return NS_OK;
}
already_AddRefed<nsICaret> PresShell::SetCaret(nsICaret *aNewCaret)
{
nsICaret *oldCaret = nsnull;
mCaret.swap(oldCaret);
mCaret = aNewCaret;
return oldCaret;
}
NS_IMETHODIMP PresShell::SetCaretEnabled(PRBool aInEnable)
{
nsresult result = NS_OK;
@ -3362,9 +3372,12 @@ static void UpdateViewProperties(nsPresContext* aPresContext, nsIViewManager* aV
NS_IMETHODIMP
PresShell::StyleChangeReflow()
{
WillCauseReflow();
if (mCaret) {
mCaret->InvalidateOutsideCaret();
}
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
if (rootFrame) {
// Kick off a top-down reflow
@ -5136,6 +5149,9 @@ PresShell::CharacterDataChanged(nsIDocument *aDocument,
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
WillCauseReflow();
if (mCaret) {
mCaret->InvalidateOutsideCaret();
}
mFrameConstructor->CharacterDataChanged(aContent, aAppend);
VERIFY_STYLE_TREE;
DidCauseReflow();
@ -5232,11 +5248,10 @@ PresShell::ContentRemoved(nsIDocument *aDocument,
NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentRemoved");
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
// XXX fix for bug 304383. Remove when bug 287813 is fixed?
// Make sure that the caret doesn't leave a turd where the child used to be.
if (mCaret) {
nsIFrame* frame = GetPrimaryFrameFor(aChild);
if (frame && (frame->GetStateBits() & NS_FRAME_EXTERNAL_REFERENCE)) {
mCaret->EraseCaret();
if (mCaret->GetCaretContent() == aChild) {
mCaret->InvalidateOutsideCaret();
}
}
@ -5505,7 +5520,7 @@ PresShell::RenderOffscreen(nsRect aRect, PRBool aUntrusted,
return NS_OK;
}
nsDisplayListBuilder builder(rootFrame, PR_FALSE);
nsDisplayListBuilder builder(rootFrame, PR_FALSE, PR_FALSE);
nsDisplayList list;
nsIScrollableView* scrollingView = nsnull;
mViewManager->GetRootScrollableView(&scrollingView);
@ -5518,7 +5533,11 @@ PresShell::RenderOffscreen(nsRect aRect, PRBool aUntrusted,
builder.SetIgnoreScrollFrame(GetRootScrollFrame());
}
builder.EnterPresShell(rootFrame, r);
rv = rootFrame->BuildDisplayListForStackingContext(&builder, r, &list);
builder.LeavePresShell(rootFrame, r);
NS_ENSURE_SUCCESS(rv, rv);
nsRegion region(r);
@ -5566,9 +5585,6 @@ PresShell::Paint(nsIView* aView,
}
return NS_OK;
}
if (mCaret)
mCaret->EraseCaret();
nsLayoutUtils::PaintFrame(aRenderingContext, frame, aDirtyRegion,
backgroundColor);
@ -5810,7 +5826,7 @@ PresShell::HandleEvent(nsIView *aView,
nsPoint eventPoint
= nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
nsIFrame* targetFrame =
targetFrame = nsLayoutUtils::GetFrameForPoint(frame, eventPoint);
nsLayoutUtils::GetFrameForPoint(frame, eventPoint);
if (targetFrame) {
PresShell* shell =
NS_STATIC_CAST(PresShell*, targetFrame->GetPresContext()->PresShell());
@ -7421,7 +7437,7 @@ PresShellViewEventListener::WillRefreshRegion(nsIViewManager *aViewManager,
nsIRegion *aRegion,
PRUint32 aUpdateFlags)
{
return HideCaret();
return NS_OK;
}
NS_IMETHODIMP
@ -7433,7 +7449,7 @@ PresShellViewEventListener::DidRefreshRegion(nsIViewManager *aViewManager,
{
nsCSSRendering::DidPaint();
return RestoreCaretVisibility();
return NS_OK;
}
NS_IMETHODIMP
@ -7443,7 +7459,7 @@ PresShellViewEventListener::WillRefreshRect(nsIViewManager *aViewManager,
const nsRect *aRect,
PRUint32 aUpdateFlags)
{
return HideCaret();
return NS_OK;
}
NS_IMETHODIMP
@ -7455,7 +7471,7 @@ PresShellViewEventListener::DidRefreshRect(nsIViewManager *aViewManager,
{
nsCSSRendering::DidPaint();
return RestoreCaretVisibility();
return NS_OK;
}

View File

@ -915,6 +915,17 @@ nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
return DisplayOutlineUnconditional(aBuilder, aLists);
}
nsresult
nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
{
if (!IsVisibleForPainting(aBuilder))
return NS_OK;
return aLists.Content()->AppendNewToTop(
new (aBuilder) nsDisplayCaret(this, aBuilder->GetCaret()));
}
nsresult
nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists,
@ -1118,6 +1129,8 @@ BuildDisplayListWithOverflowClip(nsDisplayListBuilder* aBuilder, nsIFrame* aFram
nsDisplayListCollection set;
nsresult rv = aFrame->BuildDisplayList(aBuilder, aDirtyRect, set);
NS_ENSURE_SUCCESS(rv, rv);
rv = aBuilder->DisplayCaret(aFrame, aDirtyRect, aSet);
NS_ENSURE_SUCCESS(rv, rv);
return aFrame->OverflowClip(aBuilder, set, aSet, aClipRect);
}
@ -1439,9 +1452,12 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
overflowClip);
} else {
rv = aChild->BuildDisplayList(aBuilder, dirty, aLists);
if (NS_SUCCEEDED(rv)) {
rv = aBuilder->DisplayCaret(aChild, dirty, aLists);
}
}
aChild->RemoveStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
return NS_OK;
return rv;
}
nsDisplayList list;
@ -1451,6 +1467,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
isComposited) {
// True stacking context
rv = aChild->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
if (NS_SUCCEEDED(rv)) {
rv = aBuilder->DisplayCaret(aChild, dirty, aLists);
}
} else {
nsRect clipRect;
PRBool applyAbsPosClipping =
@ -1473,6 +1492,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
pseudoStack, overflowClip);
} else {
rv = aChild->BuildDisplayList(aBuilder, clippedDirtyRect, pseudoStack);
if (NS_SUCCEEDED(rv)) {
rv = aBuilder->DisplayCaret(aChild, dirty, aLists);
}
}
aChild->RemoveStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);

View File

@ -303,7 +303,13 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
return NS_OK;
nsRect dirty = aDirtyRect - f->GetOffsetTo(this);
return f->BuildDisplayListForStackingContext(aBuilder, dirty, aLists.Content());
aBuilder->EnterPresShell(f, dirty);
rv = f->BuildDisplayListForStackingContext(aBuilder, dirty, aLists.Content());
aBuilder->LeavePresShell(f, dirty);
return rv;
}
void

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -99,10 +100,10 @@ struct nsMargin;
typedef class nsIFrame nsIBox;
// IID for the nsIFrame interface
// bdf02423-88d6-41ca-818a-54d7b51328c3
// 4742c112-3577-4d90-aeb8-833729f14033
#define NS_IFRAME_IID \
{ 0xbdf02423, 0x88d6, 0x41ca, \
{ 0x81, 0x8a, 0x54, 0xd7, 0xb5, 0x13, 0x28, 0xc3 } }
{ 0x4742c112, 0x3577, 0x4d90, \
{ 0xae, 0xb8, 0x83, 0x37, 0x29, 0xf1, 0x40, 0x33 } }
/**
* Indication of how the frame can be split. This is used when doing runaround
@ -695,6 +696,15 @@ public:
NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) { return NS_OK; }
/**
* Displays the caret onto the given display list builder. The caret is
* painted on top of the rest of the display list items.
*
* @param aDirtyRect is the dirty rectangle that we're repainting.
*/
nsresult DisplayCaret(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists);
PRBool IsThemed() {
return IsThemed(GetStyleDisplay());