Bug 317375. Reorganize frame painting and mouse event targeting around frame display lists. Some review from dbaron, lots of testing help from Martijn, Julien and others

git-svn-id: svn://10.0.0.236/trunk@188202 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
roc+%cs.cmu.edu
2006-01-26 02:29:17 +00:00
parent 1c9ffd71d5
commit df8627e29b
174 changed files with 8094 additions and 7747 deletions

View File

@@ -140,6 +140,8 @@
#include "prenv.h"
#include "nsIAttribute.h"
#include "nsIGlobalHistory2.h"
#include "nsDisplayList.h"
#include "nsRegion.h"
#ifdef MOZ_REFLOW_PERF_DSP
#include "nsIRenderingContext.h"
@@ -1153,6 +1155,11 @@ public:
virtual nsresult ReconstructFrames(void);
virtual void Freeze();
virtual void Thaw();
NS_IMETHOD RenderOffscreen(nsRect aRect, PRBool aUntrusted,
PRBool aIgnoreViewportScrolling,
nscolor aBackgroundColor,
nsIRenderingContext** aRenderedContext);
#ifdef IBMBIDI
NS_IMETHOD SetCaretBidiLevel(PRUint8 aLevel);
@@ -1164,13 +1171,16 @@ public:
//nsIViewObserver interface
NS_IMETHOD Paint(nsIView *aView,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
nsIRenderingContext* aRenderingContext,
const nsRegion& aDirtyRegion);
NS_IMETHOD ComputeRepaintRegionForCopy(nsIView* aRootView,
nsIView* aMovingView,
nsPoint aDelta,
const nsRect& aCopyRect,
nsRegion* aRepaintRegion);
NS_IMETHOD HandleEvent(nsIView* aView,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus,
PRBool aForceHandle,
PRBool& aHandled);
nsEventStatus* aEventStatus);
NS_IMETHOD HandleDOMEventWithTarget(nsIContent* aTargetContent,
nsEvent* aEvent,
nsEventStatus* aStatus);
@@ -1315,6 +1325,7 @@ protected:
nsresult CloneStyleSet(nsStyleSet* aSet, nsStyleSet** aResult);
PRBool VerifyIncrementalReflow();
PRBool mInVerifyReflow;
void ShowEventTargetDebug();
#endif
/**
@@ -1409,8 +1420,8 @@ private:
PRBool InZombieDocument(nsIContent *aContent);
nsresult RetargetEventToParent(nsIView *aView, nsGUIEvent* aEvent,
nsEventStatus* aEventStatus, PRBool aForceHandle,
PRBool& aHandled, nsIContent *aZombieFocusedContent);
nsEventStatus* aEventStatus,
nsIContent *aZombieFocusedContent);
void FreeDynamicStack();
@@ -1419,6 +1430,10 @@ private:
void PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent);
void PopCurrentEventInfo();
nsresult HandleEventInternal(nsEvent* aEvent, nsIView* aView, PRUint32 aFlags, nsEventStatus *aStatus);
nsresult HandlePositionedEvent(nsIView* aView,
nsIFrame* aTargetFrame,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus);
//help funcs for resize events
void CreateResizeEventTimer();
@@ -5378,77 +5393,100 @@ PresShell::BidiStyleChangeReflow()
//nsIViewObserver
// Return TRUE if any clipping is to be done.
static PRBool ComputeClipRect(nsIFrame* aFrame, nsRect& aResult) {
const nsStyleDisplay* display = aFrame->GetStyleDisplay();
// 'clip' only applies to absolutely positioned elements, and is
// relative to the element's border edge. 'clip' applies to the entire
// element: border, padding, and content areas, and even scrollbars if
// there are any.
if (display->IsAbsolutelyPositioned() && (display->mClipFlags & NS_STYLE_CLIP_RECT)) {
nsSize size = aFrame->GetSize();
// Start with the 'auto' values and then factor in user specified values
nsRect clipRect(0, 0, size.width, size.height);
if (display->mClipFlags & NS_STYLE_CLIP_RECT) {
if (0 == (NS_STYLE_CLIP_TOP_AUTO & display->mClipFlags)) {
clipRect.y = display->mClip.y;
}
if (0 == (NS_STYLE_CLIP_LEFT_AUTO & display->mClipFlags)) {
clipRect.x = display->mClip.x;
}
if (0 == (NS_STYLE_CLIP_RIGHT_AUTO & display->mClipFlags)) {
clipRect.width = display->mClip.width;
}
if (0 == (NS_STYLE_CLIP_BOTTOM_AUTO & display->mClipFlags)) {
clipRect.height = display->mClip.height;
}
}
aResult = clipRect;
return PR_TRUE;
}
return PR_FALSE;
}
// If the element is absolutely positioned and has a specified clip rect
// then it pushes the current rendering context and sets the clip rect.
// Returns PR_TRUE if the clip rect is set and PR_FALSE otherwise
static PRBool
SetClipRect(nsIRenderingContext& aRenderingContext, nsIFrame* aFrame)
NS_IMETHODIMP
PresShell::ComputeRepaintRegionForCopy(nsIView* aRootView,
nsIView* aMovingView,
nsPoint aDelta,
const nsRect& aCopyRect,
nsRegion* aRepaintRegion)
{
nsRect clipRect;
if (ComputeClipRect(aFrame, clipRect)) {
// Set updated clip-rect into the rendering context
aRenderingContext.PushState();
aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect);
return PR_TRUE;
}
return PR_FALSE;
}
static PRBool
InClipRect(nsIFrame* aFrame, const nsPoint& aEventPoint)
{
nsRect clipRect;
if (ComputeClipRect(aFrame, clipRect)) {
return clipRect.Contains(aEventPoint);
} else {
return PR_TRUE;
}
return nsLayoutUtils::ComputeRepaintRegionForCopy(
NS_STATIC_CAST(nsIFrame*, aRootView->GetClientData()),
NS_STATIC_CAST(nsIFrame*, aMovingView->GetClientData()),
aDelta, aCopyRect, aRepaintRegion);
}
NS_IMETHODIMP
PresShell::Paint(nsIView *aView,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
PresShell::RenderOffscreen(nsRect aRect, PRBool aUntrusted,
PRBool aIgnoreViewportScrolling,
nscolor aBackgroundColor,
nsIRenderingContext** aRenderedContext)
{
nsIView* rootView;
mViewManager->GetRootView(rootView);
NS_ASSERTION(rootView, "No root view?");
nsIWidget* rootWidget = rootView->GetWidget();
NS_ASSERTION(rootWidget, "No root widget?");
*aRenderedContext = nsnull;
NS_ASSERTION(!aUntrusted, "We don't support untrusted yet");
if (aUntrusted)
return NS_ERROR_NOT_IMPLEMENTED;
nsCOMPtr<nsIRenderingContext> tmpContext;
mPresContext->DeviceContext()->CreateRenderingContext(rootWidget,
*getter_AddRefs(tmpContext));
if (!tmpContext)
return NS_ERROR_FAILURE;
nsRect bounds(nsPoint(0, 0), aRect.Size());
bounds.ScaleRoundOut(mPresContext->TwipsToPixels());
nsIDrawingSurface* surface;
nsresult rv
= tmpContext->CreateDrawingSurface(bounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS,
surface);
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
nsCOMPtr<nsIRenderingContext> localcx;
rv = nsLayoutUtils::CreateOffscreenContext(mPresContext->DeviceContext(),
surface, aRect, getter_AddRefs(localcx));
if (NS_FAILED(rv)) {
tmpContext->DestroyDrawingSurface(surface);
return NS_ERROR_FAILURE;
}
// clipping and translation is set by CreateOffscreenContext
localcx->SetColor(aBackgroundColor);
localcx->FillRect(aRect);
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
if (!rootFrame) {
localcx.swap(*aRenderedContext);
return NS_OK;
}
nsDisplayListBuilder builder(rootFrame, PR_FALSE);
nsDisplayList list;
nsIScrollableView* scrollingView = nsnull;
mViewManager->GetRootScrollableView(&scrollingView);
nsRect r = aRect;
if (aIgnoreViewportScrolling && scrollingView) {
nscoord x, y;
scrollingView->GetScrollPosition(x, y);
localcx->Translate(x, y);
r.MoveBy(-x, -y);
builder.SetIgnoreScrollFrame(GetRootScrollFrame(rootFrame));
}
rv = rootFrame->BuildDisplayListForStackingContext(&builder, r, &list);
NS_ENSURE_SUCCESS(rv, rv);
nsRegion region(r);
list.OptimizeVisibility(&builder, &region);
list.Paint(&builder, localcx, r);
// Flush the list so we don't trigger the IsEmpty-on-destruction assertion
list.DeleteAll();
localcx.swap(*aRenderedContext);
return NS_OK;
}
NS_IMETHODIMP
PresShell::Paint(nsIView* aView,
nsIRenderingContext* aRenderingContext,
const nsRegion& aDirtyRegion)
{
nsIFrame* frame;
nsresult rv = NS_OK;
@@ -5467,31 +5505,19 @@ PresShell::Paint(nsIView *aView,
if (mCaret)
mCaret->EraseCaret();
// If the frame is absolutely positioned, then the 'clip' property
// applies
PRBool setClipRect = SetClipRect(aRenderingContext, frame);
rv = frame->Paint(mPresContext, aRenderingContext, aDirtyRect,
NS_FRAME_PAINT_LAYER_BACKGROUND);
rv = frame->Paint(mPresContext, aRenderingContext, aDirtyRect,
NS_FRAME_PAINT_LAYER_FLOATS);
rv = frame->Paint(mPresContext, aRenderingContext, aDirtyRect,
NS_FRAME_PAINT_LAYER_FOREGROUND);
if (setClipRect)
aRenderingContext.PopState();
nsLayoutUtils::PaintFrame(aRenderingContext, frame, aDirtyRegion);
#ifdef NS_DEBUG
// Draw a border around the frame
if (nsIFrameDebug::GetShowFrameBorders()) {
nsRect r = frame->GetRect();
aRenderingContext.SetColor(NS_RGB(0,0,255));
aRenderingContext.DrawRect(0, 0, r.width, r.height);
aRenderingContext->SetColor(NS_RGB(0,0,255));
aRenderingContext->DrawRect(0, 0, r.width, r.height);
}
// Draw a border around the current event target
if ((nsIFrameDebug::GetShowEventTargetFrameBorder()) && (aView == mCurrentTargetView)) {
aRenderingContext.SetColor(NS_RGB(128,0,128));
aRenderingContext.DrawRect(mCurrentTargetRect.x, mCurrentTargetRect.y, mCurrentTargetRect.width, mCurrentTargetRect.height);
aRenderingContext->SetColor(NS_RGB(128,0,128));
aRenderingContext->DrawRect(mCurrentTargetRect.x, mCurrentTargetRect.y, mCurrentTargetRect.width, mCurrentTargetRect.height);
}
#endif
}
@@ -5579,8 +5605,6 @@ PRBool PresShell::InZombieDocument(nsIContent *aContent)
nsresult PresShell::RetargetEventToParent(nsIView *aView,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus,
PRBool aForceHandle,
PRBool& aHandled,
nsIContent* aZombieFocusedContent)
{
// Send this events straight up to the parent pres shell.
@@ -5645,20 +5669,15 @@ nsresult PresShell::RetargetEventToParent(nsIView *aView,
PopCurrentEventInfo();
return parentViewObserver->HandleEvent(aView, aEvent,
aEventStatus,
aForceHandle,
aHandled);
aEventStatus);
}
NS_IMETHODIMP
PresShell::HandleEvent(nsIView *aView,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus,
PRBool aForceHandle,
PRBool& aHandled)
nsEventStatus* aEventStatus)
{
NS_ASSERTION(aView, "null view");
aHandled = PR_TRUE;
if (mIsDestroying || mIsReflowing || mChangeNestCount) {
return NS_OK;
@@ -5689,7 +5708,6 @@ PresShell::HandleEvent(nsIView *aView,
nsIView *view;
vm->GetRootView(view);
if (view == aView) {
aHandled = PR_TRUE;
*aEventStatus = nsEventStatus_eConsumeDoDefault;
mPresContext->SysColorChanged();
return NS_OK;
@@ -5697,13 +5715,42 @@ PresShell::HandleEvent(nsIView *aView,
}
return NS_OK;
}
nsIFrame* frame = NS_STATIC_CAST(nsIFrame*, aView->GetClientData());
PRBool dispatchUsingCoordinates =
!NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_EVENT(aEvent) &&
aEvent->message != NS_CONTEXTMENU_KEY && !NS_IS_FOCUS_EVENT(aEvent);
nsIFrame* targetFrame;
if (frame && dispatchUsingCoordinates) {
nsPoint eventPoint
= nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
targetFrame = nsLayoutUtils::GetFrameForPoint(frame, eventPoint);
if (targetFrame) {
PresShell* shell =
NS_STATIC_CAST(PresShell*, targetFrame->GetPresContext()->PresShell());
if (shell != this) {
// handle the event in the correct shell.
nsIView* subshellRootView;
shell->GetViewManager()->GetRootView(subshellRootView);
// We pass the subshell's root view as the view to start from. This is
// the only correct alternative; if the event was captured then it
// must have been captured by us or some ancestor shell and we
// now ask the subshell to dispatch it normally.
return shell->HandlePositionedEvent(subshellRootView, targetFrame,
aEvent, aEventStatus);
}
}
if (!targetFrame) {
targetFrame = frame;
}
return HandlePositionedEvent(aView, targetFrame, aEvent, aEventStatus);
}
// if this event has no frame, we need to retarget it at a parent
// view that has a frame.
if (!frame &&
((NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_EVENT(aEvent)))) {
if (!frame && (NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_EVENT(aEvent))) {
nsIView* targetView = aView;
while (targetView && !targetView->GetClientData()) {
targetView = targetView->GetParent();
@@ -5721,17 +5768,14 @@ PresShell::HandleEvent(nsIView *aView,
PushCurrentEventInfo(nsnull, nsnull);
// key and IME events go to the focused frame
nsCOMPtr<nsIEventStateManager> manager;
if ((NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_EVENT(aEvent) ||
aEvent->message == NS_CONTEXTMENU_KEY)) {
nsIEventStateManager *esm = mPresContext->EventStateManager();
nsIEventStateManager *esm = mPresContext->EventStateManager();
if (NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_EVENT(aEvent) ||
aEvent->message == NS_CONTEXTMENU_KEY) {
esm->GetFocusedFrame(&mCurrentEventFrame);
if (mCurrentEventFrame) {
esm->GetFocusedContent(getter_AddRefs(mCurrentEventContent));
}
else {
} else {
#if defined(MOZ_X11) || defined(XP_WIN)
#if defined(MOZ_X11)
if (NS_IS_IME_EVENT(aEvent)) {
@@ -5776,118 +5820,22 @@ PresShell::HandleEvent(nsIView *aView,
mCurrentEventFrame = nsnull; // XXXldb Isn't it already?
}
if (mCurrentEventContent && InZombieDocument(mCurrentEventContent)) {
return RetargetEventToParent(aView, aEvent, aEventStatus, aForceHandle,
aHandled, mCurrentEventContent);
return RetargetEventToParent(aView, aEvent, aEventStatus,
mCurrentEventContent);
}
}
else if (!InClipRect(frame,
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame))) {
// we only check for the clip rect on this frame ... all frames with clip
// have views so any viewless children of this frame cannot have clip.
// Furthermore if the event is not in the clip for this frame, then none
// of the children can get it either.
if (aForceHandle) {
mCurrentEventFrame = frame;
}
else {
mCurrentEventFrame = nsnull;
}
aHandled = PR_FALSE;
rv = NS_OK;
} else {
rv = NS_OK;
nsPoint eventPoint;
eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
mCurrentEventFrame = frame->GetFrameForPoint(eventPoint,
NS_FRAME_PAINT_LAYER_FOREGROUND);
if (!mCurrentEventFrame) {
mCurrentEventFrame = frame->GetFrameForPoint(eventPoint,
NS_FRAME_PAINT_LAYER_FLOATS);
if (!mCurrentEventFrame) {
mCurrentEventFrame = frame->GetFrameForPoint(eventPoint,
NS_FRAME_PAINT_LAYER_BACKGROUND);
if (!mCurrentEventFrame) {
if (aForceHandle) {
mCurrentEventFrame = frame;
}
else {
mCurrentEventFrame = nsnull;
}
aHandled = PR_FALSE;
}
}
}
if (mCurrentEventFrame) {
nsCOMPtr<nsIContent> targetElement;
mCurrentEventFrame->GetContentForEvent(mPresContext, aEvent,
getter_AddRefs(targetElement));
// If there is no content for this frame, target it anyway. Some
// frames can be targeted but do not have content, particularly
// windows with scrolling off.
if (targetElement) {
// Bug 103055, bug 185889: mouse events apply to *elements*, not all
// nodes. Thus we get the nearest element parent here.
// XXX we leave the frame the same even if we find an element
// parent, so that the text frame will receive the event (selection
// and friends are the ones who care about that anyway)
//
// We use weak pointers because during this tight loop, the node
// will *not* go away. And this happens on every mousemove.
while (targetElement &&
!targetElement->IsContentOfType(nsIContent::eELEMENT)) {
targetElement = targetElement->GetParent();
}
// If we found an element, target it. Otherwise, target *nothing*.
if (!targetElement) {
mCurrentEventContent = nsnull;
mCurrentEventFrame = nsnull;
} else if (targetElement != mCurrentEventContent) {
mCurrentEventContent = targetElement;
}
}
}
mCurrentEventFrame = frame;
}
if (GetCurrentEventFrame()) {
rv = HandleEventInternal(aEvent, aView,
NS_EVENT_FLAG_INIT, aEventStatus);
}
#ifdef NS_DEBUG
if ((nsIFrameDebug::GetShowEventTargetFrameBorder()) &&
(GetCurrentEventFrame())) {
nsIView *oldView = mCurrentTargetView;
nsPoint offset(0,0);
nsRect oldTargetRect(mCurrentTargetRect);
mCurrentTargetRect = mCurrentEventFrame->GetRect();
mCurrentTargetView = mCurrentEventFrame->GetView();
if (!mCurrentTargetView ) {
mCurrentEventFrame->GetOffsetFromView(offset, &mCurrentTargetView);
}
if (mCurrentTargetView) {
mCurrentTargetRect.x = offset.x;
mCurrentTargetRect.y = offset.y;
// use aView or mCurrentTargetView??
if ((mCurrentTargetRect != oldTargetRect) ||
(mCurrentTargetView != oldView)) {
nsIViewManager* vm = GetViewManager();
if (vm) {
vm->UpdateView(mCurrentTargetView,mCurrentTargetRect,0);
if (oldView)
vm->UpdateView(oldView,oldTargetRect,0);
}
}
}
}
ShowEventTargetDebug();
#endif
PopCurrentEventInfo();
}
else {
} else {
// Focus events need to be dispatched even if no frame was found, since
// we don't want the focus controller to be out of sync.
@@ -5899,16 +5847,103 @@ PresShell::HandleEvent(nsIView *aView,
else if (NS_IS_KEY_EVENT(aEvent)) {
// Keypress events in new blank tabs should not be completely thrown away.
// Retarget them -- the parent chrome shell might make use of them.
return RetargetEventToParent(aView, aEvent, aEventStatus, aForceHandle,
aHandled, mCurrentEventContent);
return RetargetEventToParent(aView, aEvent, aEventStatus,
mCurrentEventContent);
}
aHandled = PR_FALSE;
}
return rv;
}
#ifdef NS_DEBUG
void
PresShell::ShowEventTargetDebug()
{
if ((nsIFrameDebug::GetShowEventTargetFrameBorder()) &&
(GetCurrentEventFrame())) {
nsIView *oldView = mCurrentTargetView;
nsPoint offset(0,0);
nsRect oldTargetRect(mCurrentTargetRect);
mCurrentTargetRect = mCurrentEventFrame->GetRect();
mCurrentTargetView = mCurrentEventFrame->GetView();
if (!mCurrentTargetView ) {
mCurrentEventFrame->GetOffsetFromView(offset, &mCurrentTargetView);
}
if (mCurrentTargetView) {
mCurrentTargetRect.x = offset.x;
mCurrentTargetRect.y = offset.y;
// use aView or mCurrentTargetView??
if ((mCurrentTargetRect != oldTargetRect) ||
(mCurrentTargetView != oldView)) {
nsIViewManager* vm = GetViewManager();
if (vm) {
vm->UpdateView(mCurrentTargetView,mCurrentTargetRect,0);
if (oldView)
vm->UpdateView(oldView,oldTargetRect,0);
}
}
}
}
}
#endif
nsresult
PresShell::HandlePositionedEvent(nsIView* aView,
nsIFrame* aTargetFrame,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
nsresult rv = NS_OK;
PushCurrentEventInfo(nsnull, nsnull);
mCurrentEventFrame = aTargetFrame;
if (mCurrentEventFrame) {
nsCOMPtr<nsIContent> targetElement;
mCurrentEventFrame->GetContentForEvent(mPresContext, aEvent,
getter_AddRefs(targetElement));
// If there is no content for this frame, target it anyway. Some
// frames can be targeted but do not have content, particularly
// windows with scrolling off.
if (targetElement) {
// Bug 103055, bug 185889: mouse events apply to *elements*, not all
// nodes. Thus we get the nearest element parent here.
// XXX we leave the frame the same even if we find an element
// parent, so that the text frame will receive the event (selection
// and friends are the ones who care about that anyway)
//
// We use weak pointers because during this tight loop, the node
// will *not* go away. And this happens on every mousemove.
while (targetElement &&
!targetElement->IsContentOfType(nsIContent::eELEMENT)) {
targetElement = targetElement->GetParent();
}
// If we found an element, target it. Otherwise, target *nothing*.
if (!targetElement) {
mCurrentEventContent = nsnull;
mCurrentEventFrame = nsnull;
} else if (targetElement != mCurrentEventContent) {
mCurrentEventContent = targetElement;
}
}
}
if (GetCurrentEventFrame()) {
rv = HandleEventInternal(aEvent, aView,
NS_EVENT_FLAG_INIT, aEventStatus);
}
#ifdef NS_DEBUG
ShowEventTargetDebug();
#endif
PopCurrentEventInfo();
return rv;
}
NS_IMETHODIMP
PresShell::HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame, nsIContent* aContent, PRUint32 aFlags, nsEventStatus* aStatus)
{