diff --git a/editor/libeditor/text/nsEditorEventListeners.cpp b/editor/libeditor/text/nsEditorEventListeners.cpp index e7e495eea24..38705f1d79d 100644 --- a/editor/libeditor/text/nsEditorEventListeners.cpp +++ b/editor/libeditor/text/nsEditorEventListeners.cpp @@ -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 presShell = do_QueryReferent(mPresShell); + if (presShell) + { + NS_ASSERTION(mOtherCaret, "Where'd my other caret go?"); + mCaret = presShell->SetCaret(mOtherCaret); + } + + mOtherCaret = mCaret = nsnull; } if (!mEditor) diff --git a/editor/libeditor/text/nsEditorEventListeners.h b/editor/libeditor/text/nsEditorEventListeners.h index 1786cea5819..225f618fe9c 100644 --- a/editor/libeditor/text/nsEditorEventListeners.h +++ b/editor/libeditor/text/nsEditorEventListeners.h @@ -235,8 +235,8 @@ protected: nsWeakPtr mPresShell; nsCOMPtr mCaret; + nsCOMPtr mOtherCaret; PRBool mCaretDrawn; - }; /** editor Implementation of the FocusListener interface diff --git a/gfx/src/thebes/nsThebesRenderingContext.cpp b/gfx/src/thebes/nsThebesRenderingContext.cpp index 3d2e1666434..b264b55fd94 100644 --- a/gfx/src/thebes/nsThebesRenderingContext.cpp +++ b/gfx/src/thebes/nsThebesRenderingContext.cpp @@ -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; } diff --git a/layout/base/nsCaret.cpp b/layout/base/nsCaret.cpp index a589f06c4cf..137f7f02247 100644 --- a/layout/base/nsCaret.cpp +++ b/layout/base/nsCaret.cpp @@ -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 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 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 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; -*/ - nsCOMPtrcontentNode = do_QueryInterface(focusNode); + nsCOMPtr 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 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 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 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 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 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 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 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 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 domSelection = do_QueryReferent(mDomSelectionWeak); - nsCOMPtr privateSelection = do_QueryInterface(domSelection); + mCaretRect = frameRect; + nsCOMPtr domSelection = do_QueryReferent(mDomSelectionWeak); + nsCOMPtr 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 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 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 diff --git a/layout/base/nsCaret.h b/layout/base/nsCaret.h index b432f1e98a9..5d09eda1d09 100644 --- a/layout/base/nsCaret.h +++ b/layout/base/nsCaret.h @@ -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 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
in text control) + nsRect mCaretRect; // the last caret rect, in the coodinates of the last frame. + + nsCOMPtr 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
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 mBidiKeyboard; // Bidi keyboard object to set and query keyboard language diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 2fecc2d5d1f..e6498e2f685 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -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 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 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) { diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index a881b1efde8..b77cf63fba2 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -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 @@ -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 mBoundingSelection; + nsTArray 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 mCaret; +}; + /** * The standard display item to paint the CSS borders of a frame. */ diff --git a/layout/base/nsICaret.h b/layout/base/nsICaret.h index af7d90dcaae..70270080534 100644 --- a/layout/base/nsICaret.h +++ b/layout/base/nsICaret.h @@ -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) diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index a4b7ad59ace..0588822a216 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -96,9 +96,8 @@ class nsISelection; template 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 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. diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index e5c3469d430..5ec50a569a7 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -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); +} diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 5d100e88f79..0a703289853 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -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__ diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index ee6429c13da..167cc415aaf 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -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 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 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; } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 44090df1bb3..d2352001567 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -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); diff --git a/layout/generic/nsFrameFrame.cpp b/layout/generic/nsFrameFrame.cpp index ee994939f95..6d6efbb24a1 100644 --- a/layout/generic/nsFrameFrame.cpp +++ b/layout/generic/nsFrameFrame.cpp @@ -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 diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 940db0f4cbb..4595b8fa60b 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -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());