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
);
433 // damageRegion is the damaged area, in twips, relative to the view origin
434 nsRegion damageRegion
;
435 // convert pixels-relative-to-widget-origin to twips-relative-to-widget-origin
436 ConvertNativeRegionToAppRegion(aRegion
, &damageRegion
, mContext
);
437 // move it from widget coordinates into view coordinates
438 damageRegion
.MoveBy(viewRect
.x
, viewRect
.y
);
440 if (damageRegion
.IsEmpty()) {
442 nsRect damageRect
= damageRegion
.GetBounds();
443 printf("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
444 damageRect
.x
, damageRect
.y
, damageRect
.width
, damageRect
.height
,
445 viewRect
.x
, viewRect
.y
, viewRect
.width
, viewRect
.height
);
450 #ifdef NS_VM_PERF_METRICS
451 MOZ_TIMER_DEBUGLOG(("Reset nsViewManager::Refresh(region), this=%p\n", this));
452 MOZ_TIMER_RESET(mWatch
);
454 MOZ_TIMER_DEBUGLOG(("Start: nsViewManager::Refresh(region)\n"));
455 MOZ_TIMER_START(mWatch
);
458 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
460 RootViewManager()->mRecursiveRefreshPending
= PR_TRUE
;
465 nsAutoScriptBlocker scriptBlocker
;
466 SetPainting(PR_TRUE
);
468 nsCOMPtr
<nsIRenderingContext
> localcx
;
469 NS_ASSERTION(aView
->GetWidget(),
470 "Must have a widget to calculate coordinates correctly");
471 if (nsnull
== aContext
)
473 localcx
= CreateRenderingContext(*aView
);
475 //couldn't get rendering context. this is ok at init time atleast
476 if (nsnull
== localcx
) {
477 SetPainting(PR_FALSE
);
481 // plain assignment grabs another reference.
485 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
487 nsRefPtr
<gfxContext
> ctx
= localcx
->ThebesContext();
491 nsPoint vtowoffset
= aView
->ViewToWidgetOffset();
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 void nsViewManager::DefaultRefresh(nsView
* aView
, nsIRenderingContext
*aContext
, const nsRect
* aRect
)
527 NS_PRECONDITION(aView
, "Must have a view to work with!");
528 nsIWidget
* widget
= aView
->GetNearestWidget(nsnull
);
532 nsCOMPtr
<nsIRenderingContext
> context
= aContext
;
534 context
= CreateRenderingContext(*aView
);
539 nscolor bgcolor
= mDefaultBackgroundColor
;
541 if (NS_GET_A(mDefaultBackgroundColor
) == 0) {
542 NS_WARNING("nsViewManager: Asked to paint a default background, but no default background color is set!");
546 context
->SetColor(bgcolor
);
547 context
->FillRect(*aRect
);
550 void nsViewManager::AddCoveringWidgetsToOpaqueRegion(nsRegion
&aRgn
, nsIDeviceContext
* aContext
,
552 NS_PRECONDITION(aRootView
, "Must have root view");
554 // We accumulate the bounds of widgets obscuring aRootView's widget into opaqueRgn.
555 // In OptimizeDisplayList, display list elements which lie behind obscuring native
556 // widgets are dropped.
557 // This shouldn't really be necessary, since the GFX/Widget toolkit should remove these
558 // covering widgets from the clip region passed into the paint command. But right now
559 // they only give us a paint rect and not a region, so we can't access that information.
560 // It's important to identifying areas that are covered by native widgets to avoid
561 // painting their views many times as we process invalidates from the root widget all the
562 // way down to the nested widgets.
564 // NB: we must NOT add widgets that correspond to floating views!
565 // We may be required to paint behind them
568 nsIWidget
* widget
= aRootView
->GetNearestWidget(nsnull
);
573 for (nsIWidget
* childWidget
= widget
->GetFirstChild();
575 childWidget
= childWidget
->GetNextSibling()) {
576 PRBool widgetVisible
;
577 childWidget
->IsVisible(widgetVisible
);
579 nsView
* view
= nsView::GetViewFor(childWidget
);
580 if (view
&& view
->GetVisibility() == nsViewVisibility_kShow
581 && !view
->GetFloating()) {
582 nsRect bounds
= view
->GetBounds();
583 if (bounds
.width
> 0 && bounds
.height
> 0) {
584 nsView
* viewParent
= view
->GetParent();
586 while (viewParent
&& viewParent
!= aRootView
) {
587 viewParent
->ConvertToParentCoords(&bounds
.x
, &bounds
.y
);
588 viewParent
= viewParent
->GetParent();
591 // maybe we couldn't get the view into the coordinate
592 // system of aRootView (maybe it's not a descendant
593 // view of aRootView?); if so, don't use it
595 aRgn
.Or(aRgn
, bounds
);
603 void nsViewManager::RenderViews(nsView
*aView
, nsIRenderingContext
& aRC
,
604 const nsRegion
& aRegion
)
607 nsView
* displayRoot
= GetDisplayRootFor(aView
);
608 nsPoint offsetToRoot
= aView
->GetOffsetTo(displayRoot
);
609 nsRegion
damageRegion(aRegion
);
610 damageRegion
.MoveBy(offsetToRoot
);
613 aRC
.Translate(-offsetToRoot
.x
, -offsetToRoot
.y
);
614 mObserver
->Paint(displayRoot
, &aRC
, damageRegion
);
619 void nsViewManager::ProcessPendingUpdates(nsView
* aView
, PRBool aDoInvalidate
)
621 NS_ASSERTION(IsRootVM(), "Updates will be missed");
623 // Protect against a null-view.
628 if (aView
->HasWidget()) {
629 aView
->ResetWidgetBounds(PR_FALSE
, PR_FALSE
, PR_TRUE
);
632 // process pending updates in child view.
633 for (nsView
* childView
= aView
->GetFirstChild(); childView
;
634 childView
= childView
->GetNextSibling()) {
635 ProcessPendingUpdates(childView
, aDoInvalidate
);
638 if (aDoInvalidate
&& aView
->HasNonEmptyDirtyRegion()) {
639 // Push out updates after we've processed the children; ensures that
640 // damage is applied based on the final widget geometry
641 NS_ASSERTION(mRefreshEnabled
, "Cannot process pending updates with refresh disabled");
642 nsRegion
* dirtyRegion
= aView
->GetDirtyRegion();
644 UpdateWidgetArea(aView
, *dirtyRegion
, nsnull
);
645 dirtyRegion
->SetEmpty();
650 NS_IMETHODIMP
nsViewManager::Composite()
653 return RootViewManager()->Composite();
656 if (UpdateCount() > 0)
665 NS_IMETHODIMP
nsViewManager::UpdateView(nsIView
*aView
, PRUint32 aUpdateFlags
)
667 // Mark the entire view as damaged
668 nsView
* view
= static_cast<nsView
*>(aView
);
670 nsRect bounds
= view
->GetBounds();
671 view
->ConvertFromParentCoords(&bounds
.x
, &bounds
.y
);
672 return UpdateView(view
, bounds
, aUpdateFlags
);
675 // This method accumulates the intersectons of all dirty regions attached to
676 // descendants of aSourceView with the cliprect of aTargetView into the dirty
677 // region of aTargetView, after offseting said intersections by aOffset.
679 AccumulateIntersectionsIntoDirtyRegion(nsView
* aTargetView
,
681 const nsPoint
& aOffset
)
683 if (aSourceView
->HasNonEmptyDirtyRegion()) {
684 // In most cases, aSourceView is an ancestor of aTargetView, since most
685 // commonly we have dirty rects on the root view.
686 nsPoint offset
= aTargetView
->GetOffsetTo(aSourceView
);
687 nsRegion intersection
;
688 intersection
= *aSourceView
->GetDirtyRegion();
689 if (!intersection
.IsEmpty()) {
690 nsRegion
* targetRegion
= aTargetView
->GetDirtyRegion();
692 intersection
.MoveBy(-offset
+ aOffset
);
693 targetRegion
->Or(*targetRegion
, intersection
);
694 // Random simplification number...
695 targetRegion
->SimplifyOutward(20);
700 if (aSourceView
== aTargetView
) {
701 // No need to do this with kids of aTargetView
705 for (nsView
* kid
= aSourceView
->GetFirstChild();
707 kid
= kid
->GetNextSibling()) {
708 AccumulateIntersectionsIntoDirtyRegion(aTargetView
, kid
, aOffset
);
713 nsViewManager::WillBitBlit(nsView
* aView
, nsPoint aScrollAmount
)
716 RootViewManager()->WillBitBlit(aView
, aScrollAmount
);
720 NS_PRECONDITION(aView
, "Must have a view");
721 NS_PRECONDITION(aView
->HasWidget(), "View must have a widget");
725 // Since the view is actually moving the widget by -aScrollAmount, that's the
726 // offset we want to use when accumulating dirty rects.
727 AccumulateIntersectionsIntoDirtyRegion(aView
, GetRootView(), -aScrollAmount
);
731 // Invalidate all widgets which overlap the view, other than the view's own widgets.
733 nsViewManager::UpdateViewAfterScroll(nsView
*aView
, const nsRegion
& aUpdateRegion
)
735 NS_ASSERTION(RootViewManager()->mScrollCnt
> 0,
736 "Someone forgot to call WillBitBlit()");
737 // Look at the view's clipped rect. It may be that part of the view is clipped out
738 // in which case we don't need to worry about invalidating the clipped-out part.
739 nsRect damageRect
= aView
->GetDimensions();
740 if (damageRect
.IsEmpty()) {
741 // Don't forget to undo mScrollCnt!
742 --RootViewManager()->mScrollCnt
;
746 nsView
* displayRoot
= GetDisplayRootFor(aView
);
747 nsPoint offset
= aView
->GetOffsetTo(displayRoot
);
748 damageRect
.MoveBy(offset
);
750 UpdateWidgetArea(displayRoot
, nsRegion(damageRect
), aView
);
751 if (!aUpdateRegion
.IsEmpty()) {
752 // XXX We should update the region, not the bounds rect, but that requires
753 // a little more work. Fix this when we reshuffle this code.
754 nsRegion
update(aUpdateRegion
);
755 update
.MoveBy(offset
);
756 UpdateWidgetArea(displayRoot
, update
, nsnull
);
757 // FlushPendingInvalidates();
761 --RootViewManager()->mScrollCnt
;
765 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
766 * every widget child of aWidgetView, plus aWidgetView's own widget
767 * @param aIgnoreWidgetView if non-null, the aIgnoreWidgetView's widget and its
768 * children are not updated.
771 nsViewManager::UpdateWidgetArea(nsView
*aWidgetView
, const nsRegion
&aDamagedRegion
,
772 nsView
* aIgnoreWidgetView
)
774 if (!IsRefreshEnabled()) {
775 // accumulate this rectangle in the view's dirty region, so we can
777 nsRegion
* dirtyRegion
= aWidgetView
->GetDirtyRegion();
778 if (!dirtyRegion
) return;
780 dirtyRegion
->Or(*dirtyRegion
, aDamagedRegion
);
781 // Don't let dirtyRegion grow beyond 8 rects
782 dirtyRegion
->SimplifyOutward(8);
783 nsViewManager
* rootVM
= RootViewManager();
784 rootVM
->mHasPendingUpdates
= PR_TRUE
;
785 rootVM
->IncrementUpdateCount();
787 // this should only happen at the top level, and this result
788 // should not be consumed by top-level callers, so it doesn't
789 // really matter what we return
792 // If the bounds don't overlap at all, there's nothing to do
793 nsRegion intersection
;
794 intersection
.And(aWidgetView
->GetDimensions(), aDamagedRegion
);
795 if (intersection
.IsEmpty()) {
799 // If the widget is hidden, it don't cover nothing
800 if (nsViewVisibility_kHide
== aWidgetView
->GetVisibility()) {
802 // Assert if view is hidden but widget is visible
803 nsIWidget
* widget
= aWidgetView
->GetNearestWidget(nsnull
);
806 widget
->IsVisible(visible
);
807 NS_ASSERTION(!visible
, "View is hidden but widget is visible!");
813 if (aWidgetView
== aIgnoreWidgetView
) {
814 // the widget for aIgnoreWidgetView (and its children) should be treated as already updated.
818 nsIWidget
* widget
= aWidgetView
->GetNearestWidget(nsnull
);
820 // The root view or a scrolling view might not have a widget
821 // (for example, during printing). We get here when we scroll
822 // during printing to show selected options in a listbox, for example.
826 // Update all child widgets with the damage. In the process,
827 // accumulate the union of all the child widget areas, or at least
828 // some subset of that.
830 for (nsIWidget
* childWidget
= widget
->GetFirstChild();
832 childWidget
= childWidget
->GetNextSibling()) {
833 nsView
* view
= nsView::GetViewFor(childWidget
);
834 NS_ASSERTION(view
!= aWidgetView
, "will recur infinitely");
835 if (view
&& view
->GetVisibility() == nsViewVisibility_kShow
) {
836 // Don't mess with views that are in completely different view
838 if (view
->GetViewManager()->RootViewManager() == RootViewManager()) {
839 // get the damage region into 'view's coordinate system
840 nsRegion damage
= intersection
;
841 nsPoint offset
= view
->GetOffsetTo(aWidgetView
);
842 damage
.MoveBy(-offset
);
843 UpdateWidgetArea(view
, damage
, aIgnoreWidgetView
);
844 children
.Or(children
, view
->GetDimensions() + offset
);
845 children
.SimplifyInward(20);
851 leftOver
.Sub(intersection
, children
);
853 if (!leftOver
.IsEmpty()) {
854 NS_ASSERTION(IsRefreshEnabled(), "Can only get here with refresh enabled, I hope");
857 for (nsRegionRectIterator
iter(leftOver
); (r
= iter
.Next());) {
859 ViewToWidget(aWidgetView
, aWidgetView
, bounds
);
860 widget
->Invalidate(bounds
, PR_FALSE
);
865 NS_IMETHODIMP
nsViewManager::UpdateView(nsIView
*aView
, const nsRect
&aRect
, PRUint32 aUpdateFlags
)
867 NS_PRECONDITION(nsnull
!= aView
, "null view");
869 nsView
* view
= static_cast<nsView
*>(aView
);
871 nsRect
damagedRect(aRect
);
873 // If the rectangle is not visible then abort
874 // without invalidating. This is a performance
875 // enhancement since invalidating a native widget
877 // This also checks for silly request like damagedRect.width = 0 or damagedRect.height = 0
878 nsRectVisibility rectVisibility
;
879 GetRectVisibility(view
, damagedRect
, 0, &rectVisibility
);
880 if (rectVisibility
!= nsRectVisibility_kVisible
) {
884 // if this is a floating view, it isn't covered by any widgets other than
885 // its children. In that case we walk up to its parent widget and use
886 // that as the root to update from. This also means we update areas that
887 // may be outside the parent view(s), which is necessary for floats.
888 if (view
->GetFloating()) {
889 nsView
* widgetParent
= view
;
891 while (!widgetParent
->HasWidget()) {
892 widgetParent
->ConvertToParentCoords(&damagedRect
.x
, &damagedRect
.y
);
893 widgetParent
= widgetParent
->GetParent();
896 UpdateWidgetArea(widgetParent
, nsRegion(damagedRect
), nsnull
);
898 // Propagate the update to the root widget of the root view manager, since
899 // iframes, for example, can overlap each other and be translucent. So we
900 // have to possibly invalidate our rect in each of the widgets we have
902 damagedRect
.MoveBy(ComputeViewOffset(view
));
904 UpdateWidgetArea(RootViewManager()->GetRootView(), nsRegion(damagedRect
), nsnull
);
907 RootViewManager()->IncrementUpdateCount();
909 if (!IsRefreshEnabled()) {
913 // See if we should do an immediate refresh or wait
914 if (aUpdateFlags
& NS_VMREFRESH_IMMEDIATE
) {
921 NS_IMETHODIMP
nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags
)
923 if (RootViewManager() != this) {
924 return RootViewManager()->UpdateAllViews(aUpdateFlags
);
927 UpdateViews(mRootView
, aUpdateFlags
);
931 void nsViewManager::UpdateViews(nsView
*aView
, PRUint32 aUpdateFlags
)
934 UpdateView(aView
, aUpdateFlags
);
936 // update all children as well.
937 nsView
* childView
= aView
->GetFirstChild();
938 while (nsnull
!= childView
) {
939 UpdateViews(childView
, aUpdateFlags
);
940 childView
= childView
->GetNextSibling();
944 nsView
*nsViewManager::sCurrentlyFocusView
= nsnull
;
945 nsView
*nsViewManager::sViewFocusedBeforeSuppression
= nsnull
;
946 PRBool
nsViewManager::sFocusSuppressed
= PR_FALSE
;
948 void nsViewManager::SuppressFocusEvents(PRBool aSuppress
)
951 sFocusSuppressed
= PR_TRUE
;
952 SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView());
956 sFocusSuppressed
= PR_FALSE
;
957 if (GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression()) {
961 // We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS.
962 nsIWidget
*widget
= nsnull
;
963 nsEventStatus status
;
965 // Backup what is focused before we send the blur. If the
966 // blur causes a focus change, keep that new focus change,
967 // don't overwrite with the old "currently focused view".
968 nsIView
*currentFocusBeforeBlur
= GetCurrentlyFocusedView();
970 // Send NS_LOSTFOCUS to widget that was focused before
971 // focus/blur suppression.
972 if (GetViewFocusedBeforeSuppression()) {
973 widget
= GetViewFocusedBeforeSuppression()->GetWidget();
975 #ifdef DEBUG_FOCUS_SUPPRESSION
976 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_LOSTFOCUS\n");
978 nsGUIEvent
event(PR_TRUE
, NS_LOSTFOCUS
, widget
);
979 widget
->DispatchEvent(&event
, status
);
983 // Send NS_GOTFOCUS to the widget that we think should be focused.
984 if (GetCurrentlyFocusedView() &&
985 currentFocusBeforeBlur
== GetCurrentlyFocusedView())
987 widget
= GetCurrentlyFocusedView()->GetWidget();
989 #ifdef DEBUG_FOCUS_SUPPRESSION
990 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_GOTFOCUS\n");
992 nsGUIEvent
event(PR_TRUE
, NS_GOTFOCUS
, widget
);
993 widget
->DispatchEvent(&event
, status
);
999 static void ConvertRectAppUnitsToIntPixels(nsRect
& aRect
, PRInt32 p2a
)
1001 aRect
.x
= NSAppUnitsToIntPixels(aRect
.x
, p2a
);
1002 aRect
.y
= NSAppUnitsToIntPixels(aRect
.y
, p2a
);
1003 aRect
.width
= NSAppUnitsToIntPixels(aRect
.width
, p2a
);
1004 aRect
.height
= NSAppUnitsToIntPixels(aRect
.height
, p2a
);
1007 NS_IMETHODIMP
nsViewManager::DispatchEvent(nsGUIEvent
*aEvent
, nsEventStatus
*aStatus
)
1009 *aStatus
= nsEventStatus_eIgnore
;
1011 switch(aEvent
->message
)
1015 nsView
* view
= nsView::GetViewFor(aEvent
->widget
);
1019 nscoord width
= ((nsSizeEvent
*)aEvent
)->windowSize
->width
;
1020 nscoord height
= ((nsSizeEvent
*)aEvent
)->windowSize
->height
;
1021 width
= ((nsSizeEvent
*)aEvent
)->mWinWidth
;
1022 height
= ((nsSizeEvent
*)aEvent
)->mWinHeight
;
1024 // The root view may not be set if this is the resize associated with
1027 if (view
== mRootView
)
1029 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1030 SetWindowDimensions(NSIntPixelsToAppUnits(width
, p2a
),
1031 NSIntPixelsToAppUnits(height
, p2a
));
1032 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1041 nsPaintEvent
*event
= static_cast<nsPaintEvent
*>(aEvent
);
1042 nsView
*view
= nsView::GetViewFor(aEvent
->widget
);
1044 if (!view
|| !mContext
)
1047 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1049 // The rect is in device units, and it's in the coordinate space of its
1050 // associated window.
1051 nsCOMPtr
<nsIRegion
> region
= event
->region
;
1053 if (NS_FAILED(CreateRegion(getter_AddRefs(region
))))
1056 const nsRect
& damrect
= *event
->rect
;
1057 region
->SetTo(damrect
.x
, damrect
.y
, damrect
.width
, damrect
.height
);
1060 if (region
->IsEmpty())
1064 if (IsRefreshEnabled()) {
1065 // If an ancestor widget was hidden and then shown, we could
1066 // have a delayed resize to handle.
1067 PRBool didResize
= PR_FALSE
;
1068 for (nsViewManager
*vm
= this; vm
;
1069 vm
= vm
->mRootView
->GetParent()
1070 ? vm
->mRootView
->GetParent()->GetViewManager()
1072 if (vm
->mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
) &&
1073 IsViewVisible(vm
->mRootView
)) {
1074 vm
->DoSetWindowDimensions(vm
->mDelayedResize
.width
,
1075 vm
->mDelayedResize
.height
);
1076 vm
->mDelayedResize
.SizeTo(NSCOORD_NONE
, NSCOORD_NONE
);
1079 vm
->UpdateView(vm
->mRootView
, NS_VMREFRESH_NO_SYNC
);
1080 didResize
= PR_TRUE
;
1082 // not sure if it's valid for us to claim that we
1083 // ignored this, but we're going to do so anyway, since
1084 // we didn't actually paint anything
1085 *aStatus
= nsEventStatus_eIgnore
;
1090 //NS_ASSERTION(IsViewVisible(view), "painting an invisible view");
1092 // Just notify our own view observer that we're about to paint
1093 // XXXbz do we need to notify other view observers for viewmanagers
1095 // Make sure to not send WillPaint notifications while scrolling
1096 nsRefPtr
<nsViewManager
> rootVM
= RootViewManager();
1098 nsIWidget
*widget
= mRootView
->GetWidget();
1099 PRBool transparentWindow
= PR_FALSE
;
1101 widget
->GetHasTransparentBackground(transparentWindow
);
1103 if (rootVM
->mScrollCnt
== 0 && !transparentWindow
) {
1104 nsIViewObserver
* observer
= GetViewObserver();
1106 // Do an update view batch. Make sure not to do it DEFERRED,
1107 // since that would effectively delay any invalidates that are
1108 // triggered by the WillPaint notification (they'd happen when
1109 // the invalid event fires, which is later than the reflow
1110 // event would fire and could end up being after some timer
1111 // events, leading to frame dropping in DHTML). Note that the
1112 // observer may try to reenter this code from inside
1113 // WillPaint() by trying to do a synchronous paint, but since
1114 // refresh will be disabled it won't be able to do the paint.
1115 // We should really sort out the rules on our synch painting
1117 UpdateViewBatch
batch(this);
1118 observer
->WillPaint();
1119 batch
.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC
);
1121 // Get the view pointer again since the code above might have
1122 // destroyed it (bug 378273).
1123 view
= nsView::GetViewFor(aEvent
->widget
);
1126 // Make sure to sync up any widget geometry changes we
1127 // have pending before we paint.
1128 if (rootVM
->mHasPendingUpdates
) {
1129 rootVM
->ProcessPendingUpdates(mRootView
, PR_FALSE
);
1133 Refresh(view
, event
->renderingContext
, region
,
1134 NS_VMREFRESH_DOUBLE_BUFFER
);
1138 // since we got an NS_PAINT event, we need to
1139 // draw something so we don't get blank areas.
1141 region
->GetBoundingBox(&damRect
.x
, &damRect
.y
, &damRect
.width
, &damRect
.height
);
1142 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1143 damRect
.ScaleRoundOut(float(p2a
));
1144 DefaultRefresh(view
, event
->renderingContext
, &damRect
);
1146 // Clients like the editor can trigger multiple
1147 // reflows during what the user perceives as a single
1148 // edit operation, so it disables view manager
1149 // refreshing until the edit operation is complete
1150 // so that users don't see the intermediate steps.
1152 // Unfortunately some of these reflows can trigger
1153 // nsScrollPortView and nsScrollingView Scroll() calls
1154 // which in most cases force an immediate BitBlt and
1155 // synchronous paint to happen even if the view manager's
1156 // refresh is disabled. (Bug 97674)
1158 // Calling UpdateView() here, is necessary to add
1159 // the exposed region specified in the synchronous paint
1160 // event to the view's damaged region so that it gets
1161 // painted properly when refresh is enabled.
1163 // Note that calling UpdateView() here was deemed
1164 // to have the least impact on performance, since the
1165 // other alternative was to make Scroll() post an
1166 // async paint event for the *entire* ScrollPort or
1167 // ScrollingView's viewable area. (See bug 97674 for this
1168 // alternate patch.)
1170 UpdateView(view
, damRect
, NS_VMREFRESH_NO_SYNC
);
1180 /* Don't pass these events through. Passing them through
1181 causes performance problems on pages with lots of views/frames
1183 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1187 case NS_DISPLAYCHANGED
:
1189 //Destroy the cached backbuffer to force a new backbuffer
1190 //be constructed with the appropriate display depth.
1191 //@see bugzilla bug 6061
1192 *aStatus
= nsEventStatus_eConsumeDoDefault
;
1195 case NS_SYSCOLORCHANGED
:
1197 // Hold a refcount to the observer. The continued existence of the observer will
1198 // delay deletion of this view hierarchy should the event want to cause its
1199 // destruction in, say, some JavaScript event handler.
1200 nsView
*view
= nsView::GetViewFor(aEvent
->widget
);
1201 nsCOMPtr
<nsIViewObserver
> obs
= GetViewObserver();
1203 obs
->HandleEvent(view
, aEvent
, aStatus
);
1210 if (aEvent
->message
== NS_GOTFOCUS
) {
1211 #ifdef DEBUG_FOCUS_SUPPRESSION
1212 printf("*** 0 INFO TODO [CPEARCE] Focus changing%s\n",
1213 (nsViewManager::IsFocusSuppressed() ? " while suppressed" : ""));
1215 SetCurrentlyFocusedView(nsView::GetViewFor(aEvent
->widget
));
1217 if ((aEvent
->message
== NS_GOTFOCUS
|| aEvent
->message
== NS_LOSTFOCUS
) &&
1218 nsViewManager::IsFocusSuppressed())
1220 #ifdef DEBUG_FOCUS_SUPPRESSION
1221 printf("*** 0 INFO TODO [CPEARCE] Suppressing %s\n",
1222 (aEvent
->message
== NS_GOTFOCUS
? "NS_GOTFOCUS" : "NS_LOSTFOCUS"));
1227 if ((NS_IS_MOUSE_EVENT(aEvent
) &&
1228 // Ignore moves that we synthesize.
1229 static_cast<nsMouseEvent
*>(aEvent
)->reason
==
1230 nsMouseEvent::eReal
&&
1231 // Ignore mouse exit and enter (we'll get moves if the user
1232 // is really moving the mouse) since we get them when we
1233 // create and destroy widgets.
1234 aEvent
->message
!= NS_MOUSE_EXIT
&&
1235 aEvent
->message
!= NS_MOUSE_ENTER
) ||
1236 NS_IS_KEY_EVENT(aEvent
) ||
1237 NS_IS_IME_EVENT(aEvent
)) {
1238 gLastUserEventTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
1241 if (aEvent
->message
== NS_DEACTIVATE
) {
1243 GrabMouseEvents(nsnull
, result
);
1246 //Find the view whose coordinates system we're in.
1247 nsView
* baseView
= nsView::GetViewFor(aEvent
->widget
);
1248 nsView
* view
= baseView
;
1249 PRBool capturedEvent
= PR_FALSE
;
1251 if (!NS_IS_KEY_EVENT(aEvent
) && !NS_IS_IME_EVENT(aEvent
) &&
1252 !NS_IS_CONTEXT_MENU_KEY(aEvent
) && !NS_IS_FOCUS_EVENT(aEvent
) &&
1253 !NS_IS_QUERY_CONTENT_EVENT(aEvent
) &&
1254 aEvent
->eventStructType
!= NS_ACCESSIBLE_EVENT
) {
1255 // will dispatch using coordinates. Pretty bogus but it's consistent
1256 // with what presshell does.
1257 view
= GetDisplayRootFor(baseView
);
1260 //Find the view to which we're initially going to send the event
1262 if (NS_IS_MOUSE_EVENT(aEvent
) || NS_IS_DRAG_EVENT(aEvent
)) {
1263 nsView
* mouseGrabber
= GetMouseEventGrabber();
1265 view
= mouseGrabber
;
1266 capturedEvent
= PR_TRUE
;
1270 if (nsnull
!= view
) {
1271 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1273 if ((aEvent
->message
== NS_MOUSE_MOVE
&&
1274 static_cast<nsMouseEvent
*>(aEvent
)->reason
==
1275 nsMouseEvent::eReal
) ||
1276 aEvent
->message
== NS_MOUSE_ENTER
) {
1277 // aEvent->point is relative to the widget, i.e. the view top-left,
1278 // so we need to add the offset to the view origin
1279 nsPoint rootOffset
= baseView
->GetDimensions().TopLeft();
1280 rootOffset
+= baseView
->GetOffsetTo(RootViewManager()->mRootView
);
1281 RootViewManager()->mMouseLocation
= aEvent
->refPoint
+
1282 nsPoint(NSAppUnitsToIntPixels(rootOffset
.x
, p2a
),
1283 NSAppUnitsToIntPixels(rootOffset
.y
, p2a
));
1284 #ifdef DEBUG_MOUSE_LOCATION
1285 if (aEvent
->message
== NS_MOUSE_ENTER
)
1286 printf("[vm=%p]got mouse enter for %p\n",
1287 this, aEvent
->widget
);
1288 printf("[vm=%p]setting mouse location to (%d,%d)\n",
1289 this, mMouseLocation
.x
, mMouseLocation
.y
);
1291 if (aEvent
->message
== NS_MOUSE_ENTER
)
1292 SynthesizeMouseMove(PR_FALSE
);
1293 } else if (aEvent
->message
== NS_MOUSE_EXIT
) {
1294 // Although we only care about the mouse moving into an area
1295 // for which this view manager doesn't receive mouse move
1296 // events, we don't check which view the mouse exit was for
1297 // since this seems to vary by platform. Hopefully this
1298 // won't matter at all since we'll get the mouse move or
1299 // enter after the mouse exit when the mouse moves from one
1300 // of our widgets into another.
1301 RootViewManager()->mMouseLocation
= nsPoint(NSCOORD_NONE
, NSCOORD_NONE
);
1302 #ifdef DEBUG_MOUSE_LOCATION
1303 printf("[vm=%p]got mouse exit for %p\n",
1304 this, aEvent
->widget
);
1305 printf("[vm=%p]clearing mouse location\n",
1310 //Calculate the proper offset for the view we're going to
1311 nsPoint
offset(0, 0);
1313 if (view
!= baseView
) {
1314 //Get offset from root of baseView
1316 for (parent
= baseView
; parent
; parent
= parent
->GetParent())
1317 parent
->ConvertToParentCoords(&offset
.x
, &offset
.y
);
1319 //Subtract back offset from root of view
1320 for (parent
= view
; parent
; parent
= parent
->GetParent())
1321 parent
->ConvertFromParentCoords(&offset
.x
, &offset
.y
);
1324 // Dispatch the event
1325 nsRect baseViewDimensions
;
1326 if (baseView
!= nsnull
) {
1327 baseView
->GetDimensions(baseViewDimensions
);
1331 pt
.x
= baseViewDimensions
.x
+
1332 NSFloatPixelsToAppUnits(float(aEvent
->refPoint
.x
) + 0.5f
, p2a
);
1333 pt
.y
= baseViewDimensions
.y
+
1334 NSFloatPixelsToAppUnits(float(aEvent
->refPoint
.y
) + 0.5f
, p2a
);
1337 *aStatus
= HandleEvent(view
, pt
, aEvent
, capturedEvent
);
1340 // need to map the reply back into platform coordinates
1342 switch (aEvent
->message
) {
1344 ConvertRectAppUnitsToIntPixels(
1345 ((nsTextEvent
*)aEvent
)->theReply
.mCursorPosition
, p2a
);
1347 case NS_COMPOSITION_START
:
1348 case NS_COMPOSITION_QUERY
:
1349 ConvertRectAppUnitsToIntPixels(
1350 ((nsCompositionEvent
*)aEvent
)->theReply
.mCursorPosition
, p2a
);
1352 case NS_QUERYCARETRECT
:
1353 ConvertRectAppUnitsToIntPixels(
1354 ((nsQueryCaretRectEvent
*)aEvent
)->theReply
.mCaretRect
, p2a
);
1356 case NS_QUERY_CHARACTER_RECT
:
1357 case NS_QUERY_CARET_RECT
:
1358 ConvertRectAppUnitsToIntPixels(
1359 ((nsQueryContentEvent
*)aEvent
)->mReply
.mRect
, p2a
);
1371 nsEventStatus
nsViewManager::HandleEvent(nsView
* aView
, nsPoint aPoint
,
1372 nsGUIEvent
* aEvent
, PRBool aCaptured
) {
1373 //printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports,
1374 // event->message, event->point.x, event->point.y);
1376 // Hold a refcount to the observer. The continued existence of the observer will
1377 // delay deletion of this view hierarchy should the event want to cause its
1378 // destruction in, say, some JavaScript event handler.
1379 nsCOMPtr
<nsIViewObserver
> obs
= aView
->GetViewManager()->GetViewObserver();
1380 nsEventStatus status
= nsEventStatus_eIgnore
;
1382 obs
->HandleEvent(aView
, aEvent
, &status
);
1388 NS_IMETHODIMP
nsViewManager::GrabMouseEvents(nsIView
*aView
, PRBool
&aResult
)
1391 return RootViewManager()->GrabMouseEvents(aView
, aResult
);
1394 // Along with nsView::SetVisibility, we enforce that the mouse grabber
1395 // can never be a hidden view.
1396 if (aView
&& static_cast<nsView
*>(aView
)->GetVisibility()
1397 == nsViewVisibility_kHide
) {
1404 printf("capturing mouse events for view %x\n",aView
);
1406 printf("removing mouse capture from view %x\n",mMouseGrabber
);
1409 mMouseGrabber
= static_cast<nsView
*>(aView
);
1414 NS_IMETHODIMP
nsViewManager::GetMouseEventGrabber(nsIView
*&aView
)
1416 aView
= GetMouseEventGrabber();
1420 // Recursively reparent widgets if necessary
1422 void nsViewManager::ReparentChildWidgets(nsIView
* aView
, nsIWidget
*aNewWidget
)
1424 if (aView
->HasWidget()) {
1425 // Check to see if the parent widget is the
1426 // same as the new parent. If not then reparent
1427 // the widget, otherwise there is nothing more
1428 // to do for the view and its descendants
1429 nsIWidget
* widget
= aView
->GetWidget();
1430 nsIWidget
* parentWidget
= widget
->GetParent();
1431 // Toplevel widgets should not be reparented!
1432 if (parentWidget
&& parentWidget
!= aNewWidget
) {
1436 widget
->SetParent(aNewWidget
);
1437 NS_ASSERTION(NS_SUCCEEDED(rv
), "SetParent failed!");
1442 // Need to check each of the views children to see
1443 // if they have a widget and reparent it.
1445 nsView
* view
= static_cast<nsView
*>(aView
);
1446 for (nsView
*kid
= view
->GetFirstChild(); kid
; kid
= kid
->GetNextSibling()) {
1447 ReparentChildWidgets(kid
, aNewWidget
);
1451 // Reparent a view and its descendant views widgets if necessary
1453 void nsViewManager::ReparentWidgets(nsIView
* aView
, nsIView
*aParent
)
1455 NS_PRECONDITION(aParent
, "Must have a parent");
1456 NS_PRECONDITION(aView
, "Must have a view");
1458 // Quickly determine whether the view has pre-existing children or a
1459 // widget. In most cases the view will not have any pre-existing
1460 // children when this is called. Only in the case
1461 // where a view has been reparented by removing it from
1462 // a reinserting it into a new location in the view hierarchy do we
1463 // have to consider reparenting the existing widgets for the view and
1464 // it's descendants.
1465 nsView
* view
= static_cast<nsView
*>(aView
);
1466 if (view
->HasWidget() || view
->GetFirstChild()) {
1467 nsIWidget
* parentWidget
= aParent
->GetNearestWidget(nsnull
);
1469 ReparentChildWidgets(aView
, parentWidget
);
1472 NS_WARNING("Can not find a widget for the parent view");
1476 NS_IMETHODIMP
nsViewManager::InsertChild(nsIView
*aParent
, nsIView
*aChild
, nsIView
*aSibling
,
1479 nsView
* parent
= static_cast<nsView
*>(aParent
);
1480 nsView
* child
= static_cast<nsView
*>(aChild
);
1481 nsView
* sibling
= static_cast<nsView
*>(aSibling
);
1483 NS_PRECONDITION(nsnull
!= parent
, "null ptr");
1484 NS_PRECONDITION(nsnull
!= child
, "null ptr");
1485 NS_ASSERTION(sibling
== nsnull
|| sibling
->GetParent() == parent
,
1486 "tried to insert view with invalid sibling");
1487 NS_ASSERTION(!IsViewInserted(child
), "tried to insert an already-inserted view");
1489 if ((nsnull
!= parent
) && (nsnull
!= child
))
1491 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
1492 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
1495 if (nsnull
== aSibling
) {
1497 // insert at end of document order, i.e., before first view
1498 // this is the common case, by far
1499 parent
->InsertChild(child
, nsnull
);
1500 ReparentWidgets(child
, parent
);
1502 // insert at beginning of document order, i.e., after last view
1503 nsView
*kid
= parent
->GetFirstChild();
1504 nsView
*prev
= nsnull
;
1507 kid
= kid
->GetNextSibling();
1509 // prev is last view or null if there are no children
1510 parent
->InsertChild(child
, prev
);
1511 ReparentWidgets(child
, parent
);
1514 nsView
*kid
= parent
->GetFirstChild();
1515 nsView
*prev
= nsnull
;
1516 while (kid
&& sibling
!= kid
) {
1517 //get the next sibling view
1519 kid
= kid
->GetNextSibling();
1521 NS_ASSERTION(kid
!= nsnull
,
1522 "couldn't find sibling in child list");
1524 // insert after 'kid' in document order, i.e. before in view order
1525 parent
->InsertChild(child
, prev
);
1526 ReparentWidgets(child
, parent
);
1528 // insert before 'kid' in document order, i.e. after in view order
1529 parent
->InsertChild(child
, kid
);
1530 ReparentWidgets(child
, parent
);
1533 #else // don't keep consistent document order, but order things by z-index instead
1534 // essentially we're emulating the old InsertChild(parent, child, zindex)
1535 PRInt32 zIndex
= child
->GetZIndex();
1536 while (nsnull
!= kid
)
1538 PRInt32 idx
= kid
->GetZIndex();
1540 if (CompareZIndex(zIndex
, child
->IsTopMost(), child
->GetZIndexIsAuto(),
1541 idx
, kid
->IsTopMost(), kid
->GetZIndexIsAuto()) >= 0)
1545 kid
= kid
->GetNextSibling();
1548 parent
->InsertChild(child
, prev
);
1549 ReparentWidgets(child
, parent
);
1552 // if the parent view is marked as "floating", make the newly added view float as well.
1553 if (parent
->GetFloating())
1554 child
->SetFloating(PR_TRUE
);
1556 //and mark this area as dirty if the view is visible...
1558 if (nsViewVisibility_kHide
!= child
->GetVisibility())
1559 UpdateView(child
, NS_VMREFRESH_NO_SYNC
);
1564 NS_IMETHODIMP
nsViewManager::InsertChild(nsIView
*aParent
, nsIView
*aChild
, PRInt32 aZIndex
)
1566 // no-one really calls this with anything other than aZIndex == 0 on a fresh view
1567 // XXX this method should simply be eliminated and its callers redirected to the real method
1568 SetViewZIndex(aChild
, PR_FALSE
, aZIndex
, PR_FALSE
);
1569 return InsertChild(aParent
, aChild
, nsnull
, PR_TRUE
);
1572 NS_IMETHODIMP
nsViewManager::RemoveChild(nsIView
*aChild
)
1574 nsView
* child
= static_cast<nsView
*>(aChild
);
1575 NS_ENSURE_ARG_POINTER(child
);
1577 nsView
* parent
= child
->GetParent();
1579 if (nsnull
!= parent
)
1581 UpdateView(child
, NS_VMREFRESH_NO_SYNC
);
1582 parent
->RemoveChild(child
);
1588 NS_IMETHODIMP
nsViewManager::MoveViewBy(nsIView
*aView
, nscoord aX
, nscoord aY
)
1590 nsView
* view
= static_cast<nsView
*>(aView
);
1592 nsPoint pt
= view
->GetPosition();
1593 MoveViewTo(view
, aX
+ pt
.x
, aY
+ pt
.y
);
1597 NS_IMETHODIMP
nsViewManager::MoveViewTo(nsIView
*aView
, nscoord aX
, nscoord aY
)
1599 nsView
* view
= static_cast<nsView
*>(aView
);
1600 nsPoint oldPt
= view
->GetPosition();
1601 nsRect oldArea
= view
->GetBounds();
1602 view
->SetPosition(aX
, aY
);
1604 // only do damage control if the view is visible
1606 if ((aX
!= oldPt
.x
) || (aY
!= oldPt
.y
)) {
1607 if (view
->GetVisibility() != nsViewVisibility_kHide
) {
1608 nsView
* parentView
= view
->GetParent();
1609 UpdateView(parentView
, oldArea
, NS_VMREFRESH_NO_SYNC
);
1610 UpdateView(parentView
, view
->GetBounds(), NS_VMREFRESH_NO_SYNC
);
1616 void nsViewManager::InvalidateHorizontalBandDifference(nsView
*aView
, const nsRect
& aRect
, const nsRect
& aCutOut
,
1617 PRUint32 aUpdateFlags
, nscoord aY1
, nscoord aY2
, PRBool aInCutOut
) {
1618 nscoord height
= aY2
- aY1
;
1619 if (aRect
.x
< aCutOut
.x
) {
1620 nsRect
r(aRect
.x
, aY1
, aCutOut
.x
- aRect
.x
, height
);
1621 UpdateView(aView
, r
, aUpdateFlags
);
1623 if (!aInCutOut
&& aCutOut
.x
< aCutOut
.XMost()) {
1624 nsRect
r(aCutOut
.x
, aY1
, aCutOut
.width
, height
);
1625 UpdateView(aView
, r
, aUpdateFlags
);
1627 if (aCutOut
.XMost() < aRect
.XMost()) {
1628 nsRect
r(aCutOut
.XMost(), aY1
, aRect
.XMost() - aCutOut
.XMost(), height
);
1629 UpdateView(aView
, r
, aUpdateFlags
);
1633 void nsViewManager::InvalidateRectDifference(nsView
*aView
, const nsRect
& aRect
, const nsRect
& aCutOut
,
1634 PRUint32 aUpdateFlags
) {
1635 if (aRect
.y
< aCutOut
.y
) {
1636 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aRect
.y
, aCutOut
.y
, PR_FALSE
);
1638 if (aCutOut
.y
< aCutOut
.YMost()) {
1639 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aCutOut
.y
, aCutOut
.YMost(), PR_TRUE
);
1641 if (aCutOut
.YMost() < aRect
.YMost()) {
1642 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aCutOut
.YMost(), aRect
.YMost(), PR_FALSE
);
1646 NS_IMETHODIMP
nsViewManager::ResizeView(nsIView
*aView
, const nsRect
&aRect
, PRBool aRepaintExposedAreaOnly
)
1648 nsView
* view
= static_cast<nsView
*>(aView
);
1649 nsRect oldDimensions
;
1651 view
->GetDimensions(oldDimensions
);
1652 if (!oldDimensions
.IsExactEqual(aRect
)) {
1653 nsView
* parentView
= view
->GetParent();
1654 if (parentView
== nsnull
)
1658 // Prevent Invalidation of hidden views
1659 if (view
->GetVisibility() == nsViewVisibility_kHide
) {
1660 view
->SetDimensions(aRect
, PR_FALSE
);
1662 if (!aRepaintExposedAreaOnly
) {
1663 //Invalidate the union of the old and new size
1664 view
->SetDimensions(aRect
, PR_TRUE
);
1666 UpdateView(view
, aRect
, NS_VMREFRESH_NO_SYNC
);
1667 view
->ConvertToParentCoords(&oldDimensions
.x
, &oldDimensions
.y
);
1668 UpdateView(parentView
, oldDimensions
, NS_VMREFRESH_NO_SYNC
);
1670 view
->SetDimensions(aRect
, PR_TRUE
);
1672 InvalidateRectDifference(view
, aRect
, oldDimensions
, NS_VMREFRESH_NO_SYNC
);
1674 view
->ConvertToParentCoords(&r
.x
, &r
.y
);
1675 view
->ConvertToParentCoords(&oldDimensions
.x
, &oldDimensions
.y
);
1676 InvalidateRectDifference(parentView
, oldDimensions
, r
, NS_VMREFRESH_NO_SYNC
);
1681 // Note that if layout resizes the view and the view has a custom clip
1682 // region set, then we expect layout to update the clip region too. Thus
1683 // in the case where mClipRect has been optimized away to just be a null
1684 // pointer, and this resize is implicitly changing the clip rect, it's OK
1685 // because layout will change it back again if necessary.
1690 PRBool
nsViewManager::CanScrollWithBitBlt(nsView
* aView
, nsPoint aDelta
,
1691 nsRegion
* aUpdateRegion
)
1693 NS_ASSERTION(!IsPainting(),
1694 "View manager shouldn't be scrolling during a paint");
1695 if (IsPainting() || !mObserver
) {
1696 return PR_FALSE
; // do the safe thing
1699 nsView
* displayRoot
= GetDisplayRootFor(aView
);
1700 nsPoint displayOffset
= aView
->GetParent()->GetOffsetTo(displayRoot
);
1701 nsRect parentBounds
= aView
->GetParent()->GetDimensions() + displayOffset
;
1702 // The rect we're going to scroll is intersection of the parent bounds with its
1705 toScroll
.IntersectRect(parentBounds
+ aDelta
, parentBounds
);
1707 mObserver
->ComputeRepaintRegionForCopy(displayRoot
, aView
, -aDelta
, toScroll
,
1709 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
1711 aUpdateRegion
->MoveBy(-displayOffset
);
1713 #if defined(MOZ_WIDGET_GTK2) || defined(XP_OS2)
1714 return aUpdateRegion
->IsEmpty();
1720 NS_IMETHODIMP
nsViewManager::SetViewFloating(nsIView
*aView
, PRBool aFloating
)
1722 nsView
* view
= static_cast<nsView
*>(aView
);
1724 NS_ASSERTION(!(nsnull
== view
), "no view");
1726 view
->SetFloating(aFloating
);
1731 NS_IMETHODIMP
nsViewManager::SetViewVisibility(nsIView
*aView
, nsViewVisibility aVisible
)
1733 nsView
* view
= static_cast<nsView
*>(aView
);
1735 if (aVisible
!= view
->GetVisibility()) {
1736 view
->SetVisibility(aVisible
);
1738 if (IsViewInserted(view
)) {
1739 if (!view
->HasWidget()) {
1740 if (nsViewVisibility_kHide
== aVisible
) {
1741 nsView
* parentView
= view
->GetParent();
1743 UpdateView(parentView
, view
->GetBounds(), NS_VMREFRESH_NO_SYNC
);
1747 UpdateView(view
, NS_VMREFRESH_NO_SYNC
);
1752 // Any child views not associated with frames might not get their visibility
1753 // updated, so propagate our visibility to them. This is important because
1754 // hidden views should have all hidden children.
1755 for (nsView
* childView
= view
->GetFirstChild(); childView
;
1756 childView
= childView
->GetNextSibling()) {
1757 if (!childView
->GetClientData()) {
1758 childView
->SetVisibility(aVisible
);
1765 void nsViewManager::UpdateWidgetsForView(nsView
* aView
)
1767 NS_PRECONDITION(aView
, "Must have view!");
1769 if (aView
->HasWidget()) {
1770 aView
->GetWidget()->Update();
1773 for (nsView
* childView
= aView
->GetFirstChild();
1775 childView
= childView
->GetNextSibling()) {
1776 UpdateWidgetsForView(childView
);
1780 PRBool
nsViewManager::IsViewInserted(nsView
*aView
)
1782 if (mRootView
== aView
) {
1784 } else if (aView
->GetParent() == nsnull
) {
1787 nsView
* view
= aView
->GetParent()->GetFirstChild();
1788 while (view
!= nsnull
) {
1789 if (view
== aView
) {
1792 view
= view
->GetNextSibling();
1798 NS_IMETHODIMP
nsViewManager::SetViewZIndex(nsIView
*aView
, PRBool aAutoZIndex
, PRInt32 aZIndex
, PRBool aTopMost
)
1800 nsView
* view
= static_cast<nsView
*>(aView
);
1801 nsresult rv
= NS_OK
;
1803 NS_ASSERTION((view
!= nsnull
), "no view");
1805 // don't allow the root view's z-index to be changed. It should always be zero.
1806 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1807 if (aView
== mRootView
) {
1811 PRBool oldTopMost
= view
->IsTopMost();
1812 PRBool oldIsAuto
= view
->GetZIndexIsAuto();
1818 PRInt32 oldidx
= view
->GetZIndex();
1819 view
->SetZIndex(aAutoZIndex
, aZIndex
, aTopMost
);
1821 if (oldidx
!= aZIndex
|| oldTopMost
!= aTopMost
||
1822 oldIsAuto
!= aAutoZIndex
) {
1823 UpdateView(view
, NS_VMREFRESH_NO_SYNC
);
1829 NS_IMETHODIMP
nsViewManager::SetViewObserver(nsIViewObserver
*aObserver
)
1831 mObserver
= aObserver
;
1835 NS_IMETHODIMP
nsViewManager::GetViewObserver(nsIViewObserver
*&aObserver
)
1837 if (nsnull
!= mObserver
) {
1838 aObserver
= mObserver
;
1839 NS_ADDREF(mObserver
);
1842 return NS_ERROR_NO_INTERFACE
;
1845 NS_IMETHODIMP
nsViewManager::GetDeviceContext(nsIDeviceContext
*&aContext
)
1847 NS_IF_ADDREF(mContext
);
1848 aContext
= mContext
;
1852 already_AddRefed
<nsIRenderingContext
>
1853 nsViewManager::CreateRenderingContext(nsView
&aView
)
1855 nsView
*par
= &aView
;
1857 nsIRenderingContext
*cx
= nsnull
;
1858 nscoord ax
= 0, ay
= 0;
1862 win
= par
->GetWidget();
1866 //get absolute coordinates of view, but don't
1867 //add in view pos since the first thing you ever
1868 //need to do when painting a view is to translate
1869 //the rendering context by the views pos and other parts
1870 //of the code do this for us...
1874 par
->ConvertToParentCoords(&ax
, &ay
);
1877 par
= par
->GetParent();
1879 while (nsnull
!= par
);
1883 mContext
->CreateRenderingContext(par
, cx
);
1886 cx
->Translate(ax
, ay
);
1892 NS_IMETHODIMP
nsViewManager::DisableRefresh(void)
1895 return RootViewManager()->DisableRefresh();
1898 if (mUpdateBatchCnt
> 0)
1901 mRefreshEnabled
= PR_FALSE
;
1905 NS_IMETHODIMP
nsViewManager::EnableRefresh(PRUint32 aUpdateFlags
)
1908 return RootViewManager()->EnableRefresh(aUpdateFlags
);
1911 if (mUpdateBatchCnt
> 0)
1914 mRefreshEnabled
= PR_TRUE
;
1916 if (!mHasPendingUpdates
) {
1921 // nested batching can combine IMMEDIATE with DEFERRED. Favour
1922 // IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC.
1923 if (aUpdateFlags
& NS_VMREFRESH_IMMEDIATE
) {
1924 FlushPendingInvalidates();
1926 } else if (aUpdateFlags
& NS_VMREFRESH_DEFERRED
) {
1927 PostInvalidateEvent();
1929 FlushPendingInvalidates();
1935 nsIViewManager
* nsViewManager::BeginUpdateViewBatch(void)
1938 return RootViewManager()->BeginUpdateViewBatch();
1941 nsresult result
= NS_OK
;
1943 if (mUpdateBatchCnt
== 0) {
1944 mUpdateBatchFlags
= 0;
1945 result
= DisableRefresh();
1948 if (NS_SUCCEEDED(result
))
1954 NS_IMETHODIMP
nsViewManager::EndUpdateViewBatch(PRUint32 aUpdateFlags
)
1956 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1958 nsresult result
= NS_OK
;
1962 NS_ASSERTION(mUpdateBatchCnt
>= 0, "Invalid batch count!");
1964 if (mUpdateBatchCnt
< 0)
1966 mUpdateBatchCnt
= 0;
1967 return NS_ERROR_FAILURE
;
1970 mUpdateBatchFlags
|= aUpdateFlags
;
1971 if (mUpdateBatchCnt
== 0) {
1972 result
= EnableRefresh(mUpdateBatchFlags
);
1978 NS_IMETHODIMP
nsViewManager::SetRootScrollableView(nsIScrollableView
*aScrollable
)
1980 mRootScrollable
= aScrollable
;
1984 NS_IMETHODIMP
nsViewManager::GetRootScrollableView(nsIScrollableView
**aScrollable
)
1986 *aScrollable
= mRootScrollable
;
1990 NS_IMETHODIMP
nsViewManager::GetWidget(nsIWidget
**aWidget
)
1992 *aWidget
= GetWidget();
1993 NS_IF_ADDREF(*aWidget
);
1997 NS_IMETHODIMP
nsViewManager::ForceUpdate()
2000 return RootViewManager()->ForceUpdate();
2003 // Walk the view tree looking for widgets, and call Update() on each one
2005 UpdateWidgetsForView(mRootView
);
2011 nsPoint
nsViewManager::ComputeViewOffset(const nsView
*aView
)
2013 NS_PRECONDITION(aView
, "Null view in ComputeViewOffset?");
2015 nsPoint
origin(0, 0);
2017 const nsView
* rootView
;
2018 const nsView
* origView
= aView
;
2025 origin
+= aView
->GetPosition();
2026 aView
= aView
->GetParent();
2028 NS_ASSERTION(rootView
==
2029 origView
->GetViewManager()->RootViewManager()->GetRootView(),
2030 "Unexpected root view");
2034 void nsViewManager::ViewToWidget(nsView
*aView
, nsView
* aWidgetView
, nsRect
&aRect
) const
2036 while (aView
!= aWidgetView
) {
2037 aView
->ConvertToParentCoords(&aRect
.x
, &aRect
.y
);
2038 aView
= aView
->GetParent();
2041 // intersect aRect with bounds of aWidgetView, to prevent generating any illegal rectangles.
2043 aWidgetView
->GetDimensions(bounds
);
2044 aRect
.IntersectRect(aRect
, bounds
);
2045 // account for the view's origin not lining up with the widget's
2046 aRect
.x
-= bounds
.x
;
2047 aRect
.y
-= bounds
.y
;
2049 aRect
+= aView
->ViewToWidgetOffset();
2051 // finally, convert to device coordinates.
2052 aRect
.ScaleRoundOut(1.0f
/ mContext
->AppUnitsPerDevPixel());
2055 nsresult
nsViewManager::GetVisibleRect(nsRect
& aVisibleRect
)
2057 nsresult rv
= NS_OK
;
2059 // Get the viewport scroller
2060 nsIScrollableView
* scrollingView
;
2061 GetRootScrollableView(&scrollingView
);
2063 if (scrollingView
) {
2064 // Determine the visible rect in the scrolled view's coordinate space.
2065 // The size of the visible area is the clip view size
2066 nsScrollPortView
* clipView
= static_cast<nsScrollPortView
*>(scrollingView
);
2067 clipView
->GetDimensions(aVisibleRect
);
2069 scrollingView
->GetScrollPosition(aVisibleRect
.x
, aVisibleRect
.y
);
2071 rv
= NS_ERROR_FAILURE
;
2077 nsresult
nsViewManager::GetAbsoluteRect(nsView
*aView
, const nsRect
&aRect
,
2080 nsIScrollableView
* scrollingView
= nsnull
;
2081 GetRootScrollableView(&scrollingView
);
2082 if (nsnull
== scrollingView
) {
2083 return NS_ERROR_FAILURE
;
2086 nsIView
* scrolledIView
= nsnull
;
2087 scrollingView
->GetScrolledView(scrolledIView
);
2089 nsView
* scrolledView
= static_cast<nsView
*>(scrolledIView
);
2091 // Calculate the absolute coordinates of the aRect passed in.
2092 // aRects values are relative to aView
2094 nsView
*parentView
= aView
;
2095 while ((parentView
!= nsnull
) && (parentView
!= scrolledView
)) {
2096 parentView
->ConvertToParentCoords(&aAbsRect
.x
, &aAbsRect
.y
);
2097 parentView
= parentView
->GetParent();
2100 if (parentView
!= scrolledView
) {
2101 return NS_ERROR_FAILURE
;
2108 NS_IMETHODIMP
nsViewManager::GetRectVisibility(nsIView
*aView
,
2109 const nsRect
&aRect
,
2111 nsRectVisibility
*aRectVisibility
)
2113 nsView
* view
= static_cast<nsView
*>(aView
);
2115 // The parameter aMinTwips determines how many rows/cols of pixels must be visible on each side of the element,
2116 // in order to be counted as visible
2118 *aRectVisibility
= nsRectVisibility_kZeroAreaRect
;
2119 if (aRect
.width
== 0 || aRect
.height
== 0) {
2123 // is this view even visible?
2124 if (view
->GetVisibility() == nsViewVisibility_kHide
) {
2128 // nsViewManager::InsertChild ensures that descendants of floating views
2129 // are also marked floating.
2130 if (view
->GetFloating()) {
2131 *aRectVisibility
= nsRectVisibility_kVisible
;
2135 // Calculate the absolute coordinates for the visible rectangle
2137 if (GetVisibleRect(visibleRect
) == NS_ERROR_FAILURE
) {
2138 *aRectVisibility
= nsRectVisibility_kVisible
;
2142 // Calculate the absolute coordinates of the aRect passed in.
2143 // aRects values are relative to aView
2145 if ((GetAbsoluteRect(view
, aRect
, absRect
)) == NS_ERROR_FAILURE
) {
2146 *aRectVisibility
= nsRectVisibility_kVisible
;
2151 * If aMinTwips > 0, ensure at least aMinTwips of space around object is visible
2152 * The object is not visible if:
2153 * ((objectTop < windowTop && objectBottom < windowTop) ||
2154 * (objectBottom > windowBottom && objectTop > windowBottom) ||
2155 * (objectLeft < windowLeft && objectRight < windowLeft) ||
2156 * (objectRight > windowRight && objectLeft > windowRight))
2159 if (absRect
.y
< visibleRect
.y
&&
2160 absRect
.y
+ absRect
.height
< visibleRect
.y
+ aMinTwips
)
2161 *aRectVisibility
= nsRectVisibility_kAboveViewport
;
2162 else if (absRect
.y
+ absRect
.height
> visibleRect
.y
+ visibleRect
.height
&&
2163 absRect
.y
> visibleRect
.y
+ visibleRect
.height
- aMinTwips
)
2164 *aRectVisibility
= nsRectVisibility_kBelowViewport
;
2165 else if (absRect
.x
< visibleRect
.x
&&
2166 absRect
.x
+ absRect
.width
< visibleRect
.x
+ aMinTwips
)
2167 *aRectVisibility
= nsRectVisibility_kLeftOfViewport
;
2168 else if (absRect
.x
+ absRect
.width
> visibleRect
.x
+ visibleRect
.width
&&
2169 absRect
.x
> visibleRect
.x
+ visibleRect
.width
- aMinTwips
)
2170 *aRectVisibility
= nsRectVisibility_kRightOfViewport
;
2172 *aRectVisibility
= nsRectVisibility_kVisible
;
2178 nsViewManager::IsPainting(PRBool
& aIsPainting
)
2180 aIsPainting
= IsPainting();
2185 nsViewManager::FlushPendingInvalidates()
2187 NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!\n");
2188 NS_ASSERTION(mUpdateBatchCnt
== 0, "Must not be in an update batch!");
2189 // XXXbz this is probably not quite OK yet, if callers can explicitly
2190 // DisableRefresh while we have an event posted.
2191 // NS_ASSERTION(mRefreshEnabled, "How did we get here?");
2193 // Let all the view observers of all viewmanagers in this tree know that
2194 // we're about to "paint" (this lets them get in their invalidates now so
2195 // we don't go through two invalidate-processing cycles).
2196 NS_ASSERTION(gViewManagers
, "Better have a viewmanagers array!");
2198 // Make sure to not send WillPaint notifications while scrolling
2199 if (mScrollCnt
== 0) {
2200 // Disable refresh while we notify our view observers, so that if they do
2201 // view update batches we don't reenter this code and so that we batch
2202 // all of them together. We don't use
2203 // BeginUpdateViewBatch/EndUpdateViewBatch, since that would reenter this
2204 // exact code, but we want the effect of a single big update batch.
2205 PRBool refreshEnabled
= mRefreshEnabled
;
2206 mRefreshEnabled
= PR_FALSE
;
2210 for (index
= 0; index
< mVMCount
; index
++) {
2211 nsViewManager
* vm
= (nsViewManager
*)gViewManagers
->ElementAt(index
);
2212 if (vm
->RootViewManager() == this) {
2214 nsIViewObserver
* observer
= vm
->GetViewObserver();
2216 observer
->WillPaint();
2217 NS_ASSERTION(mUpdateBatchCnt
== 1,
2218 "Observer did not end view batch?");
2224 // Someone could have called EnableRefresh on us from inside WillPaint().
2225 // Only reset the old mRefreshEnabled value if the current value is false.
2226 if (!mRefreshEnabled
) {
2227 mRefreshEnabled
= refreshEnabled
;
2231 if (mHasPendingUpdates
) {
2232 ProcessPendingUpdates(mRootView
, PR_TRUE
);
2233 mHasPendingUpdates
= PR_FALSE
;
2238 nsViewManager::ProcessInvalidateEvent()
2240 NS_ASSERTION(IsRootVM(),
2241 "Incorrectly targeted invalidate event");
2242 // If we're in the middle of an update batch, just repost the event,
2243 // to be processed when the batch ends.
2244 PRBool processEvent
= (mUpdateBatchCnt
== 0);
2246 FlushPendingInvalidates();
2248 mInvalidateEvent
.Forget();
2249 if (!processEvent
) {
2250 // We didn't actually process this event... post a new one
2251 PostInvalidateEvent();
2256 nsViewManager::SetDefaultBackgroundColor(nscolor aColor
)
2258 mDefaultBackgroundColor
= aColor
;
2263 nsViewManager::GetDefaultBackgroundColor(nscolor
* aColor
)
2265 *aColor
= mDefaultBackgroundColor
;
2271 nsViewManager::GetLastUserEventTime(PRUint32
& aTime
)
2273 aTime
= gLastUserEventTime
;
2277 class nsSynthMouseMoveEvent
: public nsViewManagerEvent
{
2279 nsSynthMouseMoveEvent(nsViewManager
*aViewManager
,
2281 : nsViewManagerEvent(aViewManager
),
2282 mFromScroll(aFromScroll
) {
2287 mViewManager
->ProcessSynthMouseMoveEvent(mFromScroll
);
2296 nsViewManager::SynthesizeMouseMove(PRBool aFromScroll
)
2299 return RootViewManager()->SynthesizeMouseMove(aFromScroll
);
2301 if (mMouseLocation
== nsPoint(NSCOORD_NONE
, NSCOORD_NONE
))
2304 if (!mSynthMouseMoveEvent
.IsPending()) {
2305 nsRefPtr
<nsViewManagerEvent
> ev
=
2306 new nsSynthMouseMoveEvent(this, aFromScroll
);
2308 if (NS_FAILED(NS_DispatchToCurrentThread(ev
))) {
2309 NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
2310 return NS_ERROR_UNEXPECTED
;
2313 mSynthMouseMoveEvent
= ev
;
2320 * Find the first floating view with a widget in a postorder traversal of the
2321 * view tree that contains the point. Thus more deeply nested floating views
2322 * are preferred over their ancestors, and floating views earlier in the
2323 * view hierarchy (i.e., added later) are preferred over their siblings.
2324 * This is adequate for finding the "topmost" floating view under a point,
2325 * given that floating views don't supporting having a specific z-index.
2327 * We cannot exit early when aPt is outside the view bounds, because floating
2328 * views aren't necessarily included in their parent's bounds, so this could
2329 * traverse the entire view hierarchy --- use carefully.
2331 static nsView
* FindFloatingViewContaining(nsView
* aView
, nsPoint aPt
)
2333 for (nsView
* v
= aView
->GetFirstChild(); v
; v
= v
->GetNextSibling()) {
2334 nsView
* r
= FindFloatingViewContaining(v
, aPt
- v
->GetOffsetTo(aView
));
2339 if (aView
->GetFloating() && aView
->HasWidget() &&
2340 aView
->GetDimensions().Contains(aPt
) && IsViewVisible(aView
))
2347 nsViewManager::ProcessSynthMouseMoveEvent(PRBool aFromScroll
)
2349 // allow new event to be posted while handling this one only if the
2350 // source of the event is a scroll (to prevent infinite reflow loops)
2352 mSynthMouseMoveEvent
.Forget();
2354 NS_ASSERTION(IsRootVM(), "Only the root view manager should be here");
2356 if (mMouseLocation
== nsPoint(NSCOORD_NONE
, NSCOORD_NONE
) || !mRootView
) {
2357 mSynthMouseMoveEvent
.Forget();
2361 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
2362 // we need to access members after we call DispatchEvent).
2363 nsCOMPtr
<nsIViewManager
> kungFuDeathGrip(this);
2365 #ifdef DEBUG_MOUSE_LOCATION
2366 printf("[vm=%p]synthesizing mouse move to (%d,%d)\n",
2367 this, mMouseLocation
.x
, mMouseLocation
.y
);
2370 nsPoint pt
= mMouseLocation
;
2371 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
2372 pt
.x
= NSIntPixelsToAppUnits(mMouseLocation
.x
, p2a
);
2373 pt
.y
= NSIntPixelsToAppUnits(mMouseLocation
.y
, p2a
);
2374 // This could be a bit slow (traverses entire view hierarchy)
2375 // but it's OK to do it once per synthetic mouse event
2376 nsView
* view
= FindFloatingViewContaining(mRootView
, pt
);
2377 nsPoint
offset(0, 0);
2381 offset
= view
->GetOffsetTo(mRootView
);
2382 offset
.x
= NSAppUnitsToIntPixels(offset
.x
, p2a
);
2383 offset
.y
= NSAppUnitsToIntPixels(offset
.y
, p2a
);
2385 nsMouseEvent
event(PR_TRUE
, NS_MOUSE_MOVE
, view
->GetWidget(),
2386 nsMouseEvent::eSynthesized
);
2387 event
.refPoint
= mMouseLocation
- offset
;
2388 event
.time
= PR_IntervalNow();
2389 // XXX set event.isShift, event.isControl, event.isAlt, event.isMeta ?
2391 nsEventStatus status
;
2392 view
->GetViewManager()->DispatchEvent(&event
, &status
);
2395 mSynthMouseMoveEvent
.Forget();
2399 nsViewManager::InvalidateHierarchy()
2403 NS_RELEASE(mRootViewManager
);
2405 nsView
*parent
= mRootView
->GetParent();
2407 mRootViewManager
= parent
->GetViewManager()->RootViewManager();
2408 NS_ADDREF(mRootViewManager
);
2409 NS_ASSERTION(mRootViewManager
!= this,
2410 "Root view had a parent, but it has the same view manager");
2412 mRootViewManager
= this;