1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Patrick C. Beard <beard@netscape.com>
24 * Kevin McCluskey <kmcclusk@netscape.com>
25 * Robert O'Callahan <roc+@cs.cmu.edu>
26 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
45 #include "nsAutoPtr.h"
46 #include "nsViewManager.h"
47 #include "nsIRenderingContext.h"
48 #include "nsIDeviceContext.h"
49 #include "nsGfxCIID.h"
50 #include "nsIScrollableView.h"
52 #include "nsISupportsArray.h"
54 #include "nsIServiceManager.h"
55 #include "nsGUIEvent.h"
56 #include "nsIPrefBranch.h"
57 #include "nsIPrefService.h"
60 #include "nsScrollPortView.h"
61 #include "nsHashtable.h"
62 #include "nsCOMArray.h"
63 #include "nsThreadUtils.h"
64 #include "nsContentUtils.h"
65 #include "gfxContext.h"
66 #define NS_STATIC_FOCUS_SUPPRESSOR
67 #include "nsIFocusEventSuppressor.h"
69 static NS_DEFINE_IID(kBlenderCID
, NS_BLENDER_CID
);
70 static NS_DEFINE_IID(kRegionCID
, NS_REGION_CID
);
71 static NS_DEFINE_IID(kRenderingContextCID
, NS_RENDERING_CONTEXT_CID
);
76 DeCOMify newly private methods
81 A note about platform assumptions:
83 We assume all native widgets are opaque.
85 We assume that a widget is z-ordered on top of its parent.
87 We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
88 we ask for a specific z-order, we don't assume that widget z-ordering actually works.
91 #define NSCOORD_NONE PR_INT32_MIN
93 #ifdef NS_VM_PERF_METRICS
94 #include "nsITimeRecorder.h"
98 #define DEBUG_FOCUS_SUPPRESSION
101 //-------------- Begin Invalidate Event Definition ------------------------
103 class nsInvalidateEvent
: public nsViewManagerEvent
{
105 nsInvalidateEvent(nsViewManager
*vm
) : nsViewManagerEvent(vm
) {}
109 mViewManager
->ProcessInvalidateEvent();
114 //-------------- End Invalidate Event Definition ---------------------------
116 static PRBool
IsViewVisible(nsView
*aView
)
118 for (nsIView
*view
= aView
; view
; view
= view
->GetParent()) {
119 // We don't check widget visibility here because in the future (with
120 // the better approach to this that's in attachment 160801 on bug
121 // 227361), callers of the equivalent to this function should be able
122 // to rely on being notified when the result of this function changes.
123 if (view
->GetVisibility() == nsViewVisibility_kHide
)
126 // Find out if the root view is visible by asking the view observer
127 // (this won't be needed anymore if we link view trees across chrome /
128 // content boundaries in DocumentViewerImpl::MakeWindow).
129 nsIViewObserver
* vo
= aView
->GetViewManager()->GetViewObserver();
130 return vo
&& vo
->IsVisible();
134 nsViewManager::PostInvalidateEvent()
136 NS_ASSERTION(IsRootVM(), "Caller screwed up");
138 if (!mInvalidateEvent
.IsPending()) {
139 nsRefPtr
<nsViewManagerEvent
> ev
= new nsInvalidateEvent(this);
140 if (NS_FAILED(NS_DispatchToCurrentThread(ev
))) {
141 NS_WARNING("failed to dispatch nsInvalidateEvent");
143 mInvalidateEvent
= ev
;
148 #undef DEBUG_MOUSE_LOCATION
150 PRInt32
nsViewManager::mVMCount
= 0;
151 nsIRenderingContext
* nsViewManager::gCleanupContext
= nsnull
;
153 // Weakly held references to all of the view managers
154 nsVoidArray
* nsViewManager::gViewManagers
= nsnull
;
155 PRUint32
nsViewManager::gLastUserEventTime
= 0;
157 nsViewManager::nsViewManager()
158 : mMouseLocation(NSCOORD_NONE
, NSCOORD_NONE
)
159 , mDelayedResize(NSCOORD_NONE
, NSCOORD_NONE
)
160 , mRootViewManager(this)
162 if (gViewManagers
== nsnull
) {
163 NS_ASSERTION(mVMCount
== 0, "View Manager count is incorrect");
164 // Create an array to hold a list of view managers
165 gViewManagers
= new nsVoidArray
;
168 if (gCleanupContext
== nsnull
) {
169 /* XXX: This should use a device to create a matching |nsIRenderingContext| object */
170 CallCreateInstance(kRenderingContextCID
, &gCleanupContext
);
171 NS_ASSERTION(gCleanupContext
,
172 "Wasn't able to create a graphics context for cleanup");
175 gViewManagers
->AppendElement(this);
177 if (++mVMCount
== 1) {
178 NS_AddFocusSuppressorCallback(&nsViewManager::SuppressFocusEvents
);
180 // NOTE: we use a zeroing operator new, so all data members are
181 // assumed to be cleared here.
182 mDefaultBackgroundColor
= NS_RGBA(0, 0, 0, 0);
183 mHasPendingUpdates
= PR_FALSE
;
184 mRecursiveRefreshPending
= PR_FALSE
;
185 mUpdateBatchFlags
= 0;
188 nsViewManager::~nsViewManager()
191 // Destroy any remaining views
192 mRootView
->Destroy();
196 // Make sure to revoke pending events for all viewmanagers, since some events
197 // are posted by a non-root viewmanager.
198 mInvalidateEvent
.Revoke();
199 mSynthMouseMoveEvent
.Revoke();
202 // We have a strong ref to mRootViewManager
203 NS_RELEASE(mRootViewManager
);
206 mRootScrollable
= nsnull
;
208 NS_ASSERTION((mVMCount
> 0), "underflow of viewmanagers");
214 gViewManagers
->RemoveElement(this);
215 NS_ASSERTION(removed
, "Viewmanager instance not was not in the global list of viewmanagers");
218 // There aren't any more view managers so
219 // release the global array of view managers
221 NS_ASSERTION(gViewManagers
!= nsnull
, "About to delete null gViewManagers");
222 delete gViewManagers
;
223 gViewManagers
= nsnull
;
225 // Cleanup all of the offscreen drawing surfaces if the last view manager
226 // has been destroyed and there is something to cleanup
228 // Note: A global rendering context is needed because it is not possible
229 // to create a nsIRenderingContext during the shutdown of XPCOM. The last
230 // viewmanager is typically destroyed during XPCOM shutdown.
231 NS_IF_RELEASE(gCleanupContext
);
238 NS_IMPL_ISUPPORTS1(nsViewManager
, nsIViewManager
)
241 nsViewManager::CreateRegion(nsIRegion
* *result
)
245 if (!mRegionFactory
) {
246 mRegionFactory
= do_GetClassObject(kRegionCID
, &rv
);
253 nsIRegion
* region
= nsnull
;
254 rv
= CallCreateInstance(mRegionFactory
.get(), ®ion
);
255 if (NS_SUCCEEDED(rv
)) {
262 // We don't hold a reference to the presentation context because it
263 // holds a reference to us.
264 NS_IMETHODIMP
nsViewManager::Init(nsIDeviceContext
* aContext
)
266 NS_PRECONDITION(nsnull
!= aContext
, "null ptr");
268 if (nsnull
== aContext
) {
269 return NS_ERROR_NULL_POINTER
;
271 if (nsnull
!= mContext
) {
272 return NS_ERROR_ALREADY_INITIALIZED
;
276 mRefreshEnabled
= PR_TRUE
;
278 mMouseGrabber
= nsnull
;
283 NS_IMETHODIMP_(nsIView
*)
284 nsViewManager::CreateView(const nsRect
& aBounds
,
285 const nsIView
* aParent
,
286 nsViewVisibility aVisibilityFlag
)
288 nsView
*v
= new nsView(this, aVisibilityFlag
);
290 v
->SetPosition(aBounds
.x
, aBounds
.y
);
291 nsRect
dim(0, 0, aBounds
.width
, aBounds
.height
);
292 v
->SetDimensions(dim
, PR_FALSE
);
293 v
->SetParent(static_cast<nsView
*>(const_cast<nsIView
*>(aParent
)));
298 NS_IMETHODIMP_(nsIScrollableView
*)
299 nsViewManager::CreateScrollableView(const nsRect
& aBounds
,
300 const nsIView
* aParent
)
302 nsScrollPortView
*v
= new nsScrollPortView(this);
304 v
->SetPosition(aBounds
.x
, aBounds
.y
);
305 nsRect
dim(0, 0, aBounds
.width
, aBounds
.height
);
306 v
->SetDimensions(dim
, PR_FALSE
);
307 v
->SetParent(static_cast<nsView
*>(const_cast<nsIView
*>(aParent
)));
312 NS_IMETHODIMP
nsViewManager::GetRootView(nsIView
*&aView
)
318 NS_IMETHODIMP
nsViewManager::SetRootView(nsIView
*aView
)
320 nsView
* view
= static_cast<nsView
*>(aView
);
322 NS_PRECONDITION(!view
|| view
->GetViewManager() == this,
323 "Unexpected viewmanager on root view");
325 // Do NOT destroy the current root view. It's the caller's responsibility
330 nsView
* parent
= mRootView
->GetParent();
332 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
333 // no need to set mRootViewManager ourselves here.
334 parent
->InsertChild(mRootView
, nsnull
);
336 InvalidateHierarchy();
339 mRootView
->SetZIndex(PR_FALSE
, 0, PR_FALSE
);
341 // Else don't touch mRootViewManager
346 NS_IMETHODIMP
nsViewManager::GetWindowDimensions(nscoord
*aWidth
, nscoord
*aHeight
)
348 if (nsnull
!= mRootView
) {
349 if (mDelayedResize
== nsSize(NSCOORD_NONE
, NSCOORD_NONE
)) {
351 mRootView
->GetDimensions(dim
);
353 *aHeight
= dim
.height
;
355 *aWidth
= mDelayedResize
.width
;
356 *aHeight
= mDelayedResize
.height
;
367 NS_IMETHODIMP
nsViewManager::SetWindowDimensions(nscoord aWidth
, nscoord aHeight
)
370 if (IsViewVisible(mRootView
)) {
371 mDelayedResize
.SizeTo(NSCOORD_NONE
, NSCOORD_NONE
);
372 DoSetWindowDimensions(aWidth
, aHeight
);
374 mDelayedResize
.SizeTo(aWidth
, aHeight
);
381 static void ConvertNativeRegionToAppRegion(nsIRegion
* aIn
, nsRegion
* aOut
,
382 nsIDeviceContext
* context
)
384 nsRegionRectSet
* rects
= nsnull
;
385 aIn
->GetRects(&rects
);
389 PRInt32 p2a
= context
->AppUnitsPerDevPixel();
392 for (i
= 0; i
< rects
->mNumRects
; i
++) {
393 const nsRegionRect
& inR
= rects
->mRects
[i
];
395 outR
.x
= NSIntPixelsToAppUnits(inR
.x
, p2a
);
396 outR
.y
= NSIntPixelsToAppUnits(inR
.y
, p2a
);
397 outR
.width
= NSIntPixelsToAppUnits(inR
.width
, p2a
);
398 outR
.height
= NSIntPixelsToAppUnits(inR
.height
, p2a
);
399 aOut
->Or(*aOut
, outR
);
402 aIn
->FreeRects(rects
);
405 static nsView
* GetDisplayRootFor(nsView
* aView
)
407 nsView
*displayRoot
= aView
;
409 nsView
*displayParent
= displayRoot
->GetParent();
413 if (displayRoot
->GetFloating() && !displayParent
->GetFloating())
415 displayRoot
= displayParent
;
420 aRegion is given in device coordinates!!
422 void nsViewManager::Refresh(nsView
*aView
, nsIRenderingContext
*aContext
,
423 nsIRegion
*aRegion
, PRUint32 aUpdateFlags
)
425 NS_ASSERTION(aRegion
!= nsnull
, "Null aRegion");
427 if (! IsRefreshEnabled())
431 aView
->GetDimensions(viewRect
);
432 nsPoint vtowoffset
= aView
->ViewToWidgetOffset();
434 // damageRegion is the damaged area, in twips, relative to the view origin
435 nsRegion damageRegion
;
436 // convert pixels-relative-to-widget-origin to twips-relative-to-widget-origin
437 ConvertNativeRegionToAppRegion(aRegion
, &damageRegion
, mContext
);
438 // move it from widget coordinates into view coordinates
439 damageRegion
.MoveBy(viewRect
.TopLeft() - vtowoffset
);
441 if (damageRegion
.IsEmpty()) {
443 nsRect damageRect
= damageRegion
.GetBounds();
444 printf("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
445 damageRect
.x
, damageRect
.y
, damageRect
.width
, damageRect
.height
,
446 viewRect
.x
, viewRect
.y
, viewRect
.width
, viewRect
.height
);
451 #ifdef NS_VM_PERF_METRICS
452 MOZ_TIMER_DEBUGLOG(("Reset nsViewManager::Refresh(region), this=%p\n", this));
453 MOZ_TIMER_RESET(mWatch
);
455 MOZ_TIMER_DEBUGLOG(("Start: nsViewManager::Refresh(region)\n"));
456 MOZ_TIMER_START(mWatch
);
459 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
461 RootViewManager()->mRecursiveRefreshPending
= PR_TRUE
;
466 nsAutoScriptBlocker scriptBlocker
;
467 SetPainting(PR_TRUE
);
469 nsCOMPtr
<nsIRenderingContext
> localcx
;
470 NS_ASSERTION(aView
->GetWidget(),
471 "Must have a widget to calculate coordinates correctly");
472 if (nsnull
== aContext
)
474 localcx
= CreateRenderingContext(*aView
);
476 //couldn't get rendering context. this is ok at init time atleast
477 if (nsnull
== localcx
) {
478 SetPainting(PR_FALSE
);
482 // plain assignment grabs another reference.
486 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
488 nsRefPtr
<gfxContext
> ctx
= localcx
->ThebesContext();
492 ctx
->Translate(gfxPoint(gfxFloat(vtowoffset
.x
) / p2a
,
493 gfxFloat(vtowoffset
.y
) / p2a
));
495 ctx
->Translate(gfxPoint(-gfxFloat(viewRect
.x
) / p2a
,
496 -gfxFloat(viewRect
.y
) / p2a
));
498 nsRegion opaqueRegion
;
499 AddCoveringWidgetsToOpaqueRegion(opaqueRegion
, mContext
, aView
);
500 damageRegion
.Sub(damageRegion
, opaqueRegion
);
502 RenderViews(aView
, *localcx
, damageRegion
);
506 SetPainting(PR_FALSE
);
509 if (RootViewManager()->mRecursiveRefreshPending
) {
510 // Unset this flag first, since if aUpdateFlags includes NS_VMREFRESH_IMMEDIATE
511 // we'll reenter this code from the UpdateAllViews call.
512 RootViewManager()->mRecursiveRefreshPending
= PR_FALSE
;
513 UpdateAllViews(aUpdateFlags
);
516 #ifdef NS_VM_PERF_METRICS
517 MOZ_TIMER_DEBUGLOG(("Stop: nsViewManager::Refresh(region), this=%p\n", this));
518 MOZ_TIMER_STOP(mWatch
);
519 MOZ_TIMER_LOG(("vm2 Paint time (this=%p): ", this));
520 MOZ_TIMER_PRINT(mWatch
);
525 // aRect is in app units and relative to the top-left of the aView->GetWidget()
526 void nsViewManager::DefaultRefresh(nsView
* aView
, nsIRenderingContext
*aContext
, const nsRect
* aRect
)
528 NS_PRECONDITION(aView
, "Must have a view to work with!");
529 nsIWidget
* widget
= aView
->GetNearestWidget(nsnull
);
533 nsCOMPtr
<nsIRenderingContext
> context
= aContext
;
535 context
= CreateRenderingContext(*aView
);
540 nscolor bgcolor
= mDefaultBackgroundColor
;
542 if (NS_GET_A(mDefaultBackgroundColor
) == 0) {
543 NS_WARNING("nsViewManager: Asked to paint a default background, but no default background color is set!");
547 context
->SetColor(bgcolor
);
548 context
->FillRect(*aRect
);
551 void nsViewManager::AddCoveringWidgetsToOpaqueRegion(nsRegion
&aRgn
, nsIDeviceContext
* aContext
,
553 NS_PRECONDITION(aRootView
, "Must have root view");
555 // We accumulate the bounds of widgets obscuring aRootView's widget into opaqueRgn.
556 // In OptimizeDisplayList, display list elements which lie behind obscuring native
557 // widgets are dropped.
558 // This shouldn't really be necessary, since the GFX/Widget toolkit should remove these
559 // covering widgets from the clip region passed into the paint command. But right now
560 // they only give us a paint rect and not a region, so we can't access that information.
561 // It's important to identifying areas that are covered by native widgets to avoid
562 // painting their views many times as we process invalidates from the root widget all the
563 // way down to the nested widgets.
565 // NB: we must NOT add widgets that correspond to floating views!
566 // We may be required to paint behind them
569 nsIWidget
* widget
= aRootView
->GetNearestWidget(nsnull
);
574 for (nsIWidget
* childWidget
= widget
->GetFirstChild();
576 childWidget
= childWidget
->GetNextSibling()) {
577 PRBool widgetVisible
;
578 childWidget
->IsVisible(widgetVisible
);
580 nsView
* view
= nsView::GetViewFor(childWidget
);
581 if (view
&& view
->GetVisibility() == nsViewVisibility_kShow
582 && !view
->GetFloating()) {
583 nsRect bounds
= view
->GetBounds();
584 if (bounds
.width
> 0 && bounds
.height
> 0) {
585 nsView
* viewParent
= view
->GetParent();
587 while (viewParent
&& viewParent
!= aRootView
) {
588 viewParent
->ConvertToParentCoords(&bounds
.x
, &bounds
.y
);
589 viewParent
= viewParent
->GetParent();
592 // maybe we couldn't get the view into the coordinate
593 // system of aRootView (maybe it's not a descendant
594 // view of aRootView?); if so, don't use it
596 aRgn
.Or(aRgn
, bounds
);
604 // aRC and aRegion are in view coordinates
605 void nsViewManager::RenderViews(nsView
*aView
, nsIRenderingContext
& aRC
,
606 const nsRegion
& aRegion
)
609 nsView
* displayRoot
= GetDisplayRootFor(aView
);
610 nsPoint offsetToRoot
= aView
->GetOffsetTo(displayRoot
);
611 nsRegion
damageRegion(aRegion
);
612 damageRegion
.MoveBy(offsetToRoot
);
615 aRC
.Translate(-offsetToRoot
.x
, -offsetToRoot
.y
);
616 mObserver
->Paint(displayRoot
, &aRC
, damageRegion
);
621 void nsViewManager::ProcessPendingUpdates(nsView
* aView
, PRBool aDoInvalidate
)
623 NS_ASSERTION(IsRootVM(), "Updates will be missed");
625 // Protect against a null-view.
630 if (aView
->HasWidget()) {
631 aView
->ResetWidgetBounds(PR_FALSE
, PR_FALSE
, PR_TRUE
);
634 // process pending updates in child view.
635 for (nsView
* childView
= aView
->GetFirstChild(); childView
;
636 childView
= childView
->GetNextSibling()) {
637 ProcessPendingUpdates(childView
, aDoInvalidate
);
640 if (aDoInvalidate
&& aView
->HasNonEmptyDirtyRegion()) {
641 // Push out updates after we've processed the children; ensures that
642 // damage is applied based on the final widget geometry
643 NS_ASSERTION(mRefreshEnabled
, "Cannot process pending updates with refresh disabled");
644 nsRegion
* dirtyRegion
= aView
->GetDirtyRegion();
646 UpdateWidgetArea(aView
, *dirtyRegion
, nsnull
);
647 dirtyRegion
->SetEmpty();
652 NS_IMETHODIMP
nsViewManager::Composite()
655 return RootViewManager()->Composite();
658 if (UpdateCount() > 0)
667 NS_IMETHODIMP
nsViewManager::UpdateView(nsIView
*aView
, PRUint32 aUpdateFlags
)
669 // Mark the entire view as damaged
670 nsView
* view
= static_cast<nsView
*>(aView
);
672 nsRect bounds
= view
->GetBounds();
673 view
->ConvertFromParentCoords(&bounds
.x
, &bounds
.y
);
674 return UpdateView(view
, bounds
, aUpdateFlags
);
677 // This method accumulates the intersectons of all dirty regions attached to
678 // descendants of aSourceView with the cliprect of aTargetView into the dirty
679 // region of aTargetView, after offseting said intersections by aOffset.
681 AccumulateIntersectionsIntoDirtyRegion(nsView
* aTargetView
,
683 const nsPoint
& aOffset
)
685 if (aSourceView
->HasNonEmptyDirtyRegion()) {
686 // In most cases, aSourceView is an ancestor of aTargetView, since most
687 // commonly we have dirty rects on the root view.
688 nsPoint offset
= aTargetView
->GetOffsetTo(aSourceView
);
689 nsRegion intersection
;
690 intersection
= *aSourceView
->GetDirtyRegion();
691 if (!intersection
.IsEmpty()) {
692 nsRegion
* targetRegion
= aTargetView
->GetDirtyRegion();
694 intersection
.MoveBy(-offset
+ aOffset
);
695 targetRegion
->Or(*targetRegion
, intersection
);
696 // Random simplification number...
697 targetRegion
->SimplifyOutward(20);
702 if (aSourceView
== aTargetView
) {
703 // No need to do this with kids of aTargetView
707 for (nsView
* kid
= aSourceView
->GetFirstChild();
709 kid
= kid
->GetNextSibling()) {
710 AccumulateIntersectionsIntoDirtyRegion(aTargetView
, kid
, aOffset
);
715 nsViewManager::WillBitBlit(nsView
* aView
, nsPoint aScrollAmount
)
718 RootViewManager()->WillBitBlit(aView
, aScrollAmount
);
722 NS_PRECONDITION(aView
, "Must have a view");
723 NS_PRECONDITION(aView
->HasWidget(), "View must have a widget");
727 // Since the view is actually moving the widget by -aScrollAmount, that's the
728 // offset we want to use when accumulating dirty rects.
729 AccumulateIntersectionsIntoDirtyRegion(aView
, GetRootView(), -aScrollAmount
);
733 // Invalidate all widgets which overlap the view, other than the view's own widgets.
735 nsViewManager::UpdateViewAfterScroll(nsView
*aView
, const nsRegion
& aUpdateRegion
)
737 NS_ASSERTION(RootViewManager()->mScrollCnt
> 0,
738 "Someone forgot to call WillBitBlit()");
739 // Look at the view's clipped rect. It may be that part of the view is clipped out
740 // in which case we don't need to worry about invalidating the clipped-out part.
741 nsRect damageRect
= aView
->GetDimensions();
742 if (damageRect
.IsEmpty()) {
743 // Don't forget to undo mScrollCnt!
744 --RootViewManager()->mScrollCnt
;
748 nsView
* displayRoot
= GetDisplayRootFor(aView
);
749 nsPoint offset
= aView
->GetOffsetTo(displayRoot
);
750 damageRect
.MoveBy(offset
);
752 UpdateWidgetArea(displayRoot
, nsRegion(damageRect
), aView
);
753 if (!aUpdateRegion
.IsEmpty()) {
754 // XXX We should update the region, not the bounds rect, but that requires
755 // a little more work. Fix this when we reshuffle this code.
756 nsRegion
update(aUpdateRegion
);
757 update
.MoveBy(offset
);
758 UpdateWidgetArea(displayRoot
, update
, nsnull
);
759 // FlushPendingInvalidates();
763 --RootViewManager()->mScrollCnt
;
767 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
768 * every widget child of aWidgetView, plus aWidgetView's own widget
769 * @param aIgnoreWidgetView if non-null, the aIgnoreWidgetView's widget and its
770 * children are not updated.
773 nsViewManager::UpdateWidgetArea(nsView
*aWidgetView
, const nsRegion
&aDamagedRegion
,
774 nsView
* aIgnoreWidgetView
)
776 if (!IsRefreshEnabled()) {
777 // accumulate this rectangle in the view's dirty region, so we can
779 nsRegion
* dirtyRegion
= aWidgetView
->GetDirtyRegion();
780 if (!dirtyRegion
) return;
782 dirtyRegion
->Or(*dirtyRegion
, aDamagedRegion
);
783 // Don't let dirtyRegion grow beyond 8 rects
784 dirtyRegion
->SimplifyOutward(8);
785 nsViewManager
* rootVM
= RootViewManager();
786 rootVM
->mHasPendingUpdates
= PR_TRUE
;
787 rootVM
->IncrementUpdateCount();
789 // this should only happen at the top level, and this result
790 // should not be consumed by top-level callers, so it doesn't
791 // really matter what we return
794 // If the bounds don't overlap at all, there's nothing to do
795 nsRegion intersection
;
796 intersection
.And(aWidgetView
->GetDimensions(), aDamagedRegion
);
797 if (intersection
.IsEmpty()) {
801 // If the widget is hidden, it don't cover nothing
802 if (nsViewVisibility_kHide
== aWidgetView
->GetVisibility()) {
804 // Assert if view is hidden but widget is visible
805 nsIWidget
* widget
= aWidgetView
->GetNearestWidget(nsnull
);
808 widget
->IsVisible(visible
);
809 NS_ASSERTION(!visible
, "View is hidden but widget is visible!");
815 if (aWidgetView
== aIgnoreWidgetView
) {
816 // the widget for aIgnoreWidgetView (and its children) should be treated as already updated.
820 nsIWidget
* widget
= aWidgetView
->GetNearestWidget(nsnull
);
822 // The root view or a scrolling view might not have a widget
823 // (for example, during printing). We get here when we scroll
824 // during printing to show selected options in a listbox, for example.
828 // Update all child widgets with the damage. In the process,
829 // accumulate the union of all the child widget areas, or at least
830 // some subset of that.
832 for (nsIWidget
* childWidget
= widget
->GetFirstChild();
834 childWidget
= childWidget
->GetNextSibling()) {
835 nsView
* view
= nsView::GetViewFor(childWidget
);
836 NS_ASSERTION(view
!= aWidgetView
, "will recur infinitely");
837 if (view
&& view
->GetVisibility() == nsViewVisibility_kShow
) {
838 // Don't mess with views that are in completely different view
840 if (view
->GetViewManager()->RootViewManager() == RootViewManager()) {
841 // get the damage region into 'view's coordinate system
842 nsRegion damage
= intersection
;
843 nsPoint offset
= view
->GetOffsetTo(aWidgetView
);
844 damage
.MoveBy(-offset
);
845 UpdateWidgetArea(view
, damage
, aIgnoreWidgetView
);
846 children
.Or(children
, view
->GetDimensions() + offset
);
847 children
.SimplifyInward(20);
853 leftOver
.Sub(intersection
, children
);
855 if (!leftOver
.IsEmpty()) {
856 NS_ASSERTION(IsRefreshEnabled(), "Can only get here with refresh enabled, I hope");
859 for (nsRegionRectIterator
iter(leftOver
); (r
= iter
.Next());) {
861 ViewToWidget(aWidgetView
, aWidgetView
, bounds
);
862 widget
->Invalidate(bounds
, PR_FALSE
);
867 NS_IMETHODIMP
nsViewManager::UpdateView(nsIView
*aView
, const nsRect
&aRect
, PRUint32 aUpdateFlags
)
869 NS_PRECONDITION(nsnull
!= aView
, "null view");
871 nsView
* view
= static_cast<nsView
*>(aView
);
873 nsRect
damagedRect(aRect
);
875 // If the rectangle is not visible then abort
876 // without invalidating. This is a performance
877 // enhancement since invalidating a native widget
879 // This also checks for silly request like damagedRect.width = 0 or damagedRect.height = 0
880 nsRectVisibility rectVisibility
;
881 GetRectVisibility(view
, damagedRect
, 0, &rectVisibility
);
882 if (rectVisibility
!= nsRectVisibility_kVisible
) {
886 // if this is a floating view, it isn't covered by any widgets other than
887 // its children. In that case we walk up to its parent widget and use
888 // that as the root to update from. This also means we update areas that
889 // may be outside the parent view(s), which is necessary for floats.
890 if (view
->GetFloating()) {
891 nsView
* widgetParent
= view
;
893 while (!widgetParent
->HasWidget()) {
894 widgetParent
->ConvertToParentCoords(&damagedRect
.x
, &damagedRect
.y
);
895 widgetParent
= widgetParent
->GetParent();
898 UpdateWidgetArea(widgetParent
, nsRegion(damagedRect
), nsnull
);
900 // Propagate the update to the root widget of the root view manager, since
901 // iframes, for example, can overlap each other and be translucent. So we
902 // have to possibly invalidate our rect in each of the widgets we have
904 damagedRect
.MoveBy(ComputeViewOffset(view
));
906 UpdateWidgetArea(RootViewManager()->GetRootView(), nsRegion(damagedRect
), nsnull
);
909 RootViewManager()->IncrementUpdateCount();
911 if (!IsRefreshEnabled()) {
915 // See if we should do an immediate refresh or wait
916 if (aUpdateFlags
& NS_VMREFRESH_IMMEDIATE
) {
923 NS_IMETHODIMP
nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags
)
925 if (RootViewManager() != this) {
926 return RootViewManager()->UpdateAllViews(aUpdateFlags
);
929 UpdateViews(mRootView
, aUpdateFlags
);
933 void nsViewManager::UpdateViews(nsView
*aView
, PRUint32 aUpdateFlags
)
936 UpdateView(aView
, aUpdateFlags
);
938 // update all children as well.
939 nsView
* childView
= aView
->GetFirstChild();
940 while (nsnull
!= childView
) {
941 UpdateViews(childView
, aUpdateFlags
);
942 childView
= childView
->GetNextSibling();
946 nsView
*nsViewManager::sCurrentlyFocusView
= nsnull
;
947 nsView
*nsViewManager::sViewFocusedBeforeSuppression
= nsnull
;
948 PRBool
nsViewManager::sFocusSuppressed
= PR_FALSE
;
950 void nsViewManager::SuppressFocusEvents(PRBool aSuppress
)
953 sFocusSuppressed
= PR_TRUE
;
954 SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView());
958 sFocusSuppressed
= PR_FALSE
;
959 if (GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression()) {
963 // We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS.
964 nsIWidget
*widget
= nsnull
;
965 nsEventStatus status
;
967 // Backup what is focused before we send the blur. If the
968 // blur causes a focus change, keep that new focus change,
969 // don't overwrite with the old "currently focused view".
970 nsIView
*currentFocusBeforeBlur
= GetCurrentlyFocusedView();
972 // Send NS_LOSTFOCUS to widget that was focused before
973 // focus/blur suppression.
974 if (GetViewFocusedBeforeSuppression()) {
975 widget
= GetViewFocusedBeforeSuppression()->GetWidget();
977 #ifdef DEBUG_FOCUS_SUPPRESSION
978 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_LOSTFOCUS\n");
980 nsGUIEvent
event(PR_TRUE
, NS_LOSTFOCUS
, widget
);
981 widget
->DispatchEvent(&event
, status
);
985 // Send NS_GOTFOCUS to the widget that we think should be focused.
986 if (GetCurrentlyFocusedView() &&
987 currentFocusBeforeBlur
== GetCurrentlyFocusedView())
989 widget
= GetCurrentlyFocusedView()->GetWidget();
991 #ifdef DEBUG_FOCUS_SUPPRESSION
992 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_GOTFOCUS\n");
994 nsGUIEvent
event(PR_TRUE
, NS_GOTFOCUS
, widget
);
995 widget
->DispatchEvent(&event
, status
);
1001 static void ConvertRectAppUnitsToIntPixels(nsRect
& aRect
, PRInt32 p2a
)
1003 aRect
.x
= NSAppUnitsToIntPixels(aRect
.x
, p2a
);
1004 aRect
.y
= NSAppUnitsToIntPixels(aRect
.y
, p2a
);
1005 aRect
.width
= NSAppUnitsToIntPixels(aRect
.width
, p2a
);
1006 aRect
.height
= NSAppUnitsToIntPixels(aRect
.height
, p2a
);
1009 NS_IMETHODIMP
nsViewManager::DispatchEvent(nsGUIEvent
*aEvent
, nsEventStatus
*aStatus
)
1011 *aStatus
= nsEventStatus_eIgnore
;
1013 switch(aEvent
->message
)
1017 nsView
* view
= nsView::GetViewFor(aEvent
->widget
);
1021 nscoord width
= ((nsSizeEvent
*)aEvent
)->windowSize
->width
;
1022 nscoord height
= ((nsSizeEvent
*)aEvent
)->windowSize
->height
;
1023 width
= ((nsSizeEvent
*)aEvent
)->mWinWidth
;
1024 height
= ((nsSizeEvent
*)aEvent
)->mWinHeight
;
1026 // The root view may not be set if this is the resize associated with
1029 if (view
== mRootView
)
1031 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1032 SetWindowDimensions(NSIntPixelsToAppUnits(width
, p2a
),
1033 NSIntPixelsToAppUnits(height
, p2a
));
1034 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1043 nsPaintEvent
*event
= static_cast<nsPaintEvent
*>(aEvent
);
1044 nsView
*view
= nsView::GetViewFor(aEvent
->widget
);
1046 if (!view
|| !mContext
)
1049 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1051 // The rect is in device units, and it's in the coordinate space of its
1052 // associated window.
1053 nsCOMPtr
<nsIRegion
> region
= event
->region
;
1055 if (NS_FAILED(CreateRegion(getter_AddRefs(region
))))
1058 const nsRect
& damrect
= *event
->rect
;
1059 region
->SetTo(damrect
.x
, damrect
.y
, damrect
.width
, damrect
.height
);
1062 if (region
->IsEmpty())
1066 if (IsRefreshEnabled()) {
1067 // If an ancestor widget was hidden and then shown, we could
1068 // have a delayed resize to handle.
1069 PRBool didResize
= PR_FALSE
;
1070 for (nsViewManager
*vm
= this; vm
;
1071 vm
= vm
->mRootView
->GetParent()
1072 ? vm
->mRootView
->GetParent()->GetViewManager()
1074 if (vm
->mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
) &&
1075 IsViewVisible(vm
->mRootView
)) {
1076 vm
->DoSetWindowDimensions(vm
->mDelayedResize
.width
,
1077 vm
->mDelayedResize
.height
);
1078 vm
->mDelayedResize
.SizeTo(NSCOORD_NONE
, NSCOORD_NONE
);
1081 vm
->UpdateView(vm
->mRootView
, NS_VMREFRESH_NO_SYNC
);
1082 didResize
= PR_TRUE
;
1084 // not sure if it's valid for us to claim that we
1085 // ignored this, but we're going to do so anyway, since
1086 // we didn't actually paint anything
1087 *aStatus
= nsEventStatus_eIgnore
;
1092 //NS_ASSERTION(IsViewVisible(view), "painting an invisible view");
1094 // Just notify our own view observer that we're about to paint
1095 // XXXbz do we need to notify other view observers for viewmanagers
1097 // Make sure to not send WillPaint notifications while scrolling
1098 nsRefPtr
<nsViewManager
> rootVM
= RootViewManager();
1100 nsIWidget
*widget
= mRootView
->GetWidget();
1101 PRBool transparentWindow
= PR_FALSE
;
1103 transparentWindow
= widget
->GetTransparencyMode() == eTransparencyTransparent
;
1105 if (rootVM
->mScrollCnt
== 0 && !transparentWindow
) {
1106 nsIViewObserver
* observer
= GetViewObserver();
1108 // Do an update view batch. Make sure not to do it DEFERRED,
1109 // since that would effectively delay any invalidates that are
1110 // triggered by the WillPaint notification (they'd happen when
1111 // the invalid event fires, which is later than the reflow
1112 // event would fire and could end up being after some timer
1113 // events, leading to frame dropping in DHTML). Note that the
1114 // observer may try to reenter this code from inside
1115 // WillPaint() by trying to do a synchronous paint, but since
1116 // refresh will be disabled it won't be able to do the paint.
1117 // We should really sort out the rules on our synch painting
1119 UpdateViewBatch
batch(this);
1120 observer
->WillPaint();
1121 batch
.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC
);
1123 // Get the view pointer again since the code above might have
1124 // destroyed it (bug 378273).
1125 view
= nsView::GetViewFor(aEvent
->widget
);
1128 // Make sure to sync up any widget geometry changes we
1129 // have pending before we paint.
1130 if (rootVM
->mHasPendingUpdates
) {
1131 rootVM
->ProcessPendingUpdates(mRootView
, PR_FALSE
);
1135 Refresh(view
, event
->renderingContext
, region
,
1136 NS_VMREFRESH_DOUBLE_BUFFER
);
1140 // since we got an NS_PAINT event, we need to
1141 // draw something so we don't get blank areas.
1143 region
->GetBoundingBox(&damRect
.x
, &damRect
.y
, &damRect
.width
, &damRect
.height
);
1144 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1145 damRect
.ScaleRoundOut(float(p2a
));
1146 DefaultRefresh(view
, event
->renderingContext
, &damRect
);
1148 // Clients like the editor can trigger multiple
1149 // reflows during what the user perceives as a single
1150 // edit operation, so it disables view manager
1151 // refreshing until the edit operation is complete
1152 // so that users don't see the intermediate steps.
1154 // Unfortunately some of these reflows can trigger
1155 // nsScrollPortView and nsScrollingView Scroll() calls
1156 // which in most cases force an immediate BitBlt and
1157 // synchronous paint to happen even if the view manager's
1158 // refresh is disabled. (Bug 97674)
1160 // Calling UpdateView() here, is necessary to add
1161 // the exposed region specified in the synchronous paint
1162 // event to the view's damaged region so that it gets
1163 // painted properly when refresh is enabled.
1165 // Note that calling UpdateView() here was deemed
1166 // to have the least impact on performance, since the
1167 // other alternative was to make Scroll() post an
1168 // async paint event for the *entire* ScrollPort or
1169 // ScrollingView's viewable area. (See bug 97674 for this
1170 // alternate patch.)
1172 UpdateView(view
, damRect
, NS_VMREFRESH_NO_SYNC
);
1182 /* Don't pass these events through. Passing them through
1183 causes performance problems on pages with lots of views/frames
1185 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1189 case NS_DISPLAYCHANGED
:
1191 //Destroy the cached backbuffer to force a new backbuffer
1192 //be constructed with the appropriate display depth.
1193 //@see bugzilla bug 6061
1194 *aStatus
= nsEventStatus_eConsumeDoDefault
;
1197 case NS_SYSCOLORCHANGED
:
1199 // Hold a refcount to the observer. The continued existence of the observer will
1200 // delay deletion of this view hierarchy should the event want to cause its
1201 // destruction in, say, some JavaScript event handler.
1202 nsView
*view
= nsView::GetViewFor(aEvent
->widget
);
1203 nsCOMPtr
<nsIViewObserver
> obs
= GetViewObserver();
1205 obs
->HandleEvent(view
, aEvent
, aStatus
);
1212 if (aEvent
->message
== NS_GOTFOCUS
) {
1213 #ifdef DEBUG_FOCUS_SUPPRESSION
1214 printf("*** 0 INFO TODO [CPEARCE] Focus changing%s\n",
1215 (nsViewManager::IsFocusSuppressed() ? " while suppressed" : ""));
1217 SetCurrentlyFocusedView(nsView::GetViewFor(aEvent
->widget
));
1219 if ((aEvent
->message
== NS_GOTFOCUS
|| aEvent
->message
== NS_LOSTFOCUS
) &&
1220 nsViewManager::IsFocusSuppressed())
1222 #ifdef DEBUG_FOCUS_SUPPRESSION
1223 printf("*** 0 INFO TODO [CPEARCE] Suppressing %s\n",
1224 (aEvent
->message
== NS_GOTFOCUS
? "NS_GOTFOCUS" : "NS_LOSTFOCUS"));
1229 if ((NS_IS_MOUSE_EVENT(aEvent
) &&
1230 // Ignore moves that we synthesize.
1231 static_cast<nsMouseEvent
*>(aEvent
)->reason
==
1232 nsMouseEvent::eReal
&&
1233 // Ignore mouse exit and enter (we'll get moves if the user
1234 // is really moving the mouse) since we get them when we
1235 // create and destroy widgets.
1236 aEvent
->message
!= NS_MOUSE_EXIT
&&
1237 aEvent
->message
!= NS_MOUSE_ENTER
) ||
1238 NS_IS_KEY_EVENT(aEvent
) ||
1239 NS_IS_IME_EVENT(aEvent
)) {
1240 gLastUserEventTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
1243 if (aEvent
->message
== NS_DEACTIVATE
) {
1245 GrabMouseEvents(nsnull
, result
);
1248 //Find the view whose coordinates system we're in.
1249 nsView
* baseView
= nsView::GetViewFor(aEvent
->widget
);
1250 nsView
* view
= baseView
;
1251 PRBool capturedEvent
= PR_FALSE
;
1253 if (!NS_IS_KEY_EVENT(aEvent
) && !NS_IS_IME_EVENT(aEvent
) &&
1254 !NS_IS_CONTEXT_MENU_KEY(aEvent
) && !NS_IS_FOCUS_EVENT(aEvent
) &&
1255 !NS_IS_QUERY_CONTENT_EVENT(aEvent
) &&
1256 aEvent
->eventStructType
!= NS_ACCESSIBLE_EVENT
) {
1257 // will dispatch using coordinates. Pretty bogus but it's consistent
1258 // with what presshell does.
1259 view
= GetDisplayRootFor(baseView
);
1262 //Find the view to which we're initially going to send the event
1264 if (NS_IS_MOUSE_EVENT(aEvent
) || NS_IS_DRAG_EVENT(aEvent
)) {
1265 nsView
* mouseGrabber
= GetMouseEventGrabber();
1267 view
= mouseGrabber
;
1268 capturedEvent
= PR_TRUE
;
1272 if (nsnull
!= view
) {
1273 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1275 if ((aEvent
->message
== NS_MOUSE_MOVE
&&
1276 static_cast<nsMouseEvent
*>(aEvent
)->reason
==
1277 nsMouseEvent::eReal
) ||
1278 aEvent
->message
== NS_MOUSE_ENTER
) {
1279 // aEvent->point is relative to the widget, i.e. the view top-left,
1280 // so we need to add the offset to the view origin
1281 nsPoint rootOffset
= baseView
->GetDimensions().TopLeft();
1282 rootOffset
+= baseView
->GetOffsetTo(RootViewManager()->mRootView
);
1283 RootViewManager()->mMouseLocation
= aEvent
->refPoint
+
1284 nsPoint(NSAppUnitsToIntPixels(rootOffset
.x
, p2a
),
1285 NSAppUnitsToIntPixels(rootOffset
.y
, p2a
));
1286 #ifdef DEBUG_MOUSE_LOCATION
1287 if (aEvent
->message
== NS_MOUSE_ENTER
)
1288 printf("[vm=%p]got mouse enter for %p\n",
1289 this, aEvent
->widget
);
1290 printf("[vm=%p]setting mouse location to (%d,%d)\n",
1291 this, mMouseLocation
.x
, mMouseLocation
.y
);
1293 if (aEvent
->message
== NS_MOUSE_ENTER
)
1294 SynthesizeMouseMove(PR_FALSE
);
1295 } else if (aEvent
->message
== NS_MOUSE_EXIT
) {
1296 // Although we only care about the mouse moving into an area
1297 // for which this view manager doesn't receive mouse move
1298 // events, we don't check which view the mouse exit was for
1299 // since this seems to vary by platform. Hopefully this
1300 // won't matter at all since we'll get the mouse move or
1301 // enter after the mouse exit when the mouse moves from one
1302 // of our widgets into another.
1303 RootViewManager()->mMouseLocation
= nsPoint(NSCOORD_NONE
, NSCOORD_NONE
);
1304 #ifdef DEBUG_MOUSE_LOCATION
1305 printf("[vm=%p]got mouse exit for %p\n",
1306 this, aEvent
->widget
);
1307 printf("[vm=%p]clearing mouse location\n",
1312 //Calculate the proper offset for the view we're going to
1313 nsPoint
offset(0, 0);
1315 if (view
!= baseView
) {
1316 //Get offset from root of baseView
1318 for (parent
= baseView
; parent
; parent
= parent
->GetParent())
1319 parent
->ConvertToParentCoords(&offset
.x
, &offset
.y
);
1321 //Subtract back offset from root of view
1322 for (parent
= view
; parent
; parent
= parent
->GetParent())
1323 parent
->ConvertFromParentCoords(&offset
.x
, &offset
.y
);
1326 // Dispatch the event
1327 nsRect baseViewDimensions
;
1328 if (baseView
!= nsnull
) {
1329 baseView
->GetDimensions(baseViewDimensions
);
1333 pt
.x
= baseViewDimensions
.x
+
1334 NSFloatPixelsToAppUnits(float(aEvent
->refPoint
.x
) + 0.5f
, p2a
);
1335 pt
.y
= baseViewDimensions
.y
+
1336 NSFloatPixelsToAppUnits(float(aEvent
->refPoint
.y
) + 0.5f
, p2a
);
1339 *aStatus
= HandleEvent(view
, pt
, aEvent
, capturedEvent
);
1342 // need to map the reply back into platform coordinates
1344 switch (aEvent
->message
) {
1346 ConvertRectAppUnitsToIntPixels(
1347 ((nsTextEvent
*)aEvent
)->theReply
.mCursorPosition
, p2a
);
1349 case NS_COMPOSITION_START
:
1350 case NS_COMPOSITION_QUERY
:
1351 ConvertRectAppUnitsToIntPixels(
1352 ((nsCompositionEvent
*)aEvent
)->theReply
.mCursorPosition
, p2a
);
1354 case NS_QUERY_CHARACTER_RECT
:
1355 case NS_QUERY_CARET_RECT
:
1356 ConvertRectAppUnitsToIntPixels(
1357 ((nsQueryContentEvent
*)aEvent
)->mReply
.mRect
, p2a
);
1369 nsEventStatus
nsViewManager::HandleEvent(nsView
* aView
, nsPoint aPoint
,
1370 nsGUIEvent
* aEvent
, PRBool aCaptured
) {
1371 //printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports,
1372 // event->message, event->point.x, event->point.y);
1374 // Hold a refcount to the observer. The continued existence of the observer will
1375 // delay deletion of this view hierarchy should the event want to cause its
1376 // destruction in, say, some JavaScript event handler.
1377 nsCOMPtr
<nsIViewObserver
> obs
= aView
->GetViewManager()->GetViewObserver();
1378 nsEventStatus status
= nsEventStatus_eIgnore
;
1380 obs
->HandleEvent(aView
, aEvent
, &status
);
1386 NS_IMETHODIMP
nsViewManager::GrabMouseEvents(nsIView
*aView
, PRBool
&aResult
)
1389 return RootViewManager()->GrabMouseEvents(aView
, aResult
);
1392 // Along with nsView::SetVisibility, we enforce that the mouse grabber
1393 // can never be a hidden view.
1394 if (aView
&& static_cast<nsView
*>(aView
)->GetVisibility()
1395 == nsViewVisibility_kHide
) {
1402 printf("capturing mouse events for view %x\n",aView
);
1404 printf("removing mouse capture from view %x\n",mMouseGrabber
);
1407 mMouseGrabber
= static_cast<nsView
*>(aView
);
1412 NS_IMETHODIMP
nsViewManager::GetMouseEventGrabber(nsIView
*&aView
)
1414 aView
= GetMouseEventGrabber();
1418 // Recursively reparent widgets if necessary
1420 void nsViewManager::ReparentChildWidgets(nsIView
* aView
, nsIWidget
*aNewWidget
)
1422 if (aView
->HasWidget()) {
1423 // Check to see if the parent widget is the
1424 // same as the new parent. If not then reparent
1425 // the widget, otherwise there is nothing more
1426 // to do for the view and its descendants
1427 nsIWidget
* widget
= aView
->GetWidget();
1428 nsIWidget
* parentWidget
= widget
->GetParent();
1429 // Toplevel widgets should not be reparented!
1430 if (parentWidget
&& parentWidget
!= aNewWidget
) {
1434 widget
->SetParent(aNewWidget
);
1435 NS_ASSERTION(NS_SUCCEEDED(rv
), "SetParent failed!");
1440 // Need to check each of the views children to see
1441 // if they have a widget and reparent it.
1443 nsView
* view
= static_cast<nsView
*>(aView
);
1444 for (nsView
*kid
= view
->GetFirstChild(); kid
; kid
= kid
->GetNextSibling()) {
1445 ReparentChildWidgets(kid
, aNewWidget
);
1449 // Reparent a view and its descendant views widgets if necessary
1451 void nsViewManager::ReparentWidgets(nsIView
* aView
, nsIView
*aParent
)
1453 NS_PRECONDITION(aParent
, "Must have a parent");
1454 NS_PRECONDITION(aView
, "Must have a view");
1456 // Quickly determine whether the view has pre-existing children or a
1457 // widget. In most cases the view will not have any pre-existing
1458 // children when this is called. Only in the case
1459 // where a view has been reparented by removing it from
1460 // a reinserting it into a new location in the view hierarchy do we
1461 // have to consider reparenting the existing widgets for the view and
1462 // it's descendants.
1463 nsView
* view
= static_cast<nsView
*>(aView
);
1464 if (view
->HasWidget() || view
->GetFirstChild()) {
1465 nsIWidget
* parentWidget
= aParent
->GetNearestWidget(nsnull
);
1467 ReparentChildWidgets(aView
, parentWidget
);
1470 NS_WARNING("Can not find a widget for the parent view");
1474 NS_IMETHODIMP
nsViewManager::InsertChild(nsIView
*aParent
, nsIView
*aChild
, nsIView
*aSibling
,
1477 nsView
* parent
= static_cast<nsView
*>(aParent
);
1478 nsView
* child
= static_cast<nsView
*>(aChild
);
1479 nsView
* sibling
= static_cast<nsView
*>(aSibling
);
1481 NS_PRECONDITION(nsnull
!= parent
, "null ptr");
1482 NS_PRECONDITION(nsnull
!= child
, "null ptr");
1483 NS_ASSERTION(sibling
== nsnull
|| sibling
->GetParent() == parent
,
1484 "tried to insert view with invalid sibling");
1485 NS_ASSERTION(!IsViewInserted(child
), "tried to insert an already-inserted view");
1487 if ((nsnull
!= parent
) && (nsnull
!= child
))
1489 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
1490 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
1493 if (nsnull
== aSibling
) {
1495 // insert at end of document order, i.e., before first view
1496 // this is the common case, by far
1497 parent
->InsertChild(child
, nsnull
);
1498 ReparentWidgets(child
, parent
);
1500 // insert at beginning of document order, i.e., after last view
1501 nsView
*kid
= parent
->GetFirstChild();
1502 nsView
*prev
= nsnull
;
1505 kid
= kid
->GetNextSibling();
1507 // prev is last view or null if there are no children
1508 parent
->InsertChild(child
, prev
);
1509 ReparentWidgets(child
, parent
);
1512 nsView
*kid
= parent
->GetFirstChild();
1513 nsView
*prev
= nsnull
;
1514 while (kid
&& sibling
!= kid
) {
1515 //get the next sibling view
1517 kid
= kid
->GetNextSibling();
1519 NS_ASSERTION(kid
!= nsnull
,
1520 "couldn't find sibling in child list");
1522 // insert after 'kid' in document order, i.e. before in view order
1523 parent
->InsertChild(child
, prev
);
1524 ReparentWidgets(child
, parent
);
1526 // insert before 'kid' in document order, i.e. after in view order
1527 parent
->InsertChild(child
, kid
);
1528 ReparentWidgets(child
, parent
);
1531 #else // don't keep consistent document order, but order things by z-index instead
1532 // essentially we're emulating the old InsertChild(parent, child, zindex)
1533 PRInt32 zIndex
= child
->GetZIndex();
1534 while (nsnull
!= kid
)
1536 PRInt32 idx
= kid
->GetZIndex();
1538 if (CompareZIndex(zIndex
, child
->IsTopMost(), child
->GetZIndexIsAuto(),
1539 idx
, kid
->IsTopMost(), kid
->GetZIndexIsAuto()) >= 0)
1543 kid
= kid
->GetNextSibling();
1546 parent
->InsertChild(child
, prev
);
1547 ReparentWidgets(child
, parent
);
1550 // if the parent view is marked as "floating", make the newly added view float as well.
1551 if (parent
->GetFloating())
1552 child
->SetFloating(PR_TRUE
);
1554 //and mark this area as dirty if the view is visible...
1556 if (nsViewVisibility_kHide
!= child
->GetVisibility())
1557 UpdateView(child
, NS_VMREFRESH_NO_SYNC
);
1562 NS_IMETHODIMP
nsViewManager::InsertChild(nsIView
*aParent
, nsIView
*aChild
, PRInt32 aZIndex
)
1564 // no-one really calls this with anything other than aZIndex == 0 on a fresh view
1565 // XXX this method should simply be eliminated and its callers redirected to the real method
1566 SetViewZIndex(aChild
, PR_FALSE
, aZIndex
, PR_FALSE
);
1567 return InsertChild(aParent
, aChild
, nsnull
, PR_TRUE
);
1570 NS_IMETHODIMP
nsViewManager::RemoveChild(nsIView
*aChild
)
1572 nsView
* child
= static_cast<nsView
*>(aChild
);
1573 NS_ENSURE_ARG_POINTER(child
);
1575 nsView
* parent
= child
->GetParent();
1577 if (nsnull
!= parent
)
1579 UpdateView(child
, NS_VMREFRESH_NO_SYNC
);
1580 parent
->RemoveChild(child
);
1586 NS_IMETHODIMP
nsViewManager::MoveViewBy(nsIView
*aView
, nscoord aX
, nscoord aY
)
1588 nsView
* view
= static_cast<nsView
*>(aView
);
1590 nsPoint pt
= view
->GetPosition();
1591 MoveViewTo(view
, aX
+ pt
.x
, aY
+ pt
.y
);
1595 NS_IMETHODIMP
nsViewManager::MoveViewTo(nsIView
*aView
, nscoord aX
, nscoord aY
)
1597 nsView
* view
= static_cast<nsView
*>(aView
);
1598 nsPoint oldPt
= view
->GetPosition();
1599 nsRect oldArea
= view
->GetBounds();
1600 view
->SetPosition(aX
, aY
);
1602 // only do damage control if the view is visible
1604 if ((aX
!= oldPt
.x
) || (aY
!= oldPt
.y
)) {
1605 if (view
->GetVisibility() != nsViewVisibility_kHide
) {
1606 nsView
* parentView
= view
->GetParent();
1607 UpdateView(parentView
, oldArea
, NS_VMREFRESH_NO_SYNC
);
1608 UpdateView(parentView
, view
->GetBounds(), NS_VMREFRESH_NO_SYNC
);
1614 void nsViewManager::InvalidateHorizontalBandDifference(nsView
*aView
, const nsRect
& aRect
, const nsRect
& aCutOut
,
1615 PRUint32 aUpdateFlags
, nscoord aY1
, nscoord aY2
, PRBool aInCutOut
) {
1616 nscoord height
= aY2
- aY1
;
1617 if (aRect
.x
< aCutOut
.x
) {
1618 nsRect
r(aRect
.x
, aY1
, aCutOut
.x
- aRect
.x
, height
);
1619 UpdateView(aView
, r
, aUpdateFlags
);
1621 if (!aInCutOut
&& aCutOut
.x
< aCutOut
.XMost()) {
1622 nsRect
r(aCutOut
.x
, aY1
, aCutOut
.width
, height
);
1623 UpdateView(aView
, r
, aUpdateFlags
);
1625 if (aCutOut
.XMost() < aRect
.XMost()) {
1626 nsRect
r(aCutOut
.XMost(), aY1
, aRect
.XMost() - aCutOut
.XMost(), height
);
1627 UpdateView(aView
, r
, aUpdateFlags
);
1631 void nsViewManager::InvalidateRectDifference(nsView
*aView
, const nsRect
& aRect
, const nsRect
& aCutOut
,
1632 PRUint32 aUpdateFlags
) {
1633 if (aRect
.y
< aCutOut
.y
) {
1634 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aRect
.y
, aCutOut
.y
, PR_FALSE
);
1636 if (aCutOut
.y
< aCutOut
.YMost()) {
1637 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aCutOut
.y
, aCutOut
.YMost(), PR_TRUE
);
1639 if (aCutOut
.YMost() < aRect
.YMost()) {
1640 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aCutOut
.YMost(), aRect
.YMost(), PR_FALSE
);
1644 NS_IMETHODIMP
nsViewManager::ResizeView(nsIView
*aView
, const nsRect
&aRect
, PRBool aRepaintExposedAreaOnly
)
1646 nsView
* view
= static_cast<nsView
*>(aView
);
1647 nsRect oldDimensions
;
1649 view
->GetDimensions(oldDimensions
);
1650 if (!oldDimensions
.IsExactEqual(aRect
)) {
1651 nsView
* parentView
= view
->GetParent();
1652 if (parentView
== nsnull
)
1656 // Prevent Invalidation of hidden views
1657 if (view
->GetVisibility() == nsViewVisibility_kHide
) {
1658 view
->SetDimensions(aRect
, PR_FALSE
);
1660 if (!aRepaintExposedAreaOnly
) {
1661 //Invalidate the union of the old and new size
1662 view
->SetDimensions(aRect
, PR_TRUE
);
1664 UpdateView(view
, aRect
, NS_VMREFRESH_NO_SYNC
);
1665 view
->ConvertToParentCoords(&oldDimensions
.x
, &oldDimensions
.y
);
1666 UpdateView(parentView
, oldDimensions
, NS_VMREFRESH_NO_SYNC
);
1668 view
->SetDimensions(aRect
, PR_TRUE
);
1670 InvalidateRectDifference(view
, aRect
, oldDimensions
, NS_VMREFRESH_NO_SYNC
);
1672 view
->ConvertToParentCoords(&r
.x
, &r
.y
);
1673 view
->ConvertToParentCoords(&oldDimensions
.x
, &oldDimensions
.y
);
1674 InvalidateRectDifference(parentView
, oldDimensions
, r
, NS_VMREFRESH_NO_SYNC
);
1679 // Note that if layout resizes the view and the view has a custom clip
1680 // region set, then we expect layout to update the clip region too. Thus
1681 // in the case where mClipRect has been optimized away to just be a null
1682 // pointer, and this resize is implicitly changing the clip rect, it's OK
1683 // because layout will change it back again if necessary.
1688 PRBool
nsViewManager::CanScrollWithBitBlt(nsView
* aView
, nsPoint aDelta
,
1689 nsRegion
* aUpdateRegion
)
1691 NS_ASSERTION(!IsPainting(),
1692 "View manager shouldn't be scrolling during a paint");
1693 if (IsPainting() || !mObserver
) {
1694 return PR_FALSE
; // do the safe thing
1697 nsView
* displayRoot
= GetDisplayRootFor(aView
);
1698 nsPoint displayOffset
= aView
->GetParent()->GetOffsetTo(displayRoot
);
1699 nsRect parentBounds
= aView
->GetParent()->GetDimensions() + displayOffset
;
1700 // The rect we're going to scroll is intersection of the parent bounds with its
1703 toScroll
.IntersectRect(parentBounds
+ aDelta
, parentBounds
);
1705 mObserver
->ComputeRepaintRegionForCopy(displayRoot
, aView
, -aDelta
, toScroll
,
1707 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
1709 aUpdateRegion
->MoveBy(-displayOffset
);
1711 #if defined(MOZ_WIDGET_GTK2) || defined(XP_OS2)
1712 return aUpdateRegion
->IsEmpty();
1718 NS_IMETHODIMP
nsViewManager::SetViewFloating(nsIView
*aView
, PRBool aFloating
)
1720 nsView
* view
= static_cast<nsView
*>(aView
);
1722 NS_ASSERTION(!(nsnull
== view
), "no view");
1724 view
->SetFloating(aFloating
);
1729 NS_IMETHODIMP
nsViewManager::SetViewVisibility(nsIView
*aView
, nsViewVisibility aVisible
)
1731 nsView
* view
= static_cast<nsView
*>(aView
);
1733 if (aVisible
!= view
->GetVisibility()) {
1734 view
->SetVisibility(aVisible
);
1736 if (IsViewInserted(view
)) {
1737 if (!view
->HasWidget()) {
1738 if (nsViewVisibility_kHide
== aVisible
) {
1739 nsView
* parentView
= view
->GetParent();
1741 UpdateView(parentView
, view
->GetBounds(), NS_VMREFRESH_NO_SYNC
);
1745 UpdateView(view
, NS_VMREFRESH_NO_SYNC
);
1750 // Any child views not associated with frames might not get their visibility
1751 // updated, so propagate our visibility to them. This is important because
1752 // hidden views should have all hidden children.
1753 for (nsView
* childView
= view
->GetFirstChild(); childView
;
1754 childView
= childView
->GetNextSibling()) {
1755 if (!childView
->GetClientData()) {
1756 childView
->SetVisibility(aVisible
);
1763 void nsViewManager::UpdateWidgetsForView(nsView
* aView
)
1765 NS_PRECONDITION(aView
, "Must have view!");
1767 if (aView
->HasWidget()) {
1768 aView
->GetWidget()->Update();
1771 for (nsView
* childView
= aView
->GetFirstChild();
1773 childView
= childView
->GetNextSibling()) {
1774 UpdateWidgetsForView(childView
);
1778 PRBool
nsViewManager::IsViewInserted(nsView
*aView
)
1780 if (mRootView
== aView
) {
1782 } else if (aView
->GetParent() == nsnull
) {
1785 nsView
* view
= aView
->GetParent()->GetFirstChild();
1786 while (view
!= nsnull
) {
1787 if (view
== aView
) {
1790 view
= view
->GetNextSibling();
1796 NS_IMETHODIMP
nsViewManager::SetViewZIndex(nsIView
*aView
, PRBool aAutoZIndex
, PRInt32 aZIndex
, PRBool aTopMost
)
1798 nsView
* view
= static_cast<nsView
*>(aView
);
1799 nsresult rv
= NS_OK
;
1801 NS_ASSERTION((view
!= nsnull
), "no view");
1803 // don't allow the root view's z-index to be changed. It should always be zero.
1804 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1805 if (aView
== mRootView
) {
1809 PRBool oldTopMost
= view
->IsTopMost();
1810 PRBool oldIsAuto
= view
->GetZIndexIsAuto();
1816 PRInt32 oldidx
= view
->GetZIndex();
1817 view
->SetZIndex(aAutoZIndex
, aZIndex
, aTopMost
);
1819 if (oldidx
!= aZIndex
|| oldTopMost
!= aTopMost
||
1820 oldIsAuto
!= aAutoZIndex
) {
1821 UpdateView(view
, NS_VMREFRESH_NO_SYNC
);
1827 NS_IMETHODIMP
nsViewManager::SetViewObserver(nsIViewObserver
*aObserver
)
1829 mObserver
= aObserver
;
1833 NS_IMETHODIMP
nsViewManager::GetViewObserver(nsIViewObserver
*&aObserver
)
1835 if (nsnull
!= mObserver
) {
1836 aObserver
= mObserver
;
1837 NS_ADDREF(mObserver
);
1840 return NS_ERROR_NO_INTERFACE
;
1843 NS_IMETHODIMP
nsViewManager::GetDeviceContext(nsIDeviceContext
*&aContext
)
1845 NS_IF_ADDREF(mContext
);
1846 aContext
= mContext
;
1850 already_AddRefed
<nsIRenderingContext
>
1851 nsViewManager::CreateRenderingContext(nsView
&aView
)
1853 nsView
*par
= &aView
;
1855 nsIRenderingContext
*cx
= nsnull
;
1856 nscoord ax
= 0, ay
= 0;
1860 win
= par
->GetWidget();
1864 //get absolute coordinates of view, but don't
1865 //add in view pos since the first thing you ever
1866 //need to do when painting a view is to translate
1867 //the rendering context by the views pos and other parts
1868 //of the code do this for us...
1872 par
->ConvertToParentCoords(&ax
, &ay
);
1875 par
= par
->GetParent();
1877 while (nsnull
!= par
);
1881 // XXXkt this has an origin at top-left of win ...
1882 mContext
->CreateRenderingContext(par
, cx
);
1884 // XXXkt ... but the translation is between the origins of views
1885 NS_ASSERTION(aView
.ViewToWidgetOffset()
1886 - aView
.GetDimensions().TopLeft() ==
1887 par
->ViewToWidgetOffset()
1888 - par
->GetDimensions().TopLeft(),
1889 "ViewToWidgetOffset not handled!");
1891 cx
->Translate(ax
, ay
);
1897 NS_IMETHODIMP
nsViewManager::DisableRefresh(void)
1900 return RootViewManager()->DisableRefresh();
1903 if (mUpdateBatchCnt
> 0)
1906 mRefreshEnabled
= PR_FALSE
;
1910 NS_IMETHODIMP
nsViewManager::EnableRefresh(PRUint32 aUpdateFlags
)
1913 return RootViewManager()->EnableRefresh(aUpdateFlags
);
1916 if (mUpdateBatchCnt
> 0)
1919 mRefreshEnabled
= PR_TRUE
;
1921 if (!mHasPendingUpdates
) {
1926 // nested batching can combine IMMEDIATE with DEFERRED. Favour
1927 // IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC.
1928 if (aUpdateFlags
& NS_VMREFRESH_IMMEDIATE
) {
1929 FlushPendingInvalidates();
1931 } else if (aUpdateFlags
& NS_VMREFRESH_DEFERRED
) {
1932 PostInvalidateEvent();
1934 FlushPendingInvalidates();
1940 nsIViewManager
* nsViewManager::BeginUpdateViewBatch(void)
1943 return RootViewManager()->BeginUpdateViewBatch();
1946 nsresult result
= NS_OK
;
1948 if (mUpdateBatchCnt
== 0) {
1949 mUpdateBatchFlags
= 0;
1950 result
= DisableRefresh();
1953 if (NS_SUCCEEDED(result
))
1959 NS_IMETHODIMP
nsViewManager::EndUpdateViewBatch(PRUint32 aUpdateFlags
)
1961 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1963 nsresult result
= NS_OK
;
1967 NS_ASSERTION(mUpdateBatchCnt
>= 0, "Invalid batch count!");
1969 if (mUpdateBatchCnt
< 0)
1971 mUpdateBatchCnt
= 0;
1972 return NS_ERROR_FAILURE
;
1975 mUpdateBatchFlags
|= aUpdateFlags
;
1976 if (mUpdateBatchCnt
== 0) {
1977 result
= EnableRefresh(mUpdateBatchFlags
);
1983 NS_IMETHODIMP
nsViewManager::SetRootScrollableView(nsIScrollableView
*aScrollable
)
1985 mRootScrollable
= aScrollable
;
1989 NS_IMETHODIMP
nsViewManager::GetRootScrollableView(nsIScrollableView
**aScrollable
)
1991 *aScrollable
= mRootScrollable
;
1995 NS_IMETHODIMP
nsViewManager::GetWidget(nsIWidget
**aWidget
)
1997 *aWidget
= GetWidget();
1998 NS_IF_ADDREF(*aWidget
);
2002 NS_IMETHODIMP
nsViewManager::ForceUpdate()
2005 return RootViewManager()->ForceUpdate();
2008 // Walk the view tree looking for widgets, and call Update() on each one
2010 UpdateWidgetsForView(mRootView
);
2016 nsPoint
nsViewManager::ComputeViewOffset(const nsView
*aView
)
2018 NS_PRECONDITION(aView
, "Null view in ComputeViewOffset?");
2020 nsPoint
origin(0, 0);
2022 const nsView
* rootView
;
2023 const nsView
* origView
= aView
;
2030 origin
+= aView
->GetPosition();
2031 aView
= aView
->GetParent();
2033 NS_ASSERTION(rootView
==
2034 origView
->GetViewManager()->RootViewManager()->GetRootView(),
2035 "Unexpected root view");
2039 void nsViewManager::ViewToWidget(nsView
*aView
, nsView
* aWidgetView
, nsRect
&aRect
) const
2041 while (aView
!= aWidgetView
) {
2042 aView
->ConvertToParentCoords(&aRect
.x
, &aRect
.y
);
2043 aView
= aView
->GetParent();
2046 // intersect aRect with bounds of aWidgetView, to prevent generating any illegal rectangles.
2048 aWidgetView
->GetDimensions(bounds
);
2049 aRect
.IntersectRect(aRect
, bounds
);
2050 // account for the view's origin not lining up with the widget's
2051 aRect
.x
-= bounds
.x
;
2052 aRect
.y
-= bounds
.y
;
2054 aRect
+= aView
->ViewToWidgetOffset();
2056 // finally, convert to device coordinates.
2057 aRect
.ScaleRoundOut(1.0f
/ mContext
->AppUnitsPerDevPixel());
2060 nsresult
nsViewManager::GetVisibleRect(nsRect
& aVisibleRect
)
2062 nsresult rv
= NS_OK
;
2064 // Get the viewport scroller
2065 nsIScrollableView
* scrollingView
;
2066 GetRootScrollableView(&scrollingView
);
2068 if (scrollingView
) {
2069 // Determine the visible rect in the scrolled view's coordinate space.
2070 // The size of the visible area is the clip view size
2071 nsScrollPortView
* clipView
= static_cast<nsScrollPortView
*>(scrollingView
);
2072 clipView
->GetDimensions(aVisibleRect
);
2074 scrollingView
->GetScrollPosition(aVisibleRect
.x
, aVisibleRect
.y
);
2076 rv
= NS_ERROR_FAILURE
;
2082 nsresult
nsViewManager::GetAbsoluteRect(nsView
*aView
, const nsRect
&aRect
,
2085 nsIScrollableView
* scrollingView
= nsnull
;
2086 GetRootScrollableView(&scrollingView
);
2087 if (nsnull
== scrollingView
) {
2088 return NS_ERROR_FAILURE
;
2091 nsIView
* scrolledIView
= nsnull
;
2092 scrollingView
->GetScrolledView(scrolledIView
);
2094 nsView
* scrolledView
= static_cast<nsView
*>(scrolledIView
);
2096 // Calculate the absolute coordinates of the aRect passed in.
2097 // aRects values are relative to aView
2099 nsView
*parentView
= aView
;
2100 while ((parentView
!= nsnull
) && (parentView
!= scrolledView
)) {
2101 parentView
->ConvertToParentCoords(&aAbsRect
.x
, &aAbsRect
.y
);
2102 parentView
= parentView
->GetParent();
2105 if (parentView
!= scrolledView
) {
2106 return NS_ERROR_FAILURE
;
2113 NS_IMETHODIMP
nsViewManager::GetRectVisibility(nsIView
*aView
,
2114 const nsRect
&aRect
,
2116 nsRectVisibility
*aRectVisibility
)
2118 nsView
* view
= static_cast<nsView
*>(aView
);
2120 // The parameter aMinTwips determines how many rows/cols of pixels must be visible on each side of the element,
2121 // in order to be counted as visible
2123 *aRectVisibility
= nsRectVisibility_kZeroAreaRect
;
2124 if (aRect
.width
== 0 || aRect
.height
== 0) {
2128 // is this view even visible?
2129 if (view
->GetVisibility() == nsViewVisibility_kHide
) {
2133 // nsViewManager::InsertChild ensures that descendants of floating views
2134 // are also marked floating.
2135 if (view
->GetFloating()) {
2136 *aRectVisibility
= nsRectVisibility_kVisible
;
2140 // Calculate the absolute coordinates for the visible rectangle
2142 if (GetVisibleRect(visibleRect
) == NS_ERROR_FAILURE
) {
2143 *aRectVisibility
= nsRectVisibility_kVisible
;
2147 // Calculate the absolute coordinates of the aRect passed in.
2148 // aRects values are relative to aView
2150 if ((GetAbsoluteRect(view
, aRect
, absRect
)) == NS_ERROR_FAILURE
) {
2151 *aRectVisibility
= nsRectVisibility_kVisible
;
2156 * If aMinTwips > 0, ensure at least aMinTwips of space around object is visible
2157 * The object is not visible if:
2158 * ((objectTop < windowTop && objectBottom < windowTop) ||
2159 * (objectBottom > windowBottom && objectTop > windowBottom) ||
2160 * (objectLeft < windowLeft && objectRight < windowLeft) ||
2161 * (objectRight > windowRight && objectLeft > windowRight))
2164 if (absRect
.y
< visibleRect
.y
&&
2165 absRect
.y
+ absRect
.height
< visibleRect
.y
+ aMinTwips
)
2166 *aRectVisibility
= nsRectVisibility_kAboveViewport
;
2167 else if (absRect
.y
+ absRect
.height
> visibleRect
.y
+ visibleRect
.height
&&
2168 absRect
.y
> visibleRect
.y
+ visibleRect
.height
- aMinTwips
)
2169 *aRectVisibility
= nsRectVisibility_kBelowViewport
;
2170 else if (absRect
.x
< visibleRect
.x
&&
2171 absRect
.x
+ absRect
.width
< visibleRect
.x
+ aMinTwips
)
2172 *aRectVisibility
= nsRectVisibility_kLeftOfViewport
;
2173 else if (absRect
.x
+ absRect
.width
> visibleRect
.x
+ visibleRect
.width
&&
2174 absRect
.x
> visibleRect
.x
+ visibleRect
.width
- aMinTwips
)
2175 *aRectVisibility
= nsRectVisibility_kRightOfViewport
;
2177 *aRectVisibility
= nsRectVisibility_kVisible
;
2183 nsViewManager::IsPainting(PRBool
& aIsPainting
)
2185 aIsPainting
= IsPainting();
2190 nsViewManager::FlushPendingInvalidates()
2192 NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!\n");
2193 NS_ASSERTION(mUpdateBatchCnt
== 0, "Must not be in an update batch!");
2194 // XXXbz this is probably not quite OK yet, if callers can explicitly
2195 // DisableRefresh while we have an event posted.
2196 // NS_ASSERTION(mRefreshEnabled, "How did we get here?");
2198 // Let all the view observers of all viewmanagers in this tree know that
2199 // we're about to "paint" (this lets them get in their invalidates now so
2200 // we don't go through two invalidate-processing cycles).
2201 NS_ASSERTION(gViewManagers
, "Better have a viewmanagers array!");
2203 // Make sure to not send WillPaint notifications while scrolling
2204 if (mScrollCnt
== 0) {
2205 // Disable refresh while we notify our view observers, so that if they do
2206 // view update batches we don't reenter this code and so that we batch
2207 // all of them together. We don't use
2208 // BeginUpdateViewBatch/EndUpdateViewBatch, since that would reenter this
2209 // exact code, but we want the effect of a single big update batch.
2210 PRBool refreshEnabled
= mRefreshEnabled
;
2211 mRefreshEnabled
= PR_FALSE
;
2215 for (index
= 0; index
< mVMCount
; index
++) {
2216 nsViewManager
* vm
= (nsViewManager
*)gViewManagers
->ElementAt(index
);
2217 if (vm
->RootViewManager() == this) {
2219 nsIViewObserver
* observer
= vm
->GetViewObserver();
2221 observer
->WillPaint();
2222 NS_ASSERTION(mUpdateBatchCnt
== 1,
2223 "Observer did not end view batch?");
2229 // Someone could have called EnableRefresh on us from inside WillPaint().
2230 // Only reset the old mRefreshEnabled value if the current value is false.
2231 if (!mRefreshEnabled
) {
2232 mRefreshEnabled
= refreshEnabled
;
2236 if (mHasPendingUpdates
) {
2237 ProcessPendingUpdates(mRootView
, PR_TRUE
);
2238 mHasPendingUpdates
= PR_FALSE
;
2243 nsViewManager::ProcessInvalidateEvent()
2245 NS_ASSERTION(IsRootVM(),
2246 "Incorrectly targeted invalidate event");
2247 // If we're in the middle of an update batch, just repost the event,
2248 // to be processed when the batch ends.
2249 PRBool processEvent
= (mUpdateBatchCnt
== 0);
2251 FlushPendingInvalidates();
2253 mInvalidateEvent
.Forget();
2254 if (!processEvent
) {
2255 // We didn't actually process this event... post a new one
2256 PostInvalidateEvent();
2261 nsViewManager::SetDefaultBackgroundColor(nscolor aColor
)
2263 mDefaultBackgroundColor
= aColor
;
2268 nsViewManager::GetDefaultBackgroundColor(nscolor
* aColor
)
2270 *aColor
= mDefaultBackgroundColor
;
2276 nsViewManager::GetLastUserEventTime(PRUint32
& aTime
)
2278 aTime
= gLastUserEventTime
;
2282 class nsSynthMouseMoveEvent
: public nsViewManagerEvent
{
2284 nsSynthMouseMoveEvent(nsViewManager
*aViewManager
,
2286 : nsViewManagerEvent(aViewManager
),
2287 mFromScroll(aFromScroll
) {
2292 mViewManager
->ProcessSynthMouseMoveEvent(mFromScroll
);
2301 nsViewManager::SynthesizeMouseMove(PRBool aFromScroll
)
2304 return RootViewManager()->SynthesizeMouseMove(aFromScroll
);
2306 if (mMouseLocation
== nsPoint(NSCOORD_NONE
, NSCOORD_NONE
))
2309 if (!mSynthMouseMoveEvent
.IsPending()) {
2310 nsRefPtr
<nsViewManagerEvent
> ev
=
2311 new nsSynthMouseMoveEvent(this, aFromScroll
);
2313 if (NS_FAILED(NS_DispatchToCurrentThread(ev
))) {
2314 NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
2315 return NS_ERROR_UNEXPECTED
;
2318 mSynthMouseMoveEvent
= ev
;
2325 * Find the first floating view with a widget in a postorder traversal of the
2326 * view tree that contains the point. Thus more deeply nested floating views
2327 * are preferred over their ancestors, and floating views earlier in the
2328 * view hierarchy (i.e., added later) are preferred over their siblings.
2329 * This is adequate for finding the "topmost" floating view under a point,
2330 * given that floating views don't supporting having a specific z-index.
2332 * We cannot exit early when aPt is outside the view bounds, because floating
2333 * views aren't necessarily included in their parent's bounds, so this could
2334 * traverse the entire view hierarchy --- use carefully.
2336 static nsView
* FindFloatingViewContaining(nsView
* aView
, nsPoint aPt
)
2338 for (nsView
* v
= aView
->GetFirstChild(); v
; v
= v
->GetNextSibling()) {
2339 nsView
* r
= FindFloatingViewContaining(v
, aPt
- v
->GetOffsetTo(aView
));
2344 if (aView
->GetFloating() && aView
->HasWidget() &&
2345 aView
->GetDimensions().Contains(aPt
) && IsViewVisible(aView
))
2352 nsViewManager::ProcessSynthMouseMoveEvent(PRBool aFromScroll
)
2354 // allow new event to be posted while handling this one only if the
2355 // source of the event is a scroll (to prevent infinite reflow loops)
2357 mSynthMouseMoveEvent
.Forget();
2359 NS_ASSERTION(IsRootVM(), "Only the root view manager should be here");
2361 if (mMouseLocation
== nsPoint(NSCOORD_NONE
, NSCOORD_NONE
) || !mRootView
) {
2362 mSynthMouseMoveEvent
.Forget();
2366 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
2367 // we need to access members after we call DispatchEvent).
2368 nsCOMPtr
<nsIViewManager
> kungFuDeathGrip(this);
2370 #ifdef DEBUG_MOUSE_LOCATION
2371 printf("[vm=%p]synthesizing mouse move to (%d,%d)\n",
2372 this, mMouseLocation
.x
, mMouseLocation
.y
);
2375 nsPoint pt
= mMouseLocation
;
2376 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
2377 pt
.x
= NSIntPixelsToAppUnits(mMouseLocation
.x
, p2a
);
2378 pt
.y
= NSIntPixelsToAppUnits(mMouseLocation
.y
, p2a
);
2379 // This could be a bit slow (traverses entire view hierarchy)
2380 // but it's OK to do it once per synthetic mouse event
2381 nsView
* view
= FindFloatingViewContaining(mRootView
, pt
);
2382 nsPoint
offset(0, 0);
2386 offset
= view
->GetOffsetTo(mRootView
);
2387 offset
.x
= NSAppUnitsToIntPixels(offset
.x
, p2a
);
2388 offset
.y
= NSAppUnitsToIntPixels(offset
.y
, p2a
);
2390 nsMouseEvent
event(PR_TRUE
, NS_MOUSE_MOVE
, view
->GetWidget(),
2391 nsMouseEvent::eSynthesized
);
2392 event
.refPoint
= mMouseLocation
- offset
;
2393 event
.time
= PR_IntervalNow();
2394 // XXX set event.isShift, event.isControl, event.isAlt, event.isMeta ?
2396 nsEventStatus status
;
2397 view
->GetViewManager()->DispatchEvent(&event
, &status
);
2400 mSynthMouseMoveEvent
.Forget();
2404 nsViewManager::InvalidateHierarchy()
2408 NS_RELEASE(mRootViewManager
);
2410 nsView
*parent
= mRootView
->GetParent();
2412 mRootViewManager
= parent
->GetViewManager()->RootViewManager();
2413 NS_ADDREF(mRootViewManager
);
2414 NS_ASSERTION(mRootViewManager
!= this,
2415 "Root view had a parent, but it has the same view manager");
2417 mRootViewManager
= this;