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 // if this is a floating view, it isn't covered by any widgets other than
898 // its children. In that case we walk up to its parent widget and use
899 // that as the root to update from. This also means we update areas that
900 // may be outside the parent view(s), which is necessary for floats.
901 if (view
->GetFloating()) {
902 nsView
* widgetParent
= view
;
904 while (!widgetParent
->HasWidget()) {
905 widgetParent
->ConvertToParentCoords(&damagedRect
.x
, &damagedRect
.y
);
906 widgetParent
= widgetParent
->GetParent();
909 UpdateWidgetArea(widgetParent
, nsRegion(damagedRect
), nsnull
);
911 // Propagate the update to the root widget of the root view manager, since
912 // iframes, for example, can overlap each other and be translucent. So we
913 // have to possibly invalidate our rect in each of the widgets we have
915 damagedRect
.MoveBy(ComputeViewOffset(view
));
917 UpdateWidgetArea(RootViewManager()->GetRootView(), nsRegion(damagedRect
), nsnull
);
920 RootViewManager()->IncrementUpdateCount();
922 if (!IsRefreshEnabled()) {
926 // See if we should do an immediate refresh or wait
927 if (aUpdateFlags
& NS_VMREFRESH_IMMEDIATE
) {
934 NS_IMETHODIMP
nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags
)
936 if (RootViewManager() != this) {
937 return RootViewManager()->UpdateAllViews(aUpdateFlags
);
940 UpdateViews(mRootView
, aUpdateFlags
);
944 void nsViewManager::UpdateViews(nsView
*aView
, PRUint32 aUpdateFlags
)
947 UpdateView(aView
, aUpdateFlags
);
949 // update all children as well.
950 nsView
* childView
= aView
->GetFirstChild();
951 while (nsnull
!= childView
) {
952 UpdateViews(childView
, aUpdateFlags
);
953 childView
= childView
->GetNextSibling();
957 nsView
*nsViewManager::sCurrentlyFocusView
= nsnull
;
958 nsView
*nsViewManager::sViewFocusedBeforeSuppression
= nsnull
;
959 PRBool
nsViewManager::sFocusSuppressed
= PR_FALSE
;
961 void nsViewManager::SuppressFocusEvents(PRBool aSuppress
)
964 sFocusSuppressed
= PR_TRUE
;
965 SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView());
969 sFocusSuppressed
= PR_FALSE
;
970 if (GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression()) {
974 // We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS.
975 nsIWidget
*widget
= nsnull
;
976 nsEventStatus status
;
978 // Backup what is focused before we send the blur. If the
979 // blur causes a focus change, keep that new focus change,
980 // don't overwrite with the old "currently focused view".
981 nsIView
*currentFocusBeforeBlur
= GetCurrentlyFocusedView();
983 // Send NS_LOSTFOCUS to widget that was focused before
984 // focus/blur suppression.
985 if (GetViewFocusedBeforeSuppression()) {
986 widget
= GetViewFocusedBeforeSuppression()->GetWidget();
988 #ifdef DEBUG_FOCUS_SUPPRESSION
989 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_LOSTFOCUS\n");
991 nsGUIEvent
event(PR_TRUE
, NS_LOSTFOCUS
, widget
);
992 widget
->DispatchEvent(&event
, status
);
996 // Send NS_GOTFOCUS to the widget that we think should be focused.
997 if (GetCurrentlyFocusedView() &&
998 currentFocusBeforeBlur
== GetCurrentlyFocusedView())
1000 widget
= GetCurrentlyFocusedView()->GetWidget();
1002 #ifdef DEBUG_FOCUS_SUPPRESSION
1003 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_GOTFOCUS\n");
1005 nsGUIEvent
event(PR_TRUE
, NS_GOTFOCUS
, widget
);
1006 widget
->DispatchEvent(&event
, status
);
1012 static void ConvertRectAppUnitsToIntPixels(nsRect
& aRect
, PRInt32 p2a
)
1014 aRect
.x
= NSAppUnitsToIntPixels(aRect
.x
, p2a
);
1015 aRect
.y
= NSAppUnitsToIntPixels(aRect
.y
, p2a
);
1016 aRect
.width
= NSAppUnitsToIntPixels(aRect
.width
, p2a
);
1017 aRect
.height
= NSAppUnitsToIntPixels(aRect
.height
, p2a
);
1020 NS_IMETHODIMP
nsViewManager::DispatchEvent(nsGUIEvent
*aEvent
, nsEventStatus
*aStatus
)
1022 *aStatus
= nsEventStatus_eIgnore
;
1024 switch(aEvent
->message
)
1028 nsView
* view
= nsView::GetViewFor(aEvent
->widget
);
1032 nscoord width
= ((nsSizeEvent
*)aEvent
)->windowSize
->width
;
1033 nscoord height
= ((nsSizeEvent
*)aEvent
)->windowSize
->height
;
1034 width
= ((nsSizeEvent
*)aEvent
)->mWinWidth
;
1035 height
= ((nsSizeEvent
*)aEvent
)->mWinHeight
;
1037 // The root view may not be set if this is the resize associated with
1040 if (view
== mRootView
)
1042 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1043 SetWindowDimensions(NSIntPixelsToAppUnits(width
, p2a
),
1044 NSIntPixelsToAppUnits(height
, p2a
));
1045 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1054 nsPaintEvent
*event
= static_cast<nsPaintEvent
*>(aEvent
);
1055 nsView
*view
= nsView::GetViewFor(aEvent
->widget
);
1057 if (!view
|| !mContext
)
1060 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1062 // The rect is in device units, and it's in the coordinate space of its
1063 // associated window.
1064 nsCOMPtr
<nsIRegion
> region
= event
->region
;
1066 if (NS_FAILED(CreateRegion(getter_AddRefs(region
))))
1069 const nsRect
& damrect
= *event
->rect
;
1070 region
->SetTo(damrect
.x
, damrect
.y
, damrect
.width
, damrect
.height
);
1073 if (region
->IsEmpty())
1077 if (IsRefreshEnabled()) {
1078 // If an ancestor widget was hidden and then shown, we could
1079 // have a delayed resize to handle.
1080 PRBool didResize
= PR_FALSE
;
1081 for (nsViewManager
*vm
= this; vm
;
1082 vm
= vm
->mRootView
->GetParent()
1083 ? vm
->mRootView
->GetParent()->GetViewManager()
1085 if (vm
->mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
) &&
1086 IsViewVisible(vm
->mRootView
)) {
1087 vm
->FlushDelayedResize();
1090 vm
->UpdateView(vm
->mRootView
, NS_VMREFRESH_NO_SYNC
);
1091 didResize
= PR_TRUE
;
1093 // not sure if it's valid for us to claim that we
1094 // ignored this, but we're going to do so anyway, since
1095 // we didn't actually paint anything
1096 *aStatus
= nsEventStatus_eIgnore
;
1101 //NS_ASSERTION(IsViewVisible(view), "painting an invisible view");
1103 // Just notify our own view observer that we're about to paint
1104 // XXXbz do we need to notify other view observers for viewmanagers
1106 // Make sure to not send WillPaint notifications while scrolling
1107 nsRefPtr
<nsViewManager
> rootVM
= RootViewManager();
1109 nsIWidget
*widget
= mRootView
->GetWidget();
1110 PRBool transparentWindow
= PR_FALSE
;
1112 transparentWindow
= widget
->GetTransparencyMode() == eTransparencyTransparent
;
1114 if (rootVM
->mScrollCnt
== 0 && !transparentWindow
) {
1115 nsIViewObserver
* observer
= GetViewObserver();
1117 // Do an update view batch. Make sure not to do it DEFERRED,
1118 // since that would effectively delay any invalidates that are
1119 // triggered by the WillPaint notification (they'd happen when
1120 // the invalid event fires, which is later than the reflow
1121 // event would fire and could end up being after some timer
1122 // events, leading to frame dropping in DHTML). Note that the
1123 // observer may try to reenter this code from inside
1124 // WillPaint() by trying to do a synchronous paint, but since
1125 // refresh will be disabled it won't be able to do the paint.
1126 // We should really sort out the rules on our synch painting
1128 UpdateViewBatch
batch(this);
1129 observer
->WillPaint();
1130 batch
.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC
);
1132 // Get the view pointer again since the code above might have
1133 // destroyed it (bug 378273).
1134 view
= nsView::GetViewFor(aEvent
->widget
);
1137 // Make sure to sync up any widget geometry changes we
1138 // have pending before we paint.
1139 if (rootVM
->mHasPendingUpdates
) {
1140 rootVM
->ProcessPendingUpdates(mRootView
, PR_FALSE
);
1144 Refresh(view
, event
->renderingContext
, region
,
1145 NS_VMREFRESH_DOUBLE_BUFFER
);
1149 // since we got an NS_PAINT event, we need to
1150 // draw something so we don't get blank areas.
1152 region
->GetBoundingBox(&damRect
.x
, &damRect
.y
, &damRect
.width
, &damRect
.height
);
1153 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1154 damRect
.ScaleRoundOut(float(p2a
));
1155 DefaultRefresh(view
, event
->renderingContext
, &damRect
);
1157 // Clients like the editor can trigger multiple
1158 // reflows during what the user perceives as a single
1159 // edit operation, so it disables view manager
1160 // refreshing until the edit operation is complete
1161 // so that users don't see the intermediate steps.
1163 // Unfortunately some of these reflows can trigger
1164 // nsScrollPortView and nsScrollingView Scroll() calls
1165 // which in most cases force an immediate BitBlt and
1166 // synchronous paint to happen even if the view manager's
1167 // refresh is disabled. (Bug 97674)
1169 // Calling UpdateView() here, is necessary to add
1170 // the exposed region specified in the synchronous paint
1171 // event to the view's damaged region so that it gets
1172 // painted properly when refresh is enabled.
1174 // Note that calling UpdateView() here was deemed
1175 // to have the least impact on performance, since the
1176 // other alternative was to make Scroll() post an
1177 // async paint event for the *entire* ScrollPort or
1178 // ScrollingView's viewable area. (See bug 97674 for this
1179 // alternate patch.)
1181 UpdateView(view
, damRect
, NS_VMREFRESH_NO_SYNC
);
1191 /* Don't pass these events through. Passing them through
1192 causes performance problems on pages with lots of views/frames
1194 *aStatus
= nsEventStatus_eConsumeNoDefault
;
1198 case NS_DISPLAYCHANGED
:
1200 //Destroy the cached backbuffer to force a new backbuffer
1201 //be constructed with the appropriate display depth.
1202 //@see bugzilla bug 6061
1203 *aStatus
= nsEventStatus_eConsumeDoDefault
;
1206 case NS_SYSCOLORCHANGED
:
1208 // Hold a refcount to the observer. The continued existence of the observer will
1209 // delay deletion of this view hierarchy should the event want to cause its
1210 // destruction in, say, some JavaScript event handler.
1211 nsView
*view
= nsView::GetViewFor(aEvent
->widget
);
1212 nsCOMPtr
<nsIViewObserver
> obs
= GetViewObserver();
1214 obs
->HandleEvent(view
, aEvent
, aStatus
);
1221 if (aEvent
->message
== NS_GOTFOCUS
) {
1222 #ifdef DEBUG_FOCUS_SUPPRESSION
1223 printf("*** 0 INFO TODO [CPEARCE] Focus changing%s\n",
1224 (nsViewManager::IsFocusSuppressed() ? " while suppressed" : ""));
1226 SetCurrentlyFocusedView(nsView::GetViewFor(aEvent
->widget
));
1228 if ((aEvent
->message
== NS_GOTFOCUS
|| aEvent
->message
== NS_LOSTFOCUS
) &&
1229 nsViewManager::IsFocusSuppressed())
1231 #ifdef DEBUG_FOCUS_SUPPRESSION
1232 printf("*** 0 INFO TODO [CPEARCE] Suppressing %s\n",
1233 (aEvent
->message
== NS_GOTFOCUS
? "NS_GOTFOCUS" : "NS_LOSTFOCUS"));
1238 if ((NS_IS_MOUSE_EVENT(aEvent
) &&
1239 // Ignore moves that we synthesize.
1240 static_cast<nsMouseEvent
*>(aEvent
)->reason
==
1241 nsMouseEvent::eReal
&&
1242 // Ignore mouse exit and enter (we'll get moves if the user
1243 // is really moving the mouse) since we get them when we
1244 // create and destroy widgets.
1245 aEvent
->message
!= NS_MOUSE_EXIT
&&
1246 aEvent
->message
!= NS_MOUSE_ENTER
) ||
1247 NS_IS_KEY_EVENT(aEvent
) ||
1248 NS_IS_IME_EVENT(aEvent
)) {
1249 gLastUserEventTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
1252 if (aEvent
->message
== NS_DEACTIVATE
) {
1254 GrabMouseEvents(nsnull
, result
);
1257 //Find the view whose coordinates system we're in.
1258 nsView
* baseView
= nsView::GetViewFor(aEvent
->widget
);
1259 nsView
* view
= baseView
;
1260 PRBool capturedEvent
= PR_FALSE
;
1262 if (!NS_IS_KEY_EVENT(aEvent
) && !NS_IS_IME_EVENT(aEvent
) &&
1263 !NS_IS_CONTEXT_MENU_KEY(aEvent
) && !NS_IS_FOCUS_EVENT(aEvent
) &&
1264 !NS_IS_QUERY_CONTENT_EVENT(aEvent
) &&
1265 aEvent
->eventStructType
!= NS_ACCESSIBLE_EVENT
) {
1266 // will dispatch using coordinates. Pretty bogus but it's consistent
1267 // with what presshell does.
1268 view
= GetDisplayRootFor(baseView
);
1271 //Find the view to which we're initially going to send the event
1273 if (NS_IS_MOUSE_EVENT(aEvent
) || NS_IS_DRAG_EVENT(aEvent
)) {
1274 nsView
* mouseGrabber
= GetMouseEventGrabber();
1276 view
= mouseGrabber
;
1277 capturedEvent
= PR_TRUE
;
1281 if (nsnull
!= view
) {
1282 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
1284 if ((aEvent
->message
== NS_MOUSE_MOVE
&&
1285 static_cast<nsMouseEvent
*>(aEvent
)->reason
==
1286 nsMouseEvent::eReal
) ||
1287 aEvent
->message
== NS_MOUSE_ENTER
||
1288 aEvent
->message
== NS_MOUSE_BUTTON_DOWN
||
1289 aEvent
->message
== NS_MOUSE_BUTTON_UP
) {
1290 // aEvent->point is relative to the widget, i.e. the view top-left,
1291 // so we need to add the offset to the view origin
1292 nsPoint rootOffset
= baseView
->GetDimensions().TopLeft();
1293 rootOffset
+= baseView
->GetOffsetTo(RootViewManager()->mRootView
);
1294 RootViewManager()->mMouseLocation
= aEvent
->refPoint
+
1295 nsPoint(NSAppUnitsToIntPixels(rootOffset
.x
, p2a
),
1296 NSAppUnitsToIntPixels(rootOffset
.y
, p2a
));
1297 #ifdef DEBUG_MOUSE_LOCATION
1298 if (aEvent
->message
== NS_MOUSE_ENTER
)
1299 printf("[vm=%p]got mouse enter for %p\n",
1300 this, aEvent
->widget
);
1301 printf("[vm=%p]setting mouse location to (%d,%d)\n",
1302 this, mMouseLocation
.x
, mMouseLocation
.y
);
1304 if (aEvent
->message
== NS_MOUSE_ENTER
)
1305 SynthesizeMouseMove(PR_FALSE
);
1306 } else if (aEvent
->message
== NS_MOUSE_EXIT
) {
1307 // Although we only care about the mouse moving into an area
1308 // for which this view manager doesn't receive mouse move
1309 // events, we don't check which view the mouse exit was for
1310 // since this seems to vary by platform. Hopefully this
1311 // won't matter at all since we'll get the mouse move or
1312 // enter after the mouse exit when the mouse moves from one
1313 // of our widgets into another.
1314 RootViewManager()->mMouseLocation
= nsPoint(NSCOORD_NONE
, NSCOORD_NONE
);
1315 #ifdef DEBUG_MOUSE_LOCATION
1316 printf("[vm=%p]got mouse exit for %p\n",
1317 this, aEvent
->widget
);
1318 printf("[vm=%p]clearing mouse location\n",
1323 //Calculate the proper offset for the view we're going to
1324 nsPoint
offset(0, 0);
1326 if (view
!= baseView
) {
1327 //Get offset from root of baseView
1329 for (parent
= baseView
; parent
; parent
= parent
->GetParent())
1330 parent
->ConvertToParentCoords(&offset
.x
, &offset
.y
);
1332 //Subtract back offset from root of view
1333 for (parent
= view
; parent
; parent
= parent
->GetParent())
1334 parent
->ConvertFromParentCoords(&offset
.x
, &offset
.y
);
1337 // Dispatch the event
1338 nsRect baseViewDimensions
;
1339 if (baseView
!= nsnull
) {
1340 baseView
->GetDimensions(baseViewDimensions
);
1344 pt
.x
= baseViewDimensions
.x
+
1345 NSFloatPixelsToAppUnits(float(aEvent
->refPoint
.x
) + 0.5f
, p2a
);
1346 pt
.y
= baseViewDimensions
.y
+
1347 NSFloatPixelsToAppUnits(float(aEvent
->refPoint
.y
) + 0.5f
, p2a
);
1350 *aStatus
= HandleEvent(view
, pt
, aEvent
, capturedEvent
);
1353 // need to map the reply back into platform coordinates
1355 switch (aEvent
->message
) {
1357 ConvertRectAppUnitsToIntPixels(
1358 ((nsTextEvent
*)aEvent
)->theReply
.mCursorPosition
, p2a
);
1360 case NS_COMPOSITION_START
:
1361 case NS_COMPOSITION_QUERY
:
1362 ConvertRectAppUnitsToIntPixels(
1363 ((nsCompositionEvent
*)aEvent
)->theReply
.mCursorPosition
, p2a
);
1365 case NS_QUERY_CHARACTER_RECT
:
1366 case NS_QUERY_CARET_RECT
:
1367 ConvertRectAppUnitsToIntPixels(
1368 ((nsQueryContentEvent
*)aEvent
)->mReply
.mRect
, p2a
);
1380 nsEventStatus
nsViewManager::HandleEvent(nsView
* aView
, nsPoint aPoint
,
1381 nsGUIEvent
* aEvent
, PRBool aCaptured
) {
1382 //printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports,
1383 // event->message, event->point.x, event->point.y);
1385 // Hold a refcount to the observer. The continued existence of the observer will
1386 // delay deletion of this view hierarchy should the event want to cause its
1387 // destruction in, say, some JavaScript event handler.
1388 nsCOMPtr
<nsIViewObserver
> obs
= aView
->GetViewManager()->GetViewObserver();
1389 nsEventStatus status
= nsEventStatus_eIgnore
;
1391 obs
->HandleEvent(aView
, aEvent
, &status
);
1397 NS_IMETHODIMP
nsViewManager::GrabMouseEvents(nsIView
*aView
, PRBool
&aResult
)
1400 return RootViewManager()->GrabMouseEvents(aView
, aResult
);
1403 // Along with nsView::SetVisibility, we enforce that the mouse grabber
1404 // can never be a hidden view.
1405 if (aView
&& static_cast<nsView
*>(aView
)->GetVisibility()
1406 == nsViewVisibility_kHide
) {
1413 printf("capturing mouse events for view %x\n",aView
);
1415 printf("removing mouse capture from view %x\n",mMouseGrabber
);
1418 mMouseGrabber
= static_cast<nsView
*>(aView
);
1423 NS_IMETHODIMP
nsViewManager::GetMouseEventGrabber(nsIView
*&aView
)
1425 aView
= GetMouseEventGrabber();
1429 // Recursively reparent widgets if necessary
1431 void nsViewManager::ReparentChildWidgets(nsIView
* aView
, nsIWidget
*aNewWidget
)
1433 if (aView
->HasWidget()) {
1434 // Check to see if the parent widget is the
1435 // same as the new parent. If not then reparent
1436 // the widget, otherwise there is nothing more
1437 // to do for the view and its descendants
1438 nsIWidget
* widget
= aView
->GetWidget();
1439 nsIWidget
* parentWidget
= widget
->GetParent();
1440 // Toplevel widgets should not be reparented!
1441 if (parentWidget
&& parentWidget
!= aNewWidget
) {
1445 widget
->SetParent(aNewWidget
);
1446 NS_ASSERTION(NS_SUCCEEDED(rv
), "SetParent failed!");
1451 // Need to check each of the views children to see
1452 // if they have a widget and reparent it.
1454 nsView
* view
= static_cast<nsView
*>(aView
);
1455 for (nsView
*kid
= view
->GetFirstChild(); kid
; kid
= kid
->GetNextSibling()) {
1456 ReparentChildWidgets(kid
, aNewWidget
);
1460 // Reparent a view and its descendant views widgets if necessary
1462 void nsViewManager::ReparentWidgets(nsIView
* aView
, nsIView
*aParent
)
1464 NS_PRECONDITION(aParent
, "Must have a parent");
1465 NS_PRECONDITION(aView
, "Must have a view");
1467 // Quickly determine whether the view has pre-existing children or a
1468 // widget. In most cases the view will not have any pre-existing
1469 // children when this is called. Only in the case
1470 // where a view has been reparented by removing it from
1471 // a reinserting it into a new location in the view hierarchy do we
1472 // have to consider reparenting the existing widgets for the view and
1473 // it's descendants.
1474 nsView
* view
= static_cast<nsView
*>(aView
);
1475 if (view
->HasWidget() || view
->GetFirstChild()) {
1476 nsIWidget
* parentWidget
= aParent
->GetNearestWidget(nsnull
);
1478 ReparentChildWidgets(aView
, parentWidget
);
1481 NS_WARNING("Can not find a widget for the parent view");
1485 NS_IMETHODIMP
nsViewManager::InsertChild(nsIView
*aParent
, nsIView
*aChild
, nsIView
*aSibling
,
1488 nsView
* parent
= static_cast<nsView
*>(aParent
);
1489 nsView
* child
= static_cast<nsView
*>(aChild
);
1490 nsView
* sibling
= static_cast<nsView
*>(aSibling
);
1492 NS_PRECONDITION(nsnull
!= parent
, "null ptr");
1493 NS_PRECONDITION(nsnull
!= child
, "null ptr");
1494 NS_ASSERTION(sibling
== nsnull
|| sibling
->GetParent() == parent
,
1495 "tried to insert view with invalid sibling");
1496 NS_ASSERTION(!IsViewInserted(child
), "tried to insert an already-inserted view");
1498 if ((nsnull
!= parent
) && (nsnull
!= child
))
1500 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
1501 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
1504 if (nsnull
== aSibling
) {
1506 // insert at end of document order, i.e., before first view
1507 // this is the common case, by far
1508 parent
->InsertChild(child
, nsnull
);
1509 ReparentWidgets(child
, parent
);
1511 // insert at beginning of document order, i.e., after last view
1512 nsView
*kid
= parent
->GetFirstChild();
1513 nsView
*prev
= nsnull
;
1516 kid
= kid
->GetNextSibling();
1518 // prev is last view or null if there are no children
1519 parent
->InsertChild(child
, prev
);
1520 ReparentWidgets(child
, parent
);
1523 nsView
*kid
= parent
->GetFirstChild();
1524 nsView
*prev
= nsnull
;
1525 while (kid
&& sibling
!= kid
) {
1526 //get the next sibling view
1528 kid
= kid
->GetNextSibling();
1530 NS_ASSERTION(kid
!= nsnull
,
1531 "couldn't find sibling in child list");
1533 // insert after 'kid' in document order, i.e. before in view order
1534 parent
->InsertChild(child
, prev
);
1535 ReparentWidgets(child
, parent
);
1537 // insert before 'kid' in document order, i.e. after in view order
1538 parent
->InsertChild(child
, kid
);
1539 ReparentWidgets(child
, parent
);
1542 #else // don't keep consistent document order, but order things by z-index instead
1543 // essentially we're emulating the old InsertChild(parent, child, zindex)
1544 PRInt32 zIndex
= child
->GetZIndex();
1545 while (nsnull
!= kid
)
1547 PRInt32 idx
= kid
->GetZIndex();
1549 if (CompareZIndex(zIndex
, child
->IsTopMost(), child
->GetZIndexIsAuto(),
1550 idx
, kid
->IsTopMost(), kid
->GetZIndexIsAuto()) >= 0)
1554 kid
= kid
->GetNextSibling();
1557 parent
->InsertChild(child
, prev
);
1558 ReparentWidgets(child
, parent
);
1561 // if the parent view is marked as "floating", make the newly added view float as well.
1562 if (parent
->GetFloating())
1563 child
->SetFloating(PR_TRUE
);
1565 //and mark this area as dirty if the view is visible...
1567 if (nsViewVisibility_kHide
!= child
->GetVisibility())
1568 UpdateView(child
, NS_VMREFRESH_NO_SYNC
);
1573 NS_IMETHODIMP
nsViewManager::InsertChild(nsIView
*aParent
, nsIView
*aChild
, PRInt32 aZIndex
)
1575 // no-one really calls this with anything other than aZIndex == 0 on a fresh view
1576 // XXX this method should simply be eliminated and its callers redirected to the real method
1577 SetViewZIndex(aChild
, PR_FALSE
, aZIndex
, PR_FALSE
);
1578 return InsertChild(aParent
, aChild
, nsnull
, PR_TRUE
);
1581 NS_IMETHODIMP
nsViewManager::RemoveChild(nsIView
*aChild
)
1583 nsView
* child
= static_cast<nsView
*>(aChild
);
1584 NS_ENSURE_ARG_POINTER(child
);
1586 nsView
* parent
= child
->GetParent();
1588 if (nsnull
!= parent
)
1590 UpdateView(child
, NS_VMREFRESH_NO_SYNC
);
1591 parent
->RemoveChild(child
);
1597 NS_IMETHODIMP
nsViewManager::MoveViewBy(nsIView
*aView
, nscoord aX
, nscoord aY
)
1599 nsView
* view
= static_cast<nsView
*>(aView
);
1601 nsPoint pt
= view
->GetPosition();
1602 MoveViewTo(view
, aX
+ pt
.x
, aY
+ pt
.y
);
1606 NS_IMETHODIMP
nsViewManager::MoveViewTo(nsIView
*aView
, nscoord aX
, nscoord aY
)
1608 nsView
* view
= static_cast<nsView
*>(aView
);
1609 nsPoint oldPt
= view
->GetPosition();
1610 nsRect oldArea
= view
->GetBounds();
1611 view
->SetPosition(aX
, aY
);
1613 // only do damage control if the view is visible
1615 if ((aX
!= oldPt
.x
) || (aY
!= oldPt
.y
)) {
1616 if (view
->GetVisibility() != nsViewVisibility_kHide
) {
1617 nsView
* parentView
= view
->GetParent();
1618 UpdateView(parentView
, oldArea
, NS_VMREFRESH_NO_SYNC
);
1619 UpdateView(parentView
, view
->GetBounds(), NS_VMREFRESH_NO_SYNC
);
1625 void nsViewManager::InvalidateHorizontalBandDifference(nsView
*aView
, const nsRect
& aRect
, const nsRect
& aCutOut
,
1626 PRUint32 aUpdateFlags
, nscoord aY1
, nscoord aY2
, PRBool aInCutOut
) {
1627 nscoord height
= aY2
- aY1
;
1628 if (aRect
.x
< aCutOut
.x
) {
1629 nsRect
r(aRect
.x
, aY1
, aCutOut
.x
- aRect
.x
, height
);
1630 UpdateView(aView
, r
, aUpdateFlags
);
1632 if (!aInCutOut
&& aCutOut
.x
< aCutOut
.XMost()) {
1633 nsRect
r(aCutOut
.x
, aY1
, aCutOut
.width
, height
);
1634 UpdateView(aView
, r
, aUpdateFlags
);
1636 if (aCutOut
.XMost() < aRect
.XMost()) {
1637 nsRect
r(aCutOut
.XMost(), aY1
, aRect
.XMost() - aCutOut
.XMost(), height
);
1638 UpdateView(aView
, r
, aUpdateFlags
);
1642 void nsViewManager::InvalidateRectDifference(nsView
*aView
, const nsRect
& aRect
, const nsRect
& aCutOut
,
1643 PRUint32 aUpdateFlags
) {
1644 if (aRect
.y
< aCutOut
.y
) {
1645 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aRect
.y
, aCutOut
.y
, PR_FALSE
);
1647 if (aCutOut
.y
< aCutOut
.YMost()) {
1648 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aCutOut
.y
, aCutOut
.YMost(), PR_TRUE
);
1650 if (aCutOut
.YMost() < aRect
.YMost()) {
1651 InvalidateHorizontalBandDifference(aView
, aRect
, aCutOut
, aUpdateFlags
, aCutOut
.YMost(), aRect
.YMost(), PR_FALSE
);
1655 NS_IMETHODIMP
nsViewManager::ResizeView(nsIView
*aView
, const nsRect
&aRect
, PRBool aRepaintExposedAreaOnly
)
1657 nsView
* view
= static_cast<nsView
*>(aView
);
1658 nsRect oldDimensions
;
1660 view
->GetDimensions(oldDimensions
);
1661 if (!oldDimensions
.IsExactEqual(aRect
)) {
1662 nsView
* parentView
= view
->GetParent();
1663 if (parentView
== nsnull
)
1667 // Prevent Invalidation of hidden views
1668 if (view
->GetVisibility() == nsViewVisibility_kHide
) {
1669 view
->SetDimensions(aRect
, PR_FALSE
);
1671 if (!aRepaintExposedAreaOnly
) {
1672 //Invalidate the union of the old and new size
1673 view
->SetDimensions(aRect
, PR_TRUE
);
1675 UpdateView(view
, aRect
, NS_VMREFRESH_NO_SYNC
);
1676 view
->ConvertToParentCoords(&oldDimensions
.x
, &oldDimensions
.y
);
1677 UpdateView(parentView
, oldDimensions
, NS_VMREFRESH_NO_SYNC
);
1679 view
->SetDimensions(aRect
, PR_TRUE
);
1681 InvalidateRectDifference(view
, aRect
, oldDimensions
, NS_VMREFRESH_NO_SYNC
);
1683 view
->ConvertToParentCoords(&r
.x
, &r
.y
);
1684 view
->ConvertToParentCoords(&oldDimensions
.x
, &oldDimensions
.y
);
1685 InvalidateRectDifference(parentView
, oldDimensions
, r
, NS_VMREFRESH_NO_SYNC
);
1690 // Note that if layout resizes the view and the view has a custom clip
1691 // region set, then we expect layout to update the clip region too. Thus
1692 // in the case where mClipRect has been optimized away to just be a null
1693 // pointer, and this resize is implicitly changing the clip rect, it's OK
1694 // because layout will change it back again if necessary.
1699 static double GetArea(const nsRect
& aRect
)
1701 return double(aRect
.width
)*double(aRect
.height
);
1704 PRBool
nsViewManager::CanScrollWithBitBlt(nsView
* aView
, nsPoint aDelta
,
1705 nsRegion
* aUpdateRegion
)
1707 NS_ASSERTION(!IsPainting(),
1708 "View manager shouldn't be scrolling during a paint");
1709 if (IsPainting() || !mObserver
) {
1710 return PR_FALSE
; // do the safe thing
1713 nsView
* displayRoot
= GetDisplayRootFor(aView
);
1714 nsPoint displayOffset
= aView
->GetParent()->GetOffsetTo(displayRoot
);
1715 nsRect parentBounds
= aView
->GetParent()->GetDimensions() + displayOffset
;
1716 // The rect we're going to scroll is intersection of the parent bounds with its
1719 toScroll
.IntersectRect(parentBounds
+ aDelta
, parentBounds
);
1721 mObserver
->ComputeRepaintRegionForCopy(displayRoot
, aView
, -aDelta
, toScroll
,
1723 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
1725 aUpdateRegion
->MoveBy(-displayOffset
);
1727 #if defined(MOZ_WIDGET_GTK2) || defined(XP_OS2)
1728 return aUpdateRegion
->IsEmpty();
1730 return GetArea(aUpdateRegion
->GetBounds()) < GetArea(parentBounds
)/2;
1734 NS_IMETHODIMP
nsViewManager::SetViewFloating(nsIView
*aView
, PRBool aFloating
)
1736 nsView
* view
= static_cast<nsView
*>(aView
);
1738 NS_ASSERTION(!(nsnull
== view
), "no view");
1740 view
->SetFloating(aFloating
);
1745 NS_IMETHODIMP
nsViewManager::SetViewVisibility(nsIView
*aView
, nsViewVisibility aVisible
)
1747 nsView
* view
= static_cast<nsView
*>(aView
);
1749 if (aVisible
!= view
->GetVisibility()) {
1750 view
->SetVisibility(aVisible
);
1752 if (IsViewInserted(view
)) {
1753 if (!view
->HasWidget()) {
1754 if (nsViewVisibility_kHide
== aVisible
) {
1755 nsView
* parentView
= view
->GetParent();
1757 UpdateView(parentView
, view
->GetBounds(), NS_VMREFRESH_NO_SYNC
);
1761 UpdateView(view
, NS_VMREFRESH_NO_SYNC
);
1766 // Any child views not associated with frames might not get their visibility
1767 // updated, so propagate our visibility to them. This is important because
1768 // hidden views should have all hidden children.
1769 for (nsView
* childView
= view
->GetFirstChild(); childView
;
1770 childView
= childView
->GetNextSibling()) {
1771 if (!childView
->GetClientData()) {
1772 childView
->SetVisibility(aVisible
);
1779 void nsViewManager::UpdateWidgetsForView(nsView
* aView
)
1781 NS_PRECONDITION(aView
, "Must have view!");
1783 if (aView
->HasWidget()) {
1784 aView
->GetWidget()->Update();
1787 for (nsView
* childView
= aView
->GetFirstChild();
1789 childView
= childView
->GetNextSibling()) {
1790 UpdateWidgetsForView(childView
);
1794 PRBool
nsViewManager::IsViewInserted(nsView
*aView
)
1796 if (mRootView
== aView
) {
1798 } else if (aView
->GetParent() == nsnull
) {
1801 nsView
* view
= aView
->GetParent()->GetFirstChild();
1802 while (view
!= nsnull
) {
1803 if (view
== aView
) {
1806 view
= view
->GetNextSibling();
1812 NS_IMETHODIMP
nsViewManager::SetViewZIndex(nsIView
*aView
, PRBool aAutoZIndex
, PRInt32 aZIndex
, PRBool aTopMost
)
1814 nsView
* view
= static_cast<nsView
*>(aView
);
1815 nsresult rv
= NS_OK
;
1817 NS_ASSERTION((view
!= nsnull
), "no view");
1819 // don't allow the root view's z-index to be changed. It should always be zero.
1820 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1821 if (aView
== mRootView
) {
1825 PRBool oldTopMost
= view
->IsTopMost();
1826 PRBool oldIsAuto
= view
->GetZIndexIsAuto();
1832 PRInt32 oldidx
= view
->GetZIndex();
1833 view
->SetZIndex(aAutoZIndex
, aZIndex
, aTopMost
);
1835 if (oldidx
!= aZIndex
|| oldTopMost
!= aTopMost
||
1836 oldIsAuto
!= aAutoZIndex
) {
1837 UpdateView(view
, NS_VMREFRESH_NO_SYNC
);
1843 NS_IMETHODIMP
nsViewManager::SetViewObserver(nsIViewObserver
*aObserver
)
1845 mObserver
= aObserver
;
1849 NS_IMETHODIMP
nsViewManager::GetViewObserver(nsIViewObserver
*&aObserver
)
1851 if (nsnull
!= mObserver
) {
1852 aObserver
= mObserver
;
1853 NS_ADDREF(mObserver
);
1856 return NS_ERROR_NO_INTERFACE
;
1859 NS_IMETHODIMP
nsViewManager::GetDeviceContext(nsIDeviceContext
*&aContext
)
1861 NS_IF_ADDREF(mContext
);
1862 aContext
= mContext
;
1866 already_AddRefed
<nsIRenderingContext
>
1867 nsViewManager::CreateRenderingContext(nsView
&aView
)
1869 nsView
*par
= &aView
;
1871 nsIRenderingContext
*cx
= nsnull
;
1872 nscoord ax
= 0, ay
= 0;
1876 win
= par
->GetWidget();
1880 //get absolute coordinates of view, but don't
1881 //add in view pos since the first thing you ever
1882 //need to do when painting a view is to translate
1883 //the rendering context by the views pos and other parts
1884 //of the code do this for us...
1888 par
->ConvertToParentCoords(&ax
, &ay
);
1891 par
= par
->GetParent();
1893 while (nsnull
!= par
);
1897 // XXXkt this has an origin at top-left of win ...
1898 mContext
->CreateRenderingContext(par
, cx
);
1900 // XXXkt ... but the translation is between the origins of views
1901 NS_ASSERTION(aView
.ViewToWidgetOffset()
1902 - aView
.GetDimensions().TopLeft() ==
1903 par
->ViewToWidgetOffset()
1904 - par
->GetDimensions().TopLeft(),
1905 "ViewToWidgetOffset not handled!");
1907 cx
->Translate(ax
, ay
);
1913 NS_IMETHODIMP
nsViewManager::DisableRefresh(void)
1916 return RootViewManager()->DisableRefresh();
1919 if (mUpdateBatchCnt
> 0)
1922 mRefreshEnabled
= PR_FALSE
;
1926 NS_IMETHODIMP
nsViewManager::EnableRefresh(PRUint32 aUpdateFlags
)
1929 return RootViewManager()->EnableRefresh(aUpdateFlags
);
1932 if (mUpdateBatchCnt
> 0)
1935 mRefreshEnabled
= PR_TRUE
;
1937 // nested batching can combine IMMEDIATE with DEFERRED. Favour
1938 // IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC. We need to
1939 // check for IMMEDIATE before checking mHasPendingUpdates, because
1940 // the latter might be false as far as gecko is concerned but the OS
1941 // might still have queued up expose events that it hasn't sent yet.
1942 if (aUpdateFlags
& NS_VMREFRESH_IMMEDIATE
) {
1943 FlushPendingInvalidates();
1945 } else if (!mHasPendingUpdates
) {
1947 } else if (aUpdateFlags
& NS_VMREFRESH_DEFERRED
) {
1948 PostInvalidateEvent();
1950 FlushPendingInvalidates();
1956 nsIViewManager
* nsViewManager::BeginUpdateViewBatch(void)
1959 return RootViewManager()->BeginUpdateViewBatch();
1962 nsresult result
= NS_OK
;
1964 if (mUpdateBatchCnt
== 0) {
1965 mUpdateBatchFlags
= 0;
1966 result
= DisableRefresh();
1969 if (NS_SUCCEEDED(result
))
1975 NS_IMETHODIMP
nsViewManager::EndUpdateViewBatch(PRUint32 aUpdateFlags
)
1977 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1979 nsresult result
= NS_OK
;
1983 NS_ASSERTION(mUpdateBatchCnt
>= 0, "Invalid batch count!");
1985 if (mUpdateBatchCnt
< 0)
1987 mUpdateBatchCnt
= 0;
1988 return NS_ERROR_FAILURE
;
1991 mUpdateBatchFlags
|= aUpdateFlags
;
1992 if (mUpdateBatchCnt
== 0) {
1993 result
= EnableRefresh(mUpdateBatchFlags
);
1999 NS_IMETHODIMP
nsViewManager::SetRootScrollableView(nsIScrollableView
*aScrollable
)
2001 mRootScrollable
= aScrollable
;
2005 NS_IMETHODIMP
nsViewManager::GetRootScrollableView(nsIScrollableView
**aScrollable
)
2007 *aScrollable
= mRootScrollable
;
2011 NS_IMETHODIMP
nsViewManager::GetWidget(nsIWidget
**aWidget
)
2013 *aWidget
= GetWidget();
2014 NS_IF_ADDREF(*aWidget
);
2018 NS_IMETHODIMP
nsViewManager::ForceUpdate()
2021 return RootViewManager()->ForceUpdate();
2024 // Walk the view tree looking for widgets, and call Update() on each one
2026 UpdateWidgetsForView(mRootView
);
2032 nsPoint
nsViewManager::ComputeViewOffset(const nsView
*aView
)
2034 NS_PRECONDITION(aView
, "Null view in ComputeViewOffset?");
2036 nsPoint
origin(0, 0);
2038 const nsView
* rootView
;
2039 const nsView
* origView
= aView
;
2046 origin
+= aView
->GetPosition();
2047 aView
= aView
->GetParent();
2049 NS_ASSERTION(rootView
==
2050 origView
->GetViewManager()->RootViewManager()->GetRootView(),
2051 "Unexpected root view");
2055 void nsViewManager::ViewToWidget(nsView
*aView
, nsView
* aWidgetView
, nsRect
&aRect
) const
2057 while (aView
!= aWidgetView
) {
2058 aView
->ConvertToParentCoords(&aRect
.x
, &aRect
.y
);
2059 aView
= aView
->GetParent();
2062 // intersect aRect with bounds of aWidgetView, to prevent generating any illegal rectangles.
2064 aWidgetView
->GetDimensions(bounds
);
2065 aRect
.IntersectRect(aRect
, bounds
);
2066 // account for the view's origin not lining up with the widget's
2067 aRect
.x
-= bounds
.x
;
2068 aRect
.y
-= bounds
.y
;
2070 aRect
+= aView
->ViewToWidgetOffset();
2072 // finally, convert to device coordinates.
2073 aRect
.ScaleRoundOut(1.0f
/ mContext
->AppUnitsPerDevPixel());
2076 nsresult
nsViewManager::GetVisibleRect(nsRect
& aVisibleRect
)
2078 nsresult rv
= NS_OK
;
2080 // Get the viewport scroller
2081 nsIScrollableView
* scrollingView
;
2082 GetRootScrollableView(&scrollingView
);
2084 if (scrollingView
) {
2085 // Determine the visible rect in the scrolled view's coordinate space.
2086 // The size of the visible area is the clip view size
2087 nsScrollPortView
* clipView
= static_cast<nsScrollPortView
*>(scrollingView
);
2088 clipView
->GetDimensions(aVisibleRect
);
2090 scrollingView
->GetScrollPosition(aVisibleRect
.x
, aVisibleRect
.y
);
2092 rv
= NS_ERROR_FAILURE
;
2098 nsresult
nsViewManager::GetAbsoluteRect(nsView
*aView
, const nsRect
&aRect
,
2101 nsIScrollableView
* scrollingView
= nsnull
;
2102 GetRootScrollableView(&scrollingView
);
2103 if (nsnull
== scrollingView
) {
2104 return NS_ERROR_FAILURE
;
2107 nsIView
* scrolledIView
= nsnull
;
2108 scrollingView
->GetScrolledView(scrolledIView
);
2110 nsView
* scrolledView
= static_cast<nsView
*>(scrolledIView
);
2112 // Calculate the absolute coordinates of the aRect passed in.
2113 // aRects values are relative to aView
2115 nsView
*parentView
= aView
;
2116 while ((parentView
!= nsnull
) && (parentView
!= scrolledView
)) {
2117 parentView
->ConvertToParentCoords(&aAbsRect
.x
, &aAbsRect
.y
);
2118 parentView
= parentView
->GetParent();
2121 if (parentView
!= scrolledView
) {
2122 return NS_ERROR_FAILURE
;
2129 NS_IMETHODIMP
nsViewManager::GetRectVisibility(nsIView
*aView
,
2130 const nsRect
&aRect
,
2132 nsRectVisibility
*aRectVisibility
)
2134 nsView
* view
= static_cast<nsView
*>(aView
);
2136 // The parameter aMinTwips determines how many rows/cols of pixels must be visible on each side of the element,
2137 // in order to be counted as visible
2139 *aRectVisibility
= nsRectVisibility_kZeroAreaRect
;
2140 if (aRect
.width
== 0 || aRect
.height
== 0) {
2144 // is this view even visible?
2145 if (view
->GetVisibility() == nsViewVisibility_kHide
) {
2149 // nsViewManager::InsertChild ensures that descendants of floating views
2150 // are also marked floating.
2151 if (view
->GetFloating()) {
2152 *aRectVisibility
= nsRectVisibility_kVisible
;
2156 // Calculate the absolute coordinates for the visible rectangle
2158 if (GetVisibleRect(visibleRect
) == NS_ERROR_FAILURE
) {
2159 *aRectVisibility
= nsRectVisibility_kVisible
;
2163 // Calculate the absolute coordinates of the aRect passed in.
2164 // aRects values are relative to aView
2166 if ((GetAbsoluteRect(view
, aRect
, absRect
)) == NS_ERROR_FAILURE
) {
2167 *aRectVisibility
= nsRectVisibility_kVisible
;
2172 * If aMinTwips > 0, ensure at least aMinTwips of space around object is visible
2173 * The object is not visible if:
2174 * ((objectTop < windowTop && objectBottom < windowTop) ||
2175 * (objectBottom > windowBottom && objectTop > windowBottom) ||
2176 * (objectLeft < windowLeft && objectRight < windowLeft) ||
2177 * (objectRight > windowRight && objectLeft > windowRight))
2180 if (absRect
.y
< visibleRect
.y
&&
2181 absRect
.y
+ absRect
.height
< visibleRect
.y
+ aMinTwips
)
2182 *aRectVisibility
= nsRectVisibility_kAboveViewport
;
2183 else if (absRect
.y
+ absRect
.height
> visibleRect
.y
+ visibleRect
.height
&&
2184 absRect
.y
> visibleRect
.y
+ visibleRect
.height
- aMinTwips
)
2185 *aRectVisibility
= nsRectVisibility_kBelowViewport
;
2186 else if (absRect
.x
< visibleRect
.x
&&
2187 absRect
.x
+ absRect
.width
< visibleRect
.x
+ aMinTwips
)
2188 *aRectVisibility
= nsRectVisibility_kLeftOfViewport
;
2189 else if (absRect
.x
+ absRect
.width
> visibleRect
.x
+ visibleRect
.width
&&
2190 absRect
.x
> visibleRect
.x
+ visibleRect
.width
- aMinTwips
)
2191 *aRectVisibility
= nsRectVisibility_kRightOfViewport
;
2193 *aRectVisibility
= nsRectVisibility_kVisible
;
2199 nsViewManager::IsPainting(PRBool
& aIsPainting
)
2201 aIsPainting
= IsPainting();
2206 nsViewManager::FlushPendingInvalidates()
2208 NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!\n");
2209 NS_ASSERTION(mUpdateBatchCnt
== 0, "Must not be in an update batch!");
2210 // XXXbz this is probably not quite OK yet, if callers can explicitly
2211 // DisableRefresh while we have an event posted.
2212 // NS_ASSERTION(mRefreshEnabled, "How did we get here?");
2214 // Let all the view observers of all viewmanagers in this tree know that
2215 // we're about to "paint" (this lets them get in their invalidates now so
2216 // we don't go through two invalidate-processing cycles).
2217 NS_ASSERTION(gViewManagers
, "Better have a viewmanagers array!");
2219 // Make sure to not send WillPaint notifications while scrolling
2220 if (mScrollCnt
== 0) {
2221 // Disable refresh while we notify our view observers, so that if they do
2222 // view update batches we don't reenter this code and so that we batch
2223 // all of them together. We don't use
2224 // BeginUpdateViewBatch/EndUpdateViewBatch, since that would reenter this
2225 // exact code, but we want the effect of a single big update batch.
2226 PRBool refreshEnabled
= mRefreshEnabled
;
2227 mRefreshEnabled
= PR_FALSE
;
2231 for (index
= 0; index
< mVMCount
; index
++) {
2232 nsViewManager
* vm
= (nsViewManager
*)gViewManagers
->ElementAt(index
);
2233 if (vm
->RootViewManager() == this) {
2235 nsIViewObserver
* observer
= vm
->GetViewObserver();
2237 observer
->WillPaint();
2238 NS_ASSERTION(mUpdateBatchCnt
== 1,
2239 "Observer did not end view batch?");
2245 // Someone could have called EnableRefresh on us from inside WillPaint().
2246 // Only reset the old mRefreshEnabled value if the current value is false.
2247 if (!mRefreshEnabled
) {
2248 mRefreshEnabled
= refreshEnabled
;
2252 if (mHasPendingUpdates
) {
2253 ProcessPendingUpdates(mRootView
, PR_TRUE
);
2254 mHasPendingUpdates
= PR_FALSE
;
2259 nsViewManager::ProcessInvalidateEvent()
2261 NS_ASSERTION(IsRootVM(),
2262 "Incorrectly targeted invalidate event");
2263 // If we're in the middle of an update batch, just repost the event,
2264 // to be processed when the batch ends.
2265 PRBool processEvent
= (mUpdateBatchCnt
== 0);
2267 FlushPendingInvalidates();
2269 mInvalidateEvent
.Forget();
2270 if (!processEvent
) {
2271 // We didn't actually process this event... post a new one
2272 PostInvalidateEvent();
2277 nsViewManager::SetDefaultBackgroundColor(nscolor aColor
)
2279 mDefaultBackgroundColor
= aColor
;
2284 nsViewManager::GetDefaultBackgroundColor(nscolor
* aColor
)
2286 *aColor
= mDefaultBackgroundColor
;
2292 nsViewManager::GetLastUserEventTime(PRUint32
& aTime
)
2294 aTime
= gLastUserEventTime
;
2298 class nsSynthMouseMoveEvent
: public nsViewManagerEvent
{
2300 nsSynthMouseMoveEvent(nsViewManager
*aViewManager
,
2302 : nsViewManagerEvent(aViewManager
),
2303 mFromScroll(aFromScroll
) {
2308 mViewManager
->ProcessSynthMouseMoveEvent(mFromScroll
);
2317 nsViewManager::SynthesizeMouseMove(PRBool aFromScroll
)
2320 return RootViewManager()->SynthesizeMouseMove(aFromScroll
);
2322 if (mMouseLocation
== nsPoint(NSCOORD_NONE
, NSCOORD_NONE
))
2325 if (!mSynthMouseMoveEvent
.IsPending()) {
2326 nsRefPtr
<nsViewManagerEvent
> ev
=
2327 new nsSynthMouseMoveEvent(this, aFromScroll
);
2329 if (NS_FAILED(NS_DispatchToCurrentThread(ev
))) {
2330 NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
2331 return NS_ERROR_UNEXPECTED
;
2334 mSynthMouseMoveEvent
= ev
;
2341 * Find the first floating view with a widget in a postorder traversal of the
2342 * view tree that contains the point. Thus more deeply nested floating views
2343 * are preferred over their ancestors, and floating views earlier in the
2344 * view hierarchy (i.e., added later) are preferred over their siblings.
2345 * This is adequate for finding the "topmost" floating view under a point,
2346 * given that floating views don't supporting having a specific z-index.
2348 * We cannot exit early when aPt is outside the view bounds, because floating
2349 * views aren't necessarily included in their parent's bounds, so this could
2350 * traverse the entire view hierarchy --- use carefully.
2352 static nsView
* FindFloatingViewContaining(nsView
* aView
, nsPoint aPt
)
2354 for (nsView
* v
= aView
->GetFirstChild(); v
; v
= v
->GetNextSibling()) {
2355 nsView
* r
= FindFloatingViewContaining(v
, aPt
- v
->GetOffsetTo(aView
));
2360 if (aView
->GetFloating() && aView
->HasWidget() &&
2361 aView
->GetDimensions().Contains(aPt
) && IsViewVisible(aView
))
2368 nsViewManager::ProcessSynthMouseMoveEvent(PRBool aFromScroll
)
2370 // allow new event to be posted while handling this one only if the
2371 // source of the event is a scroll (to prevent infinite reflow loops)
2373 mSynthMouseMoveEvent
.Forget();
2375 NS_ASSERTION(IsRootVM(), "Only the root view manager should be here");
2377 if (mMouseLocation
== nsPoint(NSCOORD_NONE
, NSCOORD_NONE
) || !mRootView
) {
2378 mSynthMouseMoveEvent
.Forget();
2382 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
2383 // we need to access members after we call DispatchEvent).
2384 nsCOMPtr
<nsIViewManager
> kungFuDeathGrip(this);
2386 #ifdef DEBUG_MOUSE_LOCATION
2387 printf("[vm=%p]synthesizing mouse move to (%d,%d)\n",
2388 this, mMouseLocation
.x
, mMouseLocation
.y
);
2391 nsPoint pt
= mMouseLocation
;
2392 PRInt32 p2a
= mContext
->AppUnitsPerDevPixel();
2393 pt
.x
= NSIntPixelsToAppUnits(mMouseLocation
.x
, p2a
);
2394 pt
.y
= NSIntPixelsToAppUnits(mMouseLocation
.y
, p2a
);
2395 // This could be a bit slow (traverses entire view hierarchy)
2396 // but it's OK to do it once per synthetic mouse event
2397 nsView
* view
= FindFloatingViewContaining(mRootView
, pt
);
2398 nsPoint
offset(0, 0);
2402 offset
= view
->GetOffsetTo(mRootView
);
2403 offset
.x
= NSAppUnitsToIntPixels(offset
.x
, p2a
);
2404 offset
.y
= NSAppUnitsToIntPixels(offset
.y
, p2a
);
2406 nsMouseEvent
event(PR_TRUE
, NS_MOUSE_MOVE
, view
->GetWidget(),
2407 nsMouseEvent::eSynthesized
);
2408 event
.refPoint
= mMouseLocation
- offset
;
2409 event
.time
= PR_IntervalNow();
2410 // XXX set event.isShift, event.isControl, event.isAlt, event.isMeta ?
2412 nsEventStatus status
;
2413 view
->GetViewManager()->DispatchEvent(&event
, &status
);
2416 mSynthMouseMoveEvent
.Forget();
2420 nsViewManager::InvalidateHierarchy()
2424 NS_RELEASE(mRootViewManager
);
2426 nsView
*parent
= mRootView
->GetParent();
2428 mRootViewManager
= parent
->GetViewManager()->RootViewManager();
2429 NS_ADDREF(mRootViewManager
);
2430 NS_ASSERTION(mRootViewManager
!= this,
2431 "Root view had a parent, but it has the same view manager");
2433 mRootViewManager
= this;