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
)
127 // Find out if the root view is visible by asking the view observer
128 // (this won't be needed anymore if we link view trees across chrome /
129 // content boundaries in DocumentViewerImpl::MakeWindow).
130 nsIViewObserver
* vo
= aView
->GetViewManager()->GetViewObserver();
131 return vo
&& vo
->IsVisible();
135 nsViewManager::PostInvalidateEvent()
137 NS_ASSERTION(IsRootVM(), "Caller screwed up");
139 if (!mInvalidateEvent
.IsPending()) {
140 nsRefPtr
<nsViewManagerEvent
> ev
= new nsInvalidateEvent(this);
141 if (NS_FAILED(NS_DispatchToCurrentThread(ev
))) {
142 NS_WARNING("failed to dispatch nsInvalidateEvent");
144 mInvalidateEvent
= ev
;
149 #undef DEBUG_MOUSE_LOCATION
151 PRInt32
nsViewManager::mVMCount
= 0;
152 nsIRenderingContext
* nsViewManager::gCleanupContext
= nsnull
;
154 // Weakly held references to all of the view managers
155 nsVoidArray
* nsViewManager::gViewManagers
= nsnull
;
156 PRUint32
nsViewManager::gLastUserEventTime
= 0;
158 nsViewManager::nsViewManager()
159 : mMouseLocation(NSCOORD_NONE
, NSCOORD_NONE
)
160 , mDelayedResize(NSCOORD_NONE
, NSCOORD_NONE
)
161 , mRootViewManager(this)
163 if (gViewManagers
== nsnull
) {
164 NS_ASSERTION(mVMCount
== 0, "View Manager count is incorrect");
165 // Create an array to hold a list of view managers
166 gViewManagers
= new nsVoidArray
;
169 if (gCleanupContext
== nsnull
) {
170 /* XXX: This should use a device to create a matching |nsIRenderingContext| object */
171 CallCreateInstance(kRenderingContextCID
, &gCleanupContext
);
172 NS_ASSERTION(gCleanupContext
,
173 "Wasn't able to create a graphics context for cleanup");
176 gViewManagers
->AppendElement(this);
178 if (++mVMCount
== 1) {
179 NS_AddFocusSuppressorCallback(&nsViewManager::SuppressFocusEvents
);
181 // NOTE: we use a zeroing operator new, so all data members are
182 // assumed to be cleared here.
183 mDefaultBackgroundColor
= NS_RGBA(0, 0, 0, 0);
184 mHasPendingUpdates
= PR_FALSE
;
185 mRecursiveRefreshPending
= PR_FALSE
;
186 mUpdateBatchFlags
= 0;
189 nsViewManager::~nsViewManager()
192 // Destroy any remaining views
193 mRootView
->Destroy();
197 // Make sure to revoke pending events for all viewmanagers, since some events
198 // are posted by a non-root viewmanager.
199 mInvalidateEvent
.Revoke();
200 mSynthMouseMoveEvent
.Revoke();
203 // We have a strong ref to mRootViewManager
204 NS_RELEASE(mRootViewManager
);
207 mRootScrollable
= nsnull
;
209 NS_ASSERTION((mVMCount
> 0), "underflow of viewmanagers");
215 gViewManagers
->RemoveElement(this);
216 NS_ASSERTION(removed
, "Viewmanager instance not was not in the global list of viewmanagers");
219 // There aren't any more view managers so
220 // release the global array of view managers
222 NS_ASSERTION(gViewManagers
!= nsnull
, "About to delete null gViewManagers");
223 delete gViewManagers
;
224 gViewManagers
= nsnull
;
226 // Cleanup all of the offscreen drawing surfaces if the last view manager
227 // has been destroyed and there is something to cleanup
229 // Note: A global rendering context is needed because it is not possible
230 // to create a nsIRenderingContext during the shutdown of XPCOM. The last
231 // viewmanager is typically destroyed during XPCOM shutdown.
232 NS_IF_RELEASE(gCleanupContext
);
239 NS_IMPL_ISUPPORTS1(nsViewManager
, nsIViewManager
)
242 nsViewManager::CreateRegion(nsIRegion
* *result
)
246 if (!mRegionFactory
) {
247 mRegionFactory
= do_GetClassObject(kRegionCID
, &rv
);
254 nsIRegion
* region
= nsnull
;
255 rv
= CallCreateInstance(mRegionFactory
.get(), ®ion
);
256 if (NS_SUCCEEDED(rv
)) {
263 // We don't hold a reference to the presentation context because it
264 // holds a reference to us.
265 NS_IMETHODIMP
nsViewManager::Init(nsIDeviceContext
* aContext
)
267 NS_PRECONDITION(nsnull
!= aContext
, "null ptr");
269 if (nsnull
== aContext
) {
270 return NS_ERROR_NULL_POINTER
;
272 if (nsnull
!= mContext
) {
273 return NS_ERROR_ALREADY_INITIALIZED
;
277 mRefreshEnabled
= PR_TRUE
;
279 mMouseGrabber
= nsnull
;
284 NS_IMETHODIMP_(nsIView
*)
285 nsViewManager::CreateView(const nsRect
& aBounds
,
286 const nsIView
* aParent
,
287 nsViewVisibility aVisibilityFlag
)
289 nsView
*v
= new nsView(this, aVisibilityFlag
);
291 v
->SetPosition(aBounds
.x
, aBounds
.y
);
292 nsRect
dim(0, 0, aBounds
.width
, aBounds
.height
);
293 v
->SetDimensions(dim
, PR_FALSE
);
294 v
->SetParent(static_cast<nsView
*>(const_cast<nsIView
*>(aParent
)));
299 NS_IMETHODIMP_(nsIScrollableView
*)
300 nsViewManager::CreateScrollableView(const nsRect
& aBounds
,
301 const nsIView
* aParent
)
303 nsScrollPortView
*v
= new nsScrollPortView(this);
305 v
->SetPosition(aBounds
.x
, aBounds
.y
);
306 nsRect
dim(0, 0, aBounds
.width
, aBounds
.height
);
307 v
->SetDimensions(dim
, PR_FALSE
);
308 v
->SetParent(static_cast<nsView
*>(const_cast<nsIView
*>(aParent
)));
313 NS_IMETHODIMP
nsViewManager::GetRootView(nsIView
*&aView
)
319 NS_IMETHODIMP
nsViewManager::SetRootView(nsIView
*aView
)
321 nsView
* view
= static_cast<nsView
*>(aView
);
323 NS_PRECONDITION(!view
|| view
->GetViewManager() == this,
324 "Unexpected viewmanager on root view");
326 // Do NOT destroy the current root view. It's the caller's responsibility
331 nsView
* parent
= mRootView
->GetParent();
333 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
334 // no need to set mRootViewManager ourselves here.
335 parent
->InsertChild(mRootView
, nsnull
);
337 InvalidateHierarchy();
340 mRootView
->SetZIndex(PR_FALSE
, 0, PR_FALSE
);
342 // Else don't touch mRootViewManager
347 NS_IMETHODIMP
nsViewManager::GetWindowDimensions(nscoord
*aWidth
, nscoord
*aHeight
)
349 if (nsnull
!= mRootView
) {
350 if (mDelayedResize
== nsSize(NSCOORD_NONE
, NSCOORD_NONE
)) {
352 mRootView
->GetDimensions(dim
);
354 *aHeight
= dim
.height
;
356 *aWidth
= mDelayedResize
.width
;
357 *aHeight
= mDelayedResize
.height
;
368 NS_IMETHODIMP
nsViewManager::SetWindowDimensions(nscoord aWidth
, nscoord aHeight
)
371 if (IsViewVisible(mRootView
)) {
372 mDelayedResize
.SizeTo(NSCOORD_NONE
, NSCOORD_NONE
);
373 DoSetWindowDimensions(aWidth
, aHeight
);
375 mDelayedResize
.SizeTo(aWidth
, aHeight
);
382 NS_IMETHODIMP
nsViewManager::FlushDelayedResize()
384 if (mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
)) {
385 DoSetWindowDimensions(mDelayedResize
.width
, mDelayedResize
.height
);
386 mDelayedResize
.SizeTo(NSCOORD_NONE
, NSCOORD_NONE
);
391 static void ConvertNativeRegionToAppRegion(nsIRegion
* aIn
, nsRegion
* aOut
,
392 nsIDeviceContext
* context
)
394 nsRegionRectSet
* rects
= nsnull
;
395 aIn
->GetRects(&rects
);
399 PRInt32 p2a
= context
->AppUnitsPerDevPixel();
402 for (i
= 0; i
< rects
->mNumRects
; i
++) {
403 const nsRegionRect
& inR
= rects
->mRects
[i
];
405 outR
.x
= NSIntPixelsToAppUnits(inR
.x
, p2a
);
406 outR
.y
= NSIntPixelsToAppUnits(inR
.y
, p2a
);
407 outR
.width
= NSIntPixelsToAppUnits(inR
.width
, p2a
);
408 outR
.height
= NSIntPixelsToAppUnits(inR
.height
, p2a
);
409 aOut
->Or(*aOut
, outR
);
412 aIn
->FreeRects(rects
);
415 static nsView
* GetDisplayRootFor(nsView
* aView
)
417 nsView
*displayRoot
= aView
;
419 nsView
*displayParent
= displayRoot
->GetParent();
423 if (displayRoot
->GetFloating() && !displayParent
->GetFloating())
425 displayRoot
= displayParent
;
430 aRegion is given in device coordinates!!
432 void nsViewManager::Refresh(nsView
*aView
, nsIRenderingContext
*aContext
,
433 nsIRegion
*aRegion
, PRUint32 aUpdateFlags
)
435 NS_ASSERTION(aRegion
!= nsnull
, "Null aRegion");
437 if (! IsRefreshEnabled())
441 aView
->GetDimensions(viewRect
);
442 nsPoint vtowoffset
= aView
->ViewToWidgetOffset();
444 // damageRegion is the damaged area, in twips, relative to the view origin
445 nsRegion damageRegion
;
446 // convert pixels-relative-to-widget-origin to twips-relative-to-widget-origin
447 ConvertNativeRegionToAppRegion(aRegion
, &damageRegion
, mContext
);
448 // move it from widget coordinates into view coordinates
449 damageRegion
.MoveBy(viewRect
.TopLeft() - vtowoffset
);
451 if (damageRegion
.IsEmpty()) {
453 nsRect damageRect
= damageRegion
.GetBounds();
454 printf("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
455 damageRect
.x
, damageRect
.y
, damageRect
.width
, damageRect
.height
,
456 viewRect
.x
, viewRect
.y
, viewRect
.width
, viewRect
.height
);
461 #ifdef NS_VM_PERF_METRICS
462 MOZ_TIMER_DEBUGLOG(("Reset nsViewManager::Refresh(region), this=%p\n", this));
463 MOZ_TIMER_RESET(mWatch
);
465 MOZ_TIMER_DEBUGLOG(("Start: nsViewManager::Refresh(region)\n"));
466 MOZ_TIMER_START(mWatch
);
469 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
471 RootViewManager()->mRecursiveRefreshPending
= PR_TRUE
;
476 nsAutoScriptBlocker scriptBlocker
;
477 SetPainting(PR_TRUE
);
479 nsCOMPtr
<nsIRenderingContext
> localcx
;
480 NS_ASSERTION(aView
->GetWidget(),
481 "Must have a widget to calculate coordinates correctly");
482 if (nsnull
== aContext
)
484 localcx
= CreateRenderingContext(*aView
);
486 //couldn't get rendering context. this is ok at init time atleast
487 if (nsnull
== localcx
) {
488 SetPainting(PR_FALSE
);
492 // plain assignment grabs another reference.
496 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
498 nsRefPtr
<gfxContext
> ctx
= localcx
->ThebesContext();
502 ctx
->Translate(gfxPoint(gfxFloat(vtowoffset
.x
) / p2a
,
503 gfxFloat(vtowoffset
.y
) / p2a
));
505 ctx
->Translate(gfxPoint(-gfxFloat(viewRect
.x
) / p2a
,
506 -gfxFloat(viewRect
.y
) / p2a
));
508 nsRegion opaqueRegion
;
509 AddCoveringWidgetsToOpaqueRegion(opaqueRegion
, mContext
, aView
);
510 damageRegion
.Sub(damageRegion
, opaqueRegion
);
512 RenderViews(aView
, *localcx
, damageRegion
);
516 SetPainting(PR_FALSE
);
519 if (RootViewManager()->mRecursiveRefreshPending
) {
520 // Unset this flag first, since if aUpdateFlags includes NS_VMREFRESH_IMMEDIATE
521 // we'll reenter this code from the UpdateAllViews call.
522 RootViewManager()->mRecursiveRefreshPending
= PR_FALSE
;
523 UpdateAllViews(aUpdateFlags
);
526 #ifdef NS_VM_PERF_METRICS
527 MOZ_TIMER_DEBUGLOG(("Stop: nsViewManager::Refresh(region), this=%p\n", this));
528 MOZ_TIMER_STOP(mWatch
);
529 MOZ_TIMER_LOG(("vm2 Paint time (this=%p): ", this));
530 MOZ_TIMER_PRINT(mWatch
);
535 // aRect is in app units and relative to the top-left of the aView->GetWidget()
536 void nsViewManager::DefaultRefresh(nsView
* aView
, nsIRenderingContext
*aContext
, const nsRect
* aRect
)
538 NS_PRECONDITION(aView
, "Must have a view to work with!");
539 nsIWidget
* widget
= aView
->GetNearestWidget(nsnull
);
543 nsCOMPtr
<nsIRenderingContext
> context
= aContext
;
545 context
= CreateRenderingContext(*aView
);
550 nscolor bgcolor
= mDefaultBackgroundColor
;
552 if (NS_GET_A(mDefaultBackgroundColor
) == 0) {
553 NS_WARNING("nsViewManager: Asked to paint a default background, but no default background color is set!");
557 context
->SetColor(bgcolor
);
558 context
->FillRect(*aRect
);
561 void nsViewManager::AddCoveringWidgetsToOpaqueRegion(nsRegion
&aRgn
, nsIDeviceContext
* aContext
,
563 NS_PRECONDITION(aRootView
, "Must have root view");
565 // We accumulate the bounds of widgets obscuring aRootView's widget into opaqueRgn.
566 // In OptimizeDisplayList, display list elements which lie behind obscuring native
567 // widgets are dropped.
568 // This shouldn't really be necessary, since the GFX/Widget toolkit should remove these
569 // covering widgets from the clip region passed into the paint command. But right now
570 // they only give us a paint rect and not a region, so we can't access that information.
571 // It's important to identifying areas that are covered by native widgets to avoid
572 // painting their views many times as we process invalidates from the root widget all the
573 // way down to the nested widgets.
575 // NB: we must NOT add widgets that correspond to floating views!
576 // We may be required to paint behind them
579 nsIWidget
* widget
= aRootView
->GetNearestWidget(nsnull
);
584 for (nsIWidget
* childWidget
= widget
->GetFirstChild();
586 childWidget
= childWidget
->GetNextSibling()) {
587 PRBool widgetVisible
;
588 childWidget
->IsVisible(widgetVisible
);
590 nsView
* view
= nsView::GetViewFor(childWidget
);
591 if (view
&& view
->GetVisibility() == nsViewVisibility_kShow
592 && !view
->GetFloating()) {
593 nsRect bounds
= view
->GetBounds();
594 if (bounds
.width
> 0 && bounds
.height
> 0) {
595 nsView
* viewParent
= view
->GetParent();
597 while (viewParent
&& viewParent
!= aRootView
) {
598 viewParent
->ConvertToParentCoords(&bounds
.x
, &bounds
.y
);
599 viewParent
= viewParent
->GetParent();
602 // maybe we couldn't get the view into the coordinate
603 // system of aRootView (maybe it's not a descendant
604 // view of aRootView?); if so, don't use it
606 aRgn
.Or(aRgn
, bounds
);
614 // aRC and aRegion are in view coordinates
615 void nsViewManager::RenderViews(nsView
*aView
, nsIRenderingContext
& aRC
,
616 const nsRegion
& aRegion
)
619 nsView
* displayRoot
= GetDisplayRootFor(aView
);
620 nsPoint offsetToRoot
= aView
->GetOffsetTo(displayRoot
);
621 nsRegion
damageRegion(aRegion
);
622 damageRegion
.MoveBy(offsetToRoot
);
625 aRC
.Translate(-offsetToRoot
.x
, -offsetToRoot
.y
);
626 mObserver
->Paint(displayRoot
, &aRC
, damageRegion
);
631 void nsViewManager::ProcessPendingUpdates(nsView
* aView
, PRBool aDoInvalidate
)
633 NS_ASSERTION(IsRootVM(), "Updates will be missed");
635 // Protect against a null-view.
640 if (aView
->HasWidget()) {
641 aView
->ResetWidgetBounds(PR_FALSE
, PR_FALSE
, PR_TRUE
);
644 // process pending updates in child view.
645 for (nsView
* childView
= aView
->GetFirstChild(); childView
;
646 childView
= childView
->GetNextSibling()) {
647 ProcessPendingUpdates(childView
, aDoInvalidate
);
650 if (aDoInvalidate
&& aView
->HasNonEmptyDirtyRegion()) {
651 // Push out updates after we've processed the children; ensures that
652 // damage is applied based on the final widget geometry
653 NS_ASSERTION(mRefreshEnabled
, "Cannot process pending updates with refresh disabled");
654 nsRegion
* dirtyRegion
= aView
->GetDirtyRegion();
656 UpdateWidgetArea(aView
, *dirtyRegion
, nsnull
);
657 dirtyRegion
->SetEmpty();
662 NS_IMETHODIMP
nsViewManager::Composite()
665 return RootViewManager()->Composite();
668 if (UpdateCount() > 0)
677 NS_IMETHODIMP
nsViewManager::UpdateView(nsIView
*aView
, PRUint32 aUpdateFlags
)
679 // Mark the entire view as damaged
680 nsView
* view
= static_cast<nsView
*>(aView
);
682 nsRect bounds
= view
->GetBounds();
683 view
->ConvertFromParentCoords(&bounds
.x
, &bounds
.y
);
684 return UpdateView(view
, bounds
, aUpdateFlags
);
687 // This method accumulates the intersectons of all dirty regions attached to
688 // descendants of aSourceView with the cliprect of aTargetView into the dirty
689 // region of aTargetView, after offseting said intersections by aOffset.
691 AccumulateIntersectionsIntoDirtyRegion(nsView
* aTargetView
,
693 const nsPoint
& aOffset
)
695 if (aSourceView
->HasNonEmptyDirtyRegion()) {
696 // In most cases, aSourceView is an ancestor of aTargetView, since most
697 // commonly we have dirty rects on the root view.
698 nsPoint offset
= aTargetView
->GetOffsetTo(aSourceView
);
699 nsRegion intersection
;
700 intersection
= *aSourceView
->GetDirtyRegion();
701 if (!intersection
.IsEmpty()) {
702 nsRegion
* targetRegion
= aTargetView
->GetDirtyRegion();
704 intersection
.MoveBy(-offset
+ aOffset
);
705 targetRegion
->Or(*targetRegion
, intersection
);
706 // Random simplification number...
707 targetRegion
->SimplifyOutward(20);
712 if (aSourceView
== aTargetView
) {
713 // No need to do this with kids of aTargetView
717 for (nsView
* kid
= aSourceView
->GetFirstChild();
719 kid
= kid
->GetNextSibling()) {
720 AccumulateIntersectionsIntoDirtyRegion(aTargetView
, kid
, aOffset
);
725 nsViewManager::WillBitBlit(nsView
* aView
, nsPoint aScrollAmount
)
728 RootViewManager()->WillBitBlit(aView
, aScrollAmount
);
732 NS_PRECONDITION(aView
, "Must have a view");
733 NS_PRECONDITION(!aView
->NeedsInvalidateFrameOnScroll(), "We shouldn't be BitBlting.");
734 NS_PRECONDITION(aView
->HasWidget(), "View must have a widget");
738 // Since the view is actually moving the widget by -aScrollAmount, that's the
739 // offset we want to use when accumulating dirty rects.
740 AccumulateIntersectionsIntoDirtyRegion(aView
, GetRootView(), -aScrollAmount
);
744 // Invalidate all widgets which overlap the view, other than the view's own widgets.
746 nsViewManager::UpdateViewAfterScroll(nsView
*aView
, const nsRegion
& aUpdateRegion
)
748 NS_ASSERTION(RootViewManager()->mScrollCnt
> 0,
749 "Someone forgot to call WillBitBlit()");
750 // Look at the view's clipped rect. It may be that part of the view is clipped out
751 // in which case we don't need to worry about invalidating the clipped-out part.
752 nsRect damageRect
= aView
->GetDimensions();
753 if (damageRect
.IsEmpty()) {
754 // Don't forget to undo mScrollCnt!
755 --RootViewManager()->mScrollCnt
;
759 nsView
* displayRoot
= GetDisplayRootFor(aView
);
760 nsPoint offset
= aView
->GetOffsetTo(displayRoot
);
761 damageRect
.MoveBy(offset
);
763 UpdateWidgetArea(displayRoot
, nsRegion(damageRect
), aView
);
764 if (!aUpdateRegion
.IsEmpty()) {
765 // XXX We should update the region, not the bounds rect, but that requires
766 // a little more work. Fix this when we reshuffle this code.
767 nsRegion
update(aUpdateRegion
);
768 update
.MoveBy(offset
);
769 UpdateWidgetArea(displayRoot
, update
, nsnull
);
770 // FlushPendingInvalidates();
774 --RootViewManager()->mScrollCnt
;
778 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
779 * every widget child of aWidgetView, plus aWidgetView's own widget
780 * @param aIgnoreWidgetView if non-null, the aIgnoreWidgetView's widget and its
781 * children are not updated.
784 nsViewManager::UpdateWidgetArea(nsView
*aWidgetView
, const nsRegion
&aDamagedRegion
,
785 nsView
* aIgnoreWidgetView
)
787 if (!IsRefreshEnabled()) {
788 // accumulate this rectangle in the view's dirty region, so we can
790 nsRegion
* dirtyRegion
= aWidgetView
->GetDirtyRegion();
791 if (!dirtyRegion
) return;
793 dirtyRegion
->Or(*dirtyRegion
, aDamagedRegion
);
794 // Don't let dirtyRegion grow beyond 8 rects
795 dirtyRegion
->SimplifyOutward(8);
796 nsViewManager
* rootVM
= RootViewManager();
797 rootVM
->mHasPendingUpdates
= PR_TRUE
;
798 rootVM
->IncrementUpdateCount();
800 // this should only happen at the top level, and this result
801 // should not be consumed by top-level callers, so it doesn't
802 // really matter what we return
805 // If the bounds don't overlap at all, there's nothing to do
806 nsRegion intersection
;
807 intersection
.And(aWidgetView
->GetDimensions(), aDamagedRegion
);
808 if (intersection
.IsEmpty()) {
812 // If the widget is hidden, it don't cover nothing
813 if (nsViewVisibility_kHide
== aWidgetView
->GetVisibility()) {
815 // Assert if view is hidden but widget is visible
816 nsIWidget
* widget
= aWidgetView
->GetNearestWidget(nsnull
);
819 widget
->IsVisible(visible
);
820 NS_ASSERTION(!visible
, "View is hidden but widget is visible!");
826 if (aWidgetView
== aIgnoreWidgetView
) {
827 // the widget for aIgnoreWidgetView (and its children) should be treated as already updated.
831 nsIWidget
* widget
= aWidgetView
->GetNearestWidget(nsnull
);
833 // The root view or a scrolling view might not have a widget
834 // (for example, during printing). We get here when we scroll
835 // during printing to show selected options in a listbox, for example.
839 // Update all child widgets with the damage. In the process,
840 // accumulate the union of all the child widget areas, or at least
841 // some subset of that.
843 for (nsIWidget
* childWidget
= widget
->GetFirstChild();
845 childWidget
= childWidget
->GetNextSibling()) {
846 nsView
* view
= nsView::GetViewFor(childWidget
);
847 NS_ASSERTION(view
!= aWidgetView
, "will recur infinitely");
848 if (view
&& view
->GetVisibility() == nsViewVisibility_kShow
) {
849 // Don't mess with views that are in completely different view
851 if (view
->GetViewManager()->RootViewManager() == RootViewManager()) {
852 // get the damage region into 'view's coordinate system
853 nsRegion damage
= intersection
;
854 nsPoint offset
= view
->GetOffsetTo(aWidgetView
);
855 damage
.MoveBy(-offset
);
856 UpdateWidgetArea(view
, damage
, aIgnoreWidgetView
);
857 children
.Or(children
, view
->GetDimensions() + offset
);
858 children
.SimplifyInward(20);
864 leftOver
.Sub(intersection
, children
);
866 if (!leftOver
.IsEmpty()) {
867 NS_ASSERTION(IsRefreshEnabled(), "Can only get here with refresh enabled, I hope");
870 for (nsRegionRectIterator
iter(leftOver
); (r
= iter
.Next());) {
872 ViewToWidget(aWidgetView
, aWidgetView
, bounds
);
873 widget
->Invalidate(bounds
, PR_FALSE
);
878 NS_IMETHODIMP
nsViewManager::UpdateView(nsIView
*aView
, const nsRect
&aRect
, PRUint32 aUpdateFlags
)
880 NS_PRECONDITION(nsnull
!= aView
, "null view");
882 nsView
* view
= static_cast<nsView
*>(aView
);
884 nsRect
damagedRect(aRect
);
886 // If the rectangle is not visible then abort
887 // without invalidating. This is a performance
888 // enhancement since invalidating a native widget
890 // This also checks for silly request like damagedRect.width = 0 or damagedRect.height = 0
891 nsRectVisibility rectVisibility
;
892 GetRectVisibility(view
, damagedRect
, 0, &rectVisibility
);
893 if (rectVisibility
!= nsRectVisibility_kVisible
) {
897 nsView
* displayRoot
= GetDisplayRootFor(view
);
898 // Propagate the update to the displayRoot, since iframes, for example,
899 // can overlap each other and be translucent. So we have to possibly
900 // invalidate our rect in each of the widgets we have lying about.
901 damagedRect
.MoveBy(view
->GetOffsetTo(displayRoot
));
902 UpdateWidgetArea(displayRoot
, nsRegion(damagedRect
), nsnull
);
904 RootViewManager()->IncrementUpdateCount();
906 if (!IsRefreshEnabled()) {
910 // See if we should do an immediate refresh or wait
911 if (aUpdateFlags
& NS_VMREFRESH_IMMEDIATE
) {
918 NS_IMETHODIMP
nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags
)
920 if (RootViewManager() != this) {
921 return RootViewManager()->UpdateAllViews(aUpdateFlags
);
924 UpdateViews(mRootView
, aUpdateFlags
);
928 void nsViewManager::UpdateViews(nsView
*aView
, PRUint32 aUpdateFlags
)
931 UpdateView(aView
, aUpdateFlags
);
933 // update all children as well.
934 nsView
* childView
= aView
->GetFirstChild();
935 while (nsnull
!= childView
) {
936 UpdateViews(childView
, aUpdateFlags
);
937 childView
= childView
->GetNextSibling();
941 nsView
*nsViewManager::sCurrentlyFocusView
= nsnull
;
942 nsView
*nsViewManager::sViewFocusedBeforeSuppression
= nsnull
;
943 PRBool
nsViewManager::sFocusSuppressed
= PR_FALSE
;
945 void nsViewManager::SuppressFocusEvents(PRBool aSuppress
)
948 sFocusSuppressed
= PR_TRUE
;
949 SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView());
953 sFocusSuppressed
= PR_FALSE
;
954 if (GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression()) {
958 // We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS.
959 nsIWidget
*widget
= nsnull
;
960 nsEventStatus status
;
962 // Backup what is focused before we send the blur. If the
963 // blur causes a focus change, keep that new focus change,
964 // don't overwrite with the old "currently focused view".
965 nsIView
*currentFocusBeforeBlur
= GetCurrentlyFocusedView();
967 // Send NS_LOSTFOCUS to widget that was focused before
968 // focus/blur suppression.
969 if (GetViewFocusedBeforeSuppression()) {
970 widget
= GetViewFocusedBeforeSuppression()->GetWidget();
972 #ifdef DEBUG_FOCUS_SUPPRESSION
973 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_LOSTFOCUS\n");
975 nsGUIEvent
event(PR_TRUE
, NS_LOSTFOCUS
, widget
);
976 widget
->DispatchEvent(&event
, status
);
980 // Send NS_GOTFOCUS to the widget that we think should be focused.
981 if (GetCurrentlyFocusedView() &&
982 currentFocusBeforeBlur
== GetCurrentlyFocusedView())
984 widget
= GetCurrentlyFocusedView()->GetWidget();
986 #ifdef DEBUG_FOCUS_SUPPRESSION
987 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_GOTFOCUS\n");
989 nsGUIEvent
event(PR_TRUE
, NS_GOTFOCUS
, widget
);
990 widget
->DispatchEvent(&event
, status
);
996 static void ConvertRectAppUnitsToIntPixels(nsRect
& aRect
, PRInt32 p2a
)
998 aRect
.x
= NSAppUnitsToIntPixels(aRect
.x
, p2a
);
999 aRect
.y
= NSAppUnitsToIntPixels(aRect
.y
, p2a
);
1000 aRect
.width
= NSAppUnitsToIntPixels(aRect
.width
, p2a
);
1001 aRect
.height
= NSAppUnitsToIntPixels(aRect
.height
, p2a
);
1004 NS_IMETHODIMP
nsViewManager::DispatchEvent(nsGUIEvent
*aEvent
, nsEventStatus
*aStatus
)
1006 *aStatus
= nsEventStatus_eIgnore
;
1008 switch(aEvent
->message
)
1012 nsView
* view
= nsView::GetViewFor(aEvent
->widget
);
1016 nscoord width
= ((nsSizeEvent
*)aEvent
)->windowSize
->width
;
1017 nscoord height
= ((nsSizeEvent
*)aEvent
)->windowSize
->height
;
1018 width
= ((nsSizeEvent
*)aEvent
)->mWinWidth
;
1019 height
= ((nsSizeEvent
*)aEvent
)->mWinHeight
;
1021 // The root view may not be set if this is the resize associated with
1024 if (view
== mRootView
)
1026 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1027 SetWindowDimensions(NSIntPixelsToAppUnits(width
, p2a
),
1028 NSIntPixelsToAppUnits(height
, p2a
));
1029 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1038 nsPaintEvent
*event
= static_cast<nsPaintEvent
*>(aEvent
);
1039 nsView
*view
= nsView::GetViewFor(aEvent
->widget
);
1041 if (!view
|| !mContext
)
1044 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1046 // The rect is in device units, and it's in the coordinate space of its
1047 // associated window.
1048 nsCOMPtr
<nsIRegion
> region
= event
->region
;
1050 if (NS_FAILED(CreateRegion(getter_AddRefs(region
))))
1053 const nsRect
& damrect
= *event
->rect
;
1054 region
->SetTo(damrect
.x
, damrect
.y
, damrect
.width
, damrect
.height
);
1057 if (region
->IsEmpty())
1061 if (IsRefreshEnabled()) {
1062 // If an ancestor widget was hidden and then shown, we could
1063 // have a delayed resize to handle.
1064 PRBool didResize
= PR_FALSE
;
1065 for (nsViewManager
*vm
= this; vm
;
1066 vm
= vm
->mRootView
->GetParent()
1067 ? vm
->mRootView
->GetParent()->GetViewManager()
1069 if (vm
->mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
) &&
1070 IsViewVisible(vm
->mRootView
)) {
1071 vm
->FlushDelayedResize();
1074 vm
->UpdateView(vm
->mRootView
, NS_VMREFRESH_NO_SYNC
);
1075 didResize
= PR_TRUE
;
1077 // not sure if it's valid for us to claim that we
1078 // ignored this, but we're going to do so anyway, since
1079 // we didn't actually paint anything
1080 *aStatus
= nsEventStatus_eIgnore
;
1085 //NS_ASSERTION(IsViewVisible(view), "painting an invisible view");
1087 // Just notify our own view observer that we're about to paint
1088 // XXXbz do we need to notify other view observers for viewmanagers
1090 // Make sure to not send WillPaint notifications while scrolling
1091 nsRefPtr
<nsViewManager
> rootVM
= RootViewManager();
1093 nsIWidget
*widget
= mRootView
->GetWidget();
1094 PRBool transparentWindow
= PR_FALSE
;
1096 transparentWindow
= widget
->GetTransparencyMode() == eTransparencyTransparent
;
1098 if (rootVM
->mScrollCnt
== 0 && !transparentWindow
) {
1099 nsIViewObserver
* observer
= GetViewObserver();
1101 // Do an update view batch. Make sure not to do it DEFERRED,
1102 // since that would effectively delay any invalidates that are
1103 // triggered by the WillPaint notification (they'd happen when
1104 // the invalid event fires, which is later than the reflow
1105 // event would fire and could end up being after some timer
1106 // events, leading to frame dropping in DHTML). Note that the
1107 // observer may try to reenter this code from inside
1108 // WillPaint() by trying to do a synchronous paint, but since
1109 // refresh will be disabled it won't be able to do the paint.
1110 // We should really sort out the rules on our synch painting
1112 UpdateViewBatch
batch(this);
1113 observer
->WillPaint();
1114 batch
.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC
);
1116 // Get the view pointer again since the code above might have
1117 // destroyed it (bug 378273).
1118 view
= nsView::GetViewFor(aEvent
->widget
);
1121 // Make sure to sync up any widget geometry changes we
1122 // have pending before we paint.
1123 if (rootVM
->mHasPendingUpdates
) {
1124 rootVM
->ProcessPendingUpdates(mRootView
, PR_FALSE
);
1128 Refresh(view
, event
->renderingContext
, region
,
1129 NS_VMREFRESH_DOUBLE_BUFFER
);
1133 // since we got an NS_PAINT event, we need to
1134 // draw something so we don't get blank areas.
1136 region
->GetBoundingBox(&damRect
.x
, &damRect
.y
, &damRect
.width
, &damRect
.height
);
1137 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1138 damRect
.ScaleRoundOut(float(p2a
));
1139 DefaultRefresh(view
, event
->renderingContext
, &damRect
);
1141 // Clients like the editor can trigger multiple
1142 // reflows during what the user perceives as a single
1143 // edit operation, so it disables view manager
1144 // refreshing until the edit operation is complete
1145 // so that users don't see the intermediate steps.
1147 // Unfortunately some of these reflows can trigger
1148 // nsScrollPortView and nsScrollingView Scroll() calls
1149 // which in most cases force an immediate BitBlt and
1150 // synchronous paint to happen even if the view manager's
1151 // refresh is disabled. (Bug 97674)
1153 // Calling UpdateView() here, is necessary to add
1154 // the exposed region specified in the synchronous paint
1155 // event to the view's damaged region so that it gets
1156 // painted properly when refresh is enabled.
1158 // Note that calling UpdateView() here was deemed
1159 // to have the least impact on performance, since the
1160 // other alternative was to make Scroll() post an
1161 // async paint event for the *entire* ScrollPort or
1162 // ScrollingView's viewable area. (See bug 97674 for this
1163 // alternate patch.)
1165 UpdateView(view
, damRect
, NS_VMREFRESH_NO_SYNC
);
1175 /* Don't pass these events through. Passing them through
1176 causes performance problems on pages with lots of views/frames
1178 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1182 case NS_DISPLAYCHANGED
:
1184 //Destroy the cached backbuffer to force a new backbuffer
1185 //be constructed with the appropriate display depth.
1186 //@see bugzilla bug 6061
1187 *aStatus
= nsEventStatus_eConsumeDoDefault
;
1190 case NS_SYSCOLORCHANGED
:
1192 // Hold a refcount to the observer. The continued existence of the observer will
1193 // delay deletion of this view hierarchy should the event want to cause its
1194 // destruction in, say, some JavaScript event handler.
1195 nsView
*view
= nsView::GetViewFor(aEvent
->widget
);
1196 nsCOMPtr
<nsIViewObserver
> obs
= GetViewObserver();
1198 obs
->HandleEvent(view
, aEvent
, aStatus
);
1205 if (aEvent
->message
== NS_GOTFOCUS
) {
1206 #ifdef DEBUG_FOCUS_SUPPRESSION
1207 printf("*** 0 INFO TODO [CPEARCE] Focus changing%s\n",
1208 (nsViewManager::IsFocusSuppressed() ? " while suppressed" : ""));
1210 SetCurrentlyFocusedView(nsView::GetViewFor(aEvent
->widget
));
1212 if ((aEvent
->message
== NS_GOTFOCUS
|| aEvent
->message
== NS_LOSTFOCUS
) &&
1213 nsViewManager::IsFocusSuppressed())
1215 #ifdef DEBUG_FOCUS_SUPPRESSION
1216 printf("*** 0 INFO TODO [CPEARCE] Suppressing %s\n",
1217 (aEvent
->message
== NS_GOTFOCUS
? "NS_GOTFOCUS" : "NS_LOSTFOCUS"));
1222 if ((NS_IS_MOUSE_EVENT(aEvent
) &&
1223 // Ignore moves that we synthesize.
1224 static_cast<nsMouseEvent
*>(aEvent
)->reason
==
1225 nsMouseEvent::eReal
&&
1226 // Ignore mouse exit and enter (we'll get moves if the user
1227 // is really moving the mouse) since we get them when we
1228 // create and destroy widgets.
1229 aEvent
->message
!= NS_MOUSE_EXIT
&&
1230 aEvent
->message
!= NS_MOUSE_ENTER
) ||
1231 NS_IS_KEY_EVENT(aEvent
) ||
1232 NS_IS_IME_EVENT(aEvent
)) {
1233 gLastUserEventTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
1236 if (aEvent
->message
== NS_DEACTIVATE
) {
1238 GrabMouseEvents(nsnull
, result
);
1241 //Find the view whose coordinates system we're in.
1242 nsView
* baseView
= nsView::GetViewFor(aEvent
->widget
);
1243 nsView
* view
= baseView
;
1244 PRBool capturedEvent
= PR_FALSE
;
1246 if (!NS_IS_KEY_EVENT(aEvent
) && !NS_IS_IME_EVENT(aEvent
) &&
1247 !NS_IS_CONTEXT_MENU_KEY(aEvent
) && !NS_IS_FOCUS_EVENT(aEvent
) &&
1248 !NS_IS_QUERY_CONTENT_EVENT(aEvent
) &&
1249 aEvent
->eventStructType
!= NS_ACCESSIBLE_EVENT
) {
1250 // will dispatch using coordinates. Pretty bogus but it's consistent
1251 // with what presshell does.
1252 view
= GetDisplayRootFor(baseView
);
1255 //Find the view to which we're initially going to send the event
1257 if (NS_IS_MOUSE_EVENT(aEvent
) || NS_IS_DRAG_EVENT(aEvent
)) {
1258 nsView
* mouseGrabber
= GetMouseEventGrabber();
1260 view
= mouseGrabber
;
1261 capturedEvent
= PR_TRUE
;
1265 if (nsnull
!= view
) {
1266 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1268 if ((aEvent
->message
== NS_MOUSE_MOVE
&&
1269 static_cast<nsMouseEvent
*>(aEvent
)->reason
==
1270 nsMouseEvent::eReal
) ||
1271 aEvent
->message
== NS_MOUSE_ENTER
||
1272 aEvent
->message
== NS_MOUSE_BUTTON_DOWN
||
1273 aEvent
->message
== NS_MOUSE_BUTTON_UP
) {
1274 // aEvent->point is relative to the widget, i.e. the view top-left,
1275 // so we need to add the offset to the view origin
1276 nsPoint rootOffset
= baseView
->GetDimensions().TopLeft();
1277 rootOffset
+= baseView
->GetOffsetTo(RootViewManager()->mRootView
);
1278 RootViewManager()->mMouseLocation
= aEvent
->refPoint
+
1279 nsPoint(NSAppUnitsToIntPixels(rootOffset
.x
, p2a
),
1280 NSAppUnitsToIntPixels(rootOffset
.y
, p2a
));
1281 #ifdef DEBUG_MOUSE_LOCATION
1282 if (aEvent
->message
== NS_MOUSE_ENTER
)
1283 printf("[vm=%p]got mouse enter for %p\n",
1284 this, aEvent
->widget
);
1285 printf("[vm=%p]setting mouse location to (%d,%d)\n",
1286 this, mMouseLocation
.x
, mMouseLocation
.y
);
1288 if (aEvent
->message
== NS_MOUSE_ENTER
)
1289 SynthesizeMouseMove(PR_FALSE
);
1290 } else if (aEvent
->message
== NS_MOUSE_EXIT
) {
1291 // Although we only care about the mouse moving into an area
1292 // for which this view manager doesn't receive mouse move
1293 // events, we don't check which view the mouse exit was for
1294 // since this seems to vary by platform. Hopefully this
1295 // won't matter at all since we'll get the mouse move or
1296 // enter after the mouse exit when the mouse moves from one
1297 // of our widgets into another.
1298 RootViewManager()->mMouseLocation
= nsPoint(NSCOORD_NONE
, NSCOORD_NONE
);
1299 #ifdef DEBUG_MOUSE_LOCATION
1300 printf("[vm=%p]got mouse exit for %p\n",
1301 this, aEvent
->widget
);
1302 printf("[vm=%p]clearing mouse location\n",
1307 //Calculate the proper offset for the view we're going to
1308 nsPoint
offset(0, 0);
1310 if (view
!= baseView
) {
1311 //Get offset from root of baseView
1313 for (parent
= baseView
; parent
; parent
= parent
->GetParent())
1314 parent
->ConvertToParentCoords(&offset
.x
, &offset
.y
);
1316 //Subtract back offset from root of view
1317 for (parent
= view
; parent
; parent
= parent
->GetParent())
1318 parent
->ConvertFromParentCoords(&offset
.x
, &offset
.y
);
1321 // Dispatch the event
1322 nsRect baseViewDimensions
;
1323 if (baseView
!= nsnull
) {
1324 baseView
->GetDimensions(baseViewDimensions
);
1328 pt
.x
= baseViewDimensions
.x
+
1329 NSFloatPixelsToAppUnits(float(aEvent
->refPoint
.x
) + 0.5f
, p2a
);
1330 pt
.y
= baseViewDimensions
.y
+
1331 NSFloatPixelsToAppUnits(float(aEvent
->refPoint
.y
) + 0.5f
, p2a
);
1334 *aStatus
= HandleEvent(view
, pt
, aEvent
, capturedEvent
);
1337 // need to map the reply back into platform coordinates
1339 switch (aEvent
->message
) {
1341 ConvertRectAppUnitsToIntPixels(
1342 ((nsTextEvent
*)aEvent
)->theReply
.mCursorPosition
, p2a
);
1344 case NS_COMPOSITION_START
:
1345 case NS_COMPOSITION_QUERY
:
1346 ConvertRectAppUnitsToIntPixels(
1347 ((nsCompositionEvent
*)aEvent
)->theReply
.mCursorPosition
, p2a
);
1349 case NS_QUERY_CHARACTER_RECT
:
1350 case NS_QUERY_CARET_RECT
:
1351 ConvertRectAppUnitsToIntPixels(
1352 ((nsQueryContentEvent
*)aEvent
)->mReply
.mRect
, p2a
);
1364 nsEventStatus
nsViewManager::HandleEvent(nsView
* aView
, nsPoint aPoint
,
1365 nsGUIEvent
* aEvent
, PRBool aCaptured
) {
1366 //printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports,
1367 // event->message, event->point.x, event->point.y);
1369 // Hold a refcount to the observer. The continued existence of the observer will
1370 // delay deletion of this view hierarchy should the event want to cause its
1371 // destruction in, say, some JavaScript event handler.
1372 nsCOMPtr
<nsIViewObserver
> obs
= aView
->GetViewManager()->GetViewObserver();
1373 nsEventStatus status
= nsEventStatus_eIgnore
;
1375 obs
->HandleEvent(aView
, aEvent
, &status
);
1381 NS_IMETHODIMP
nsViewManager::GrabMouseEvents(nsIView
*aView
, PRBool
&aResult
)
1384 return RootViewManager()->GrabMouseEvents(aView
, aResult
);
1387 // Along with nsView::SetVisibility, we enforce that the mouse grabber
1388 // can never be a hidden view.
1389 if (aView
&& static_cast<nsView
*>(aView
)->GetVisibility()
1390 == nsViewVisibility_kHide
) {
1397 printf("capturing mouse events for view %x\n",aView
);
1399 printf("removing mouse capture from view %x\n",mMouseGrabber
);
1402 mMouseGrabber
= static_cast<nsView
*>(aView
);
1407 NS_IMETHODIMP
nsViewManager::GetMouseEventGrabber(nsIView
*&aView
)
1409 aView
= GetMouseEventGrabber();
1413 // Recursively reparent widgets if necessary
1415 void nsViewManager::ReparentChildWidgets(nsIView
* aView
, nsIWidget
*aNewWidget
)
1417 if (aView
->HasWidget()) {
1418 // Check to see if the parent widget is the
1419 // same as the new parent. If not then reparent
1420 // the widget, otherwise there is nothing more
1421 // to do for the view and its descendants
1422 nsIWidget
* widget
= aView
->GetWidget();
1423 nsIWidget
* parentWidget
= widget
->GetParent();
1424 // Toplevel widgets should not be reparented!
1425 if (parentWidget
&& parentWidget
!= aNewWidget
) {
1429 widget
->SetParent(aNewWidget
);
1430 NS_ASSERTION(NS_SUCCEEDED(rv
), "SetParent failed!");
1435 // Need to check each of the views children to see
1436 // if they have a widget and reparent it.
1438 nsView
* view
= static_cast<nsView
*>(aView
);
1439 for (nsView
*kid
= view
->GetFirstChild(); kid
; kid
= kid
->GetNextSibling()) {
1440 ReparentChildWidgets(kid
, aNewWidget
);
1444 // Reparent a view and its descendant views widgets if necessary
1446 void nsViewManager::ReparentWidgets(nsIView
* aView
, nsIView
*aParent
)
1448 NS_PRECONDITION(aParent
, "Must have a parent");
1449 NS_PRECONDITION(aView
, "Must have a view");
1451 // Quickly determine whether the view has pre-existing children or a
1452 // widget. In most cases the view will not have any pre-existing
1453 // children when this is called. Only in the case
1454 // where a view has been reparented by removing it from
1455 // a reinserting it into a new location in the view hierarchy do we
1456 // have to consider reparenting the existing widgets for the view and
1457 // it's descendants.
1458 nsView
* view
= static_cast<nsView
*>(aView
);
1459 if (view
->HasWidget() || view
->GetFirstChild()) {
1460 nsIWidget
* parentWidget
= aParent
->GetNearestWidget(nsnull
);
1462 ReparentChildWidgets(aView
, parentWidget
);
1465 NS_WARNING("Can not find a widget for the parent view");
1469 NS_IMETHODIMP
nsViewManager::InsertChild(nsIView
*aParent
, nsIView
*aChild
, nsIView
*aSibling
,
1472 nsView
* parent
= static_cast<nsView
*>(aParent
);
1473 nsView
* child
= static_cast<nsView
*>(aChild
);
1474 nsView
* sibling
= static_cast<nsView
*>(aSibling
);
1476 NS_PRECONDITION(nsnull
!= parent
, "null ptr");
1477 NS_PRECONDITION(nsnull
!= child
, "null ptr");
1478 NS_ASSERTION(sibling
== nsnull
|| sibling
->GetParent() == parent
,
1479 "tried to insert view with invalid sibling");
1480 NS_ASSERTION(!IsViewInserted(child
), "tried to insert an already-inserted view");
1482 if ((nsnull
!= parent
) && (nsnull
!= child
))
1484 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
1485 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
1488 if (nsnull
== aSibling
) {
1490 // insert at end of document order, i.e., before first view
1491 // this is the common case, by far
1492 parent
->InsertChild(child
, nsnull
);
1493 ReparentWidgets(child
, parent
);
1495 // insert at beginning of document order, i.e., after last view
1496 nsView
*kid
= parent
->GetFirstChild();
1497 nsView
*prev
= nsnull
;
1500 kid
= kid
->GetNextSibling();
1502 // prev is last view or null if there are no children
1503 parent
->InsertChild(child
, prev
);
1504 ReparentWidgets(child
, parent
);
1507 nsView
*kid
= parent
->GetFirstChild();
1508 nsView
*prev
= nsnull
;
1509 while (kid
&& sibling
!= kid
) {
1510 //get the next sibling view
1512 kid
= kid
->GetNextSibling();
1514 NS_ASSERTION(kid
!= nsnull
,
1515 "couldn't find sibling in child list");
1517 // insert after 'kid' in document order, i.e. before in view order
1518 parent
->InsertChild(child
, prev
);
1519 ReparentWidgets(child
, parent
);
1521 // insert before 'kid' in document order, i.e. after in view order
1522 parent
->InsertChild(child
, kid
);
1523 ReparentWidgets(child
, parent
);
1526 #else // don't keep consistent document order, but order things by z-index instead
1527 // essentially we're emulating the old InsertChild(parent, child, zindex)
1528 PRInt32 zIndex
= child
->GetZIndex();
1529 while (nsnull
!= kid
)
1531 PRInt32 idx
= kid
->GetZIndex();
1533 if (CompareZIndex(zIndex
, child
->IsTopMost(), child
->GetZIndexIsAuto(),
1534 idx
, kid
->IsTopMost(), kid
->GetZIndexIsAuto()) >= 0)
1538 kid
= kid
->GetNextSibling();
1541 parent
->InsertChild(child
, prev
);
1542 ReparentWidgets(child
, parent
);
1545 // if the parent view is marked as "floating", make the newly added view float as well.
1546 if (parent
->GetFloating())
1547 child
->SetFloating(PR_TRUE
);
1549 //and mark this area as dirty if the view is visible...
1551 if (nsViewVisibility_kHide
!= child
->GetVisibility())
1552 UpdateView(child
, NS_VMREFRESH_NO_SYNC
);
1557 NS_IMETHODIMP
nsViewManager::InsertChild(nsIView
*aParent
, nsIView
*aChild
, PRInt32 aZIndex
)
1559 // no-one really calls this with anything other than aZIndex == 0 on a fresh view
1560 // XXX this method should simply be eliminated and its callers redirected to the real method
1561 SetViewZIndex(aChild
, PR_FALSE
, aZIndex
, PR_FALSE
);
1562 return InsertChild(aParent
, aChild
, nsnull
, PR_TRUE
);
1565 NS_IMETHODIMP
nsViewManager::RemoveChild(nsIView
*aChild
)
1567 nsView
* child
= static_cast<nsView
*>(aChild
);
1568 NS_ENSURE_ARG_POINTER(child
);
1570 nsView
* parent
= child
->GetParent();
1572 if (nsnull
!= parent
)
1574 UpdateView(child
, NS_VMREFRESH_NO_SYNC
);
1575 parent
->RemoveChild(child
);
1581 NS_IMETHODIMP
nsViewManager::MoveViewBy(nsIView
*aView
, nscoord aX
, nscoord aY
)
1583 nsView
* view
= static_cast<nsView
*>(aView
);
1585 nsPoint pt
= view
->GetPosition();
1586 MoveViewTo(view
, aX
+ pt
.x
, aY
+ pt
.y
);
1590 NS_IMETHODIMP
nsViewManager::MoveViewTo(nsIView
*aView
, nscoord aX
, nscoord aY
)
1592 nsView
* view
= static_cast<nsView
*>(aView
);
1593 nsPoint oldPt
= view
->GetPosition();
1594 nsRect oldArea
= view
->GetBounds();
1595 view
->SetPosition(aX
, aY
);
1597 // only do damage control if the view is visible
1599 if ((aX
!= oldPt
.x
) || (aY
!= oldPt
.y
)) {
1600 if (view
->GetVisibility() != nsViewVisibility_kHide
) {
1601 nsView
* parentView
= view
->GetParent();
1602 UpdateView(parentView
, oldArea
, NS_VMREFRESH_NO_SYNC
);
1603 UpdateView(parentView
, view
->GetBounds(), NS_VMREFRESH_NO_SYNC
);
1609 void nsViewManager::InvalidateHorizontalBandDifference(nsView
*aView
, const nsRect
& aRect
, const nsRect
& aCutOut
,
1610 PRUint32 aUpdateFlags
, nscoord aY1
, nscoord aY2
, PRBool aInCutOut
) {
1611 nscoord height
= aY2
- aY1
;
1612 if (aRect
.x
< aCutOut
.x
) {
1613 nsRect
r(aRect
.x
, aY1
, aCutOut
.x
- aRect
.x
, height
);
1614 UpdateView(aView
, r
, aUpdateFlags
);
1616 if (!aInCutOut
&& aCutOut
.x
< aCutOut
.XMost()) {
1617 nsRect
r(aCutOut
.x
, aY1
, aCutOut
.width
, height
);
1618 UpdateView(aView
, r
, aUpdateFlags
);
1620 if (aCutOut
.XMost() < aRect
.XMost()) {
1621 nsRect
r(aCutOut
.XMost(), aY1
, aRect
.XMost() - aCutOut
.XMost(), height
);
1622 UpdateView(aView
, r
, aUpdateFlags
);
1626 void nsViewManager::InvalidateRectDifference(nsView
*aView
, const nsRect
& aRect
, const nsRect
& aCutOut
,
1627 PRUint32 aUpdateFlags
) {
1628 if (aRect
.y
< aCutOut
.y
) {
1629 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aRect
.y
, aCutOut
.y
, PR_FALSE
);
1631 if (aCutOut
.y
< aCutOut
.YMost()) {
1632 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aCutOut
.y
, aCutOut
.YMost(), PR_TRUE
);
1634 if (aCutOut
.YMost() < aRect
.YMost()) {
1635 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aCutOut
.YMost(), aRect
.YMost(), PR_FALSE
);
1639 NS_IMETHODIMP
nsViewManager::ResizeView(nsIView
*aView
, const nsRect
&aRect
, PRBool aRepaintExposedAreaOnly
)
1641 nsView
* view
= static_cast<nsView
*>(aView
);
1642 nsRect oldDimensions
;
1644 view
->GetDimensions(oldDimensions
);
1645 if (!oldDimensions
.IsExactEqual(aRect
)) {
1646 nsView
* parentView
= view
->GetParent();
1647 if (parentView
== nsnull
)
1651 // Prevent Invalidation of hidden views
1652 if (view
->GetVisibility() == nsViewVisibility_kHide
) {
1653 view
->SetDimensions(aRect
, PR_FALSE
);
1655 if (!aRepaintExposedAreaOnly
) {
1656 //Invalidate the union of the old and new size
1657 view
->SetDimensions(aRect
, PR_TRUE
);
1659 UpdateView(view
, aRect
, NS_VMREFRESH_NO_SYNC
);
1660 view
->ConvertToParentCoords(&oldDimensions
.x
, &oldDimensions
.y
);
1661 UpdateView(parentView
, oldDimensions
, NS_VMREFRESH_NO_SYNC
);
1663 view
->SetDimensions(aRect
, PR_TRUE
);
1665 InvalidateRectDifference(view
, aRect
, oldDimensions
, NS_VMREFRESH_NO_SYNC
);
1667 view
->ConvertToParentCoords(&r
.x
, &r
.y
);
1668 view
->ConvertToParentCoords(&oldDimensions
.x
, &oldDimensions
.y
);
1669 InvalidateRectDifference(parentView
, oldDimensions
, r
, NS_VMREFRESH_NO_SYNC
);
1674 // Note that if layout resizes the view and the view has a custom clip
1675 // region set, then we expect layout to update the clip region too. Thus
1676 // in the case where mClipRect has been optimized away to just be a null
1677 // pointer, and this resize is implicitly changing the clip rect, it's OK
1678 // because layout will change it back again if necessary.
1683 static double GetArea(const nsRect
& aRect
)
1685 return double(aRect
.width
)*double(aRect
.height
);
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();
1714 return GetArea(aUpdateRegion
->GetBounds()) < GetArea(parentBounds
)/2;
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 nsWeakView parentWeakView
= aView
;
1768 if (aView
->HasWidget()) {
1769 aView
->GetWidget()->Update(); // Flushes Layout!
1770 if (!parentWeakView
.IsAlive()) {
1775 nsView
* childView
= aView
->GetFirstChild();
1777 nsWeakView childWeakView
= childView
;
1778 UpdateWidgetsForView(childView
);
1779 if (NS_LIKELY(childWeakView
.IsAlive())) {
1780 childView
= childView
->GetNextSibling();
1783 // The current view was destroyed - restart at the first child if the
1784 // parent is still alive.
1785 childView
= parentWeakView
.IsAlive() ? aView
->GetFirstChild() : nsnull
;
1790 PRBool
nsViewManager::IsViewInserted(nsView
*aView
)
1792 if (mRootView
== aView
) {
1794 } else if (aView
->GetParent() == nsnull
) {
1797 nsView
* view
= aView
->GetParent()->GetFirstChild();
1798 while (view
!= nsnull
) {
1799 if (view
== aView
) {
1802 view
= view
->GetNextSibling();
1808 NS_IMETHODIMP
nsViewManager::SetViewZIndex(nsIView
*aView
, PRBool aAutoZIndex
, PRInt32 aZIndex
, PRBool aTopMost
)
1810 nsView
* view
= static_cast<nsView
*>(aView
);
1811 nsresult rv
= NS_OK
;
1813 NS_ASSERTION((view
!= nsnull
), "no view");
1815 // don't allow the root view's z-index to be changed. It should always be zero.
1816 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1817 if (aView
== mRootView
) {
1821 PRBool oldTopMost
= view
->IsTopMost();
1822 PRBool oldIsAuto
= view
->GetZIndexIsAuto();
1828 PRInt32 oldidx
= view
->GetZIndex();
1829 view
->SetZIndex(aAutoZIndex
, aZIndex
, aTopMost
);
1831 if (oldidx
!= aZIndex
|| oldTopMost
!= aTopMost
||
1832 oldIsAuto
!= aAutoZIndex
) {
1833 UpdateView(view
, NS_VMREFRESH_NO_SYNC
);
1839 NS_IMETHODIMP
nsViewManager::SetViewObserver(nsIViewObserver
*aObserver
)
1841 mObserver
= aObserver
;
1845 NS_IMETHODIMP
nsViewManager::GetViewObserver(nsIViewObserver
*&aObserver
)
1847 if (nsnull
!= mObserver
) {
1848 aObserver
= mObserver
;
1849 NS_ADDREF(mObserver
);
1852 return NS_ERROR_NO_INTERFACE
;
1855 NS_IMETHODIMP
nsViewManager::GetDeviceContext(nsIDeviceContext
*&aContext
)
1857 NS_IF_ADDREF(mContext
);
1858 aContext
= mContext
;
1862 already_AddRefed
<nsIRenderingContext
>
1863 nsViewManager::CreateRenderingContext(nsView
&aView
)
1865 nsView
*par
= &aView
;
1867 nsIRenderingContext
*cx
= nsnull
;
1868 nscoord ax
= 0, ay
= 0;
1872 win
= par
->GetWidget();
1876 //get absolute coordinates of view, but don't
1877 //add in view pos since the first thing you ever
1878 //need to do when painting a view is to translate
1879 //the rendering context by the views pos and other parts
1880 //of the code do this for us...
1884 par
->ConvertToParentCoords(&ax
, &ay
);
1887 par
= par
->GetParent();
1889 while (nsnull
!= par
);
1893 // XXXkt this has an origin at top-left of win ...
1894 mContext
->CreateRenderingContext(par
, cx
);
1896 // XXXkt ... but the translation is between the origins of views
1897 NS_ASSERTION(aView
.ViewToWidgetOffset()
1898 - aView
.GetDimensions().TopLeft() ==
1899 par
->ViewToWidgetOffset()
1900 - par
->GetDimensions().TopLeft(),
1901 "ViewToWidgetOffset not handled!");
1903 cx
->Translate(ax
, ay
);
1909 NS_IMETHODIMP
nsViewManager::DisableRefresh(void)
1912 return RootViewManager()->DisableRefresh();
1915 if (mUpdateBatchCnt
> 0)
1918 mRefreshEnabled
= PR_FALSE
;
1922 NS_IMETHODIMP
nsViewManager::EnableRefresh(PRUint32 aUpdateFlags
)
1925 return RootViewManager()->EnableRefresh(aUpdateFlags
);
1928 if (mUpdateBatchCnt
> 0)
1931 mRefreshEnabled
= PR_TRUE
;
1933 // nested batching can combine IMMEDIATE with DEFERRED. Favour
1934 // IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC. We need to
1935 // check for IMMEDIATE before checking mHasPendingUpdates, because
1936 // the latter might be false as far as gecko is concerned but the OS
1937 // might still have queued up expose events that it hasn't sent yet.
1938 if (aUpdateFlags
& NS_VMREFRESH_IMMEDIATE
) {
1939 FlushPendingInvalidates();
1941 } else if (!mHasPendingUpdates
) {
1943 } else if (aUpdateFlags
& NS_VMREFRESH_DEFERRED
) {
1944 PostInvalidateEvent();
1946 FlushPendingInvalidates();
1952 nsIViewManager
* nsViewManager::BeginUpdateViewBatch(void)
1955 return RootViewManager()->BeginUpdateViewBatch();
1958 nsresult result
= NS_OK
;
1960 if (mUpdateBatchCnt
== 0) {
1961 mUpdateBatchFlags
= 0;
1962 result
= DisableRefresh();
1965 if (NS_SUCCEEDED(result
))
1971 NS_IMETHODIMP
nsViewManager::EndUpdateViewBatch(PRUint32 aUpdateFlags
)
1973 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1975 nsresult result
= NS_OK
;
1979 NS_ASSERTION(mUpdateBatchCnt
>= 0, "Invalid batch count!");
1981 if (mUpdateBatchCnt
< 0)
1983 mUpdateBatchCnt
= 0;
1984 return NS_ERROR_FAILURE
;
1987 mUpdateBatchFlags
|= aUpdateFlags
;
1988 if (mUpdateBatchCnt
== 0) {
1989 result
= EnableRefresh(mUpdateBatchFlags
);
1995 NS_IMETHODIMP
nsViewManager::SetRootScrollableView(nsIScrollableView
*aScrollable
)
1997 mRootScrollable
= aScrollable
;
2001 NS_IMETHODIMP
nsViewManager::GetRootScrollableView(nsIScrollableView
**aScrollable
)
2003 *aScrollable
= mRootScrollable
;
2007 NS_IMETHODIMP
nsViewManager::GetWidget(nsIWidget
**aWidget
)
2009 *aWidget
= GetWidget();
2010 NS_IF_ADDREF(*aWidget
);
2014 NS_IMETHODIMP
nsViewManager::ForceUpdate()
2017 return RootViewManager()->ForceUpdate();
2020 // Walk the view tree looking for widgets, and call Update() on each one
2022 UpdateWidgetsForView(mRootView
);
2028 void nsViewManager::ViewToWidget(nsView
*aView
, nsView
* aWidgetView
, nsRect
&aRect
) const
2030 while (aView
!= aWidgetView
) {
2031 aView
->ConvertToParentCoords(&aRect
.x
, &aRect
.y
);
2032 aView
= aView
->GetParent();
2035 // intersect aRect with bounds of aWidgetView, to prevent generating any illegal rectangles.
2037 aWidgetView
->GetDimensions(bounds
);
2038 aRect
.IntersectRect(aRect
, bounds
);
2039 // account for the view's origin not lining up with the widget's
2040 aRect
.x
-= bounds
.x
;
2041 aRect
.y
-= bounds
.y
;
2043 aRect
+= aView
->ViewToWidgetOffset();
2045 // finally, convert to device coordinates.
2046 aRect
.ScaleRoundOut(1.0f
/ mContext
->AppUnitsPerDevPixel());
2049 nsresult
nsViewManager::GetVisibleRect(nsRect
& aVisibleRect
)
2051 nsresult rv
= NS_OK
;
2053 // Get the viewport scroller
2054 nsIScrollableView
* scrollingView
;
2055 GetRootScrollableView(&scrollingView
);
2057 if (scrollingView
) {
2058 // Determine the visible rect in the scrolled view's coordinate space.
2059 // The size of the visible area is the clip view size
2060 nsScrollPortView
* clipView
= static_cast<nsScrollPortView
*>(scrollingView
);
2061 clipView
->GetDimensions(aVisibleRect
);
2063 scrollingView
->GetScrollPosition(aVisibleRect
.x
, aVisibleRect
.y
);
2065 rv
= NS_ERROR_FAILURE
;
2071 nsresult
nsViewManager::GetAbsoluteRect(nsView
*aView
, const nsRect
&aRect
,
2074 nsIScrollableView
* scrollingView
= nsnull
;
2075 GetRootScrollableView(&scrollingView
);
2076 if (nsnull
== scrollingView
) {
2077 return NS_ERROR_FAILURE
;
2080 nsIView
* scrolledIView
= nsnull
;
2081 scrollingView
->GetScrolledView(scrolledIView
);
2083 nsView
* scrolledView
= static_cast<nsView
*>(scrolledIView
);
2085 // Calculate the absolute coordinates of the aRect passed in.
2086 // aRects values are relative to aView
2088 nsView
*parentView
= aView
;
2089 while ((parentView
!= nsnull
) && (parentView
!= scrolledView
)) {
2090 parentView
->ConvertToParentCoords(&aAbsRect
.x
, &aAbsRect
.y
);
2091 parentView
= parentView
->GetParent();
2094 if (parentView
!= scrolledView
) {
2095 return NS_ERROR_FAILURE
;
2102 NS_IMETHODIMP
nsViewManager::GetRectVisibility(nsIView
*aView
,
2103 const nsRect
&aRect
,
2105 nsRectVisibility
*aRectVisibility
)
2107 nsView
* view
= static_cast<nsView
*>(aView
);
2109 // The parameter aMinTwips determines how many rows/cols of pixels must be visible on each side of the element,
2110 // in order to be counted as visible
2112 *aRectVisibility
= nsRectVisibility_kZeroAreaRect
;
2113 if (aRect
.width
== 0 || aRect
.height
== 0) {
2117 // is this view even visible?
2118 if (view
->GetVisibility() == nsViewVisibility_kHide
) {
2122 // nsViewManager::InsertChild ensures that descendants of floating views
2123 // are also marked floating.
2124 if (view
->GetFloating()) {
2125 *aRectVisibility
= nsRectVisibility_kVisible
;
2129 // Calculate the absolute coordinates for the visible rectangle
2131 if (GetVisibleRect(visibleRect
) == NS_ERROR_FAILURE
) {
2132 *aRectVisibility
= nsRectVisibility_kVisible
;
2136 // Calculate the absolute coordinates of the aRect passed in.
2137 // aRects values are relative to aView
2139 if ((GetAbsoluteRect(view
, aRect
, absRect
)) == NS_ERROR_FAILURE
) {
2140 *aRectVisibility
= nsRectVisibility_kVisible
;
2145 * If aMinTwips > 0, ensure at least aMinTwips of space around object is visible
2146 * The object is not visible if:
2147 * ((objectTop < windowTop && objectBottom < windowTop) ||
2148 * (objectBottom > windowBottom && objectTop > windowBottom) ||
2149 * (objectLeft < windowLeft && objectRight < windowLeft) ||
2150 * (objectRight > windowRight && objectLeft > windowRight))
2153 if (absRect
.y
< visibleRect
.y
&&
2154 absRect
.y
+ absRect
.height
< visibleRect
.y
+ aMinTwips
)
2155 *aRectVisibility
= nsRectVisibility_kAboveViewport
;
2156 else if (absRect
.y
+ absRect
.height
> visibleRect
.y
+ visibleRect
.height
&&
2157 absRect
.y
> visibleRect
.y
+ visibleRect
.height
- aMinTwips
)
2158 *aRectVisibility
= nsRectVisibility_kBelowViewport
;
2159 else if (absRect
.x
< visibleRect
.x
&&
2160 absRect
.x
+ absRect
.width
< visibleRect
.x
+ aMinTwips
)
2161 *aRectVisibility
= nsRectVisibility_kLeftOfViewport
;
2162 else if (absRect
.x
+ absRect
.width
> visibleRect
.x
+ visibleRect
.width
&&
2163 absRect
.x
> visibleRect
.x
+ visibleRect
.width
- aMinTwips
)
2164 *aRectVisibility
= nsRectVisibility_kRightOfViewport
;
2166 *aRectVisibility
= nsRectVisibility_kVisible
;
2172 nsViewManager::IsPainting(PRBool
& aIsPainting
)
2174 aIsPainting
= IsPainting();
2179 nsViewManager::FlushPendingInvalidates()
2181 NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!\n");
2182 NS_ASSERTION(mUpdateBatchCnt
== 0, "Must not be in an update batch!");
2183 // XXXbz this is probably not quite OK yet, if callers can explicitly
2184 // DisableRefresh while we have an event posted.
2185 // NS_ASSERTION(mRefreshEnabled, "How did we get here?");
2187 // Let all the view observers of all viewmanagers in this tree know that
2188 // we're about to "paint" (this lets them get in their invalidates now so
2189 // we don't go through two invalidate-processing cycles).
2190 NS_ASSERTION(gViewManagers
, "Better have a viewmanagers array!");
2192 // Make sure to not send WillPaint notifications while scrolling
2193 if (mScrollCnt
== 0) {
2194 // Disable refresh while we notify our view observers, so that if they do
2195 // view update batches we don't reenter this code and so that we batch
2196 // all of them together. We don't use
2197 // BeginUpdateViewBatch/EndUpdateViewBatch, since that would reenter this
2198 // exact code, but we want the effect of a single big update batch.
2199 PRBool refreshEnabled
= mRefreshEnabled
;
2200 mRefreshEnabled
= PR_FALSE
;
2204 for (index
= 0; index
< mVMCount
; index
++) {
2205 nsViewManager
* vm
= (nsViewManager
*)gViewManagers
->ElementAt(index
);
2206 if (vm
->RootViewManager() == this) {
2208 nsIViewObserver
* observer
= vm
->GetViewObserver();
2210 observer
->WillPaint();
2211 NS_ASSERTION(mUpdateBatchCnt
== 1,
2212 "Observer did not end view batch?");
2218 // Someone could have called EnableRefresh on us from inside WillPaint().
2219 // Only reset the old mRefreshEnabled value if the current value is false.
2220 if (!mRefreshEnabled
) {
2221 mRefreshEnabled
= refreshEnabled
;
2225 if (mHasPendingUpdates
) {
2226 ProcessPendingUpdates(mRootView
, PR_TRUE
);
2227 mHasPendingUpdates
= PR_FALSE
;
2232 nsViewManager::ProcessInvalidateEvent()
2234 NS_ASSERTION(IsRootVM(),
2235 "Incorrectly targeted invalidate event");
2236 // If we're in the middle of an update batch, just repost the event,
2237 // to be processed when the batch ends.
2238 PRBool processEvent
= (mUpdateBatchCnt
== 0);
2240 FlushPendingInvalidates();
2242 mInvalidateEvent
.Forget();
2243 if (!processEvent
) {
2244 // We didn't actually process this event... post a new one
2245 PostInvalidateEvent();
2250 nsViewManager::SetDefaultBackgroundColor(nscolor aColor
)
2252 mDefaultBackgroundColor
= aColor
;
2257 nsViewManager::GetDefaultBackgroundColor(nscolor
* aColor
)
2259 *aColor
= mDefaultBackgroundColor
;
2265 nsViewManager::GetLastUserEventTime(PRUint32
& aTime
)
2267 aTime
= gLastUserEventTime
;
2271 class nsSynthMouseMoveEvent
: public nsViewManagerEvent
{
2273 nsSynthMouseMoveEvent(nsViewManager
*aViewManager
,
2275 : nsViewManagerEvent(aViewManager
),
2276 mFromScroll(aFromScroll
) {
2281 mViewManager
->ProcessSynthMouseMoveEvent(mFromScroll
);
2290 nsViewManager::SynthesizeMouseMove(PRBool aFromScroll
)
2293 return RootViewManager()->SynthesizeMouseMove(aFromScroll
);
2295 if (mMouseLocation
== nsPoint(NSCOORD_NONE
, NSCOORD_NONE
))
2298 if (!mSynthMouseMoveEvent
.IsPending()) {
2299 nsRefPtr
<nsViewManagerEvent
> ev
=
2300 new nsSynthMouseMoveEvent(this, aFromScroll
);
2302 if (NS_FAILED(NS_DispatchToCurrentThread(ev
))) {
2303 NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
2304 return NS_ERROR_UNEXPECTED
;
2307 mSynthMouseMoveEvent
= ev
;
2314 * Find the first floating view with a widget in a postorder traversal of the
2315 * view tree that contains the point. Thus more deeply nested floating views
2316 * are preferred over their ancestors, and floating views earlier in the
2317 * view hierarchy (i.e., added later) are preferred over their siblings.
2318 * This is adequate for finding the "topmost" floating view under a point,
2319 * given that floating views don't supporting having a specific z-index.
2321 * We cannot exit early when aPt is outside the view bounds, because floating
2322 * views aren't necessarily included in their parent's bounds, so this could
2323 * traverse the entire view hierarchy --- use carefully.
2325 static nsView
* FindFloatingViewContaining(nsView
* aView
, nsPoint aPt
)
2327 if (aView
->GetVisibility() == nsViewVisibility_kHide
)
2328 // No need to look into descendants.
2331 for (nsView
* v
= aView
->GetFirstChild(); v
; v
= v
->GetNextSibling()) {
2332 nsView
* r
= FindFloatingViewContaining(v
, aPt
- v
->GetOffsetTo(aView
));
2337 if (aView
->GetFloating() && aView
->HasWidget() &&
2338 aView
->GetDimensions().Contains(aPt
))
2345 * This finds the first view containing the given point in a postorder
2346 * traversal of the view tree that contains the point, assuming that the
2347 * point is not in a floating view. It assumes that only floating views
2348 * extend outside the bounds of their parents.
2350 * This methods should only be called if FindFloatingViewContaining
2353 static nsView
* FindViewContaining(nsView
* aView
, nsPoint aPt
)
2355 for (nsView
* v
= aView
->GetFirstChild(); v
; v
= v
->GetNextSibling()) {
2356 if (aView
->GetDimensions().Contains(aPt
) &&
2357 aView
->GetVisibility() != nsViewVisibility_kHide
) {
2358 nsView
* r
= FindViewContaining(v
, aPt
- v
->GetOffsetTo(aView
));
2369 nsViewManager::ProcessSynthMouseMoveEvent(PRBool aFromScroll
)
2371 // allow new event to be posted while handling this one only if the
2372 // source of the event is a scroll (to prevent infinite reflow loops)
2374 mSynthMouseMoveEvent
.Forget();
2376 NS_ASSERTION(IsRootVM(), "Only the root view manager should be here");
2378 if (mMouseLocation
== nsPoint(NSCOORD_NONE
, NSCOORD_NONE
) || !mRootView
) {
2379 mSynthMouseMoveEvent
.Forget();
2383 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
2384 // we need to access members after we call DispatchEvent).
2385 nsCOMPtr
<nsIViewManager
> kungFuDeathGrip(this);
2387 #ifdef DEBUG_MOUSE_LOCATION
2388 printf("[vm=%p]synthesizing mouse move to (%d,%d)\n",
2389 this, mMouseLocation
.x
, mMouseLocation
.y
);
2392 nsPoint pt
= mMouseLocation
;
2393 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
2394 pt
.x
= NSIntPixelsToAppUnits(mMouseLocation
.x
, p2a
);
2395 pt
.y
= NSIntPixelsToAppUnits(mMouseLocation
.y
, p2a
);
2396 // This could be a bit slow (traverses entire view hierarchy)
2397 // but it's OK to do it once per synthetic mouse event
2398 nsView
* view
= FindFloatingViewContaining(mRootView
, pt
);
2399 nsPoint
offset(0, 0);
2400 nsViewManager
*pointVM
;
2403 nsView
*pointView
= FindViewContaining(mRootView
, pt
);
2404 // pointView can be null in situations related to mouse capture
2405 pointVM
= (pointView
? pointView
: view
)->GetViewManager();
2407 offset
= view
->GetOffsetTo(mRootView
);
2408 offset
.x
= NSAppUnitsToIntPixels(offset
.x
, p2a
);
2409 offset
.y
= NSAppUnitsToIntPixels(offset
.y
, p2a
);
2410 pointVM
= view
->GetViewManager();
2412 nsMouseEvent
event(PR_TRUE
, NS_MOUSE_MOVE
, view
->GetWidget(),
2413 nsMouseEvent::eSynthesized
);
2414 event
.refPoint
= mMouseLocation
- offset
;
2415 event
.time
= PR_IntervalNow();
2416 // XXX set event.isShift, event.isControl, event.isAlt, event.isMeta ?
2418 pointVM
->GetViewObserver()->DispatchSynthMouseMove(&event
, !aFromScroll
);
2421 mSynthMouseMoveEvent
.Forget();
2425 nsViewManager::InvalidateHierarchy()
2429 NS_RELEASE(mRootViewManager
);
2431 nsView
*parent
= mRootView
->GetParent();
2433 mRootViewManager
= parent
->GetViewManager()->RootViewManager();
2434 NS_ADDREF(mRootViewManager
);
2435 NS_ASSERTION(mRootViewManager
!= this,
2436 "Root view had a parent, but it has the same view manager");
2438 mRootViewManager
= this;