Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / view / src / nsViewManager.cpp
blob16f6b6ccec98bf478ac919dcbd3e86ef53cb4aeb
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
13 * License.
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.
22 * Contributor(s):
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)
43 #include "plarena.h"
45 #include "nsAutoPtr.h"
46 #include "nsViewManager.h"
47 #include "nsIRenderingContext.h"
48 #include "nsIDeviceContext.h"
49 #include "nsGfxCIID.h"
50 #include "nsIScrollableView.h"
51 #include "nsView.h"
52 #include "nsISupportsArray.h"
53 #include "nsCOMPtr.h"
54 #include "nsIServiceManager.h"
55 #include "nsGUIEvent.h"
56 #include "nsIPrefBranch.h"
57 #include "nsIPrefService.h"
58 #include "nsRegion.h"
59 #include "nsInt64.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);
73 /**
74 XXX TODO XXX
76 DeCOMify newly private methods
77 Optimize view storage
80 /**
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"
95 #endif
97 #ifdef DEBUG_smaug
98 #define DEBUG_FOCUS_SUPPRESSION
99 #endif
101 //-------------- Begin Invalidate Event Definition ------------------------
103 class nsInvalidateEvent : public nsViewManagerEvent {
104 public:
105 nsInvalidateEvent(nsViewManager *vm) : nsViewManagerEvent(vm) {}
107 NS_IMETHOD Run() {
108 if (mViewManager)
109 mViewManager->ProcessInvalidateEvent();
110 return NS_OK;
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)
124 return PR_FALSE;
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();
134 void
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");
143 } else {
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()
191 if (mRootView) {
192 // Destroy any remaining views
193 mRootView->Destroy();
194 mRootView = nsnull;
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();
202 if (!IsRootVM()) {
203 // We have a strong ref to mRootViewManager
204 NS_RELEASE(mRootViewManager);
207 mRootScrollable = nsnull;
209 NS_ASSERTION((mVMCount > 0), "underflow of viewmanagers");
210 --mVMCount;
212 #ifdef DEBUG
213 PRBool removed =
214 #endif
215 gViewManagers->RemoveElement(this);
216 NS_ASSERTION(removed, "Viewmanager instance not was not in the global list of viewmanagers");
218 if (0 == mVMCount) {
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);
235 mObserver = nsnull;
236 mContext = nsnull;
239 NS_IMPL_ISUPPORTS1(nsViewManager, nsIViewManager)
241 nsresult
242 nsViewManager::CreateRegion(nsIRegion* *result)
244 nsresult rv;
246 if (!mRegionFactory) {
247 mRegionFactory = do_GetClassObject(kRegionCID, &rv);
248 if (NS_FAILED(rv)) {
249 *result = nsnull;
250 return rv;
254 nsIRegion* region = nsnull;
255 rv = CallCreateInstance(mRegionFactory.get(), &region);
256 if (NS_SUCCEEDED(rv)) {
257 rv = region->Init();
258 *result = region;
260 return 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;
275 mContext = aContext;
277 mRefreshEnabled = PR_TRUE;
279 mMouseGrabber = nsnull;
281 return NS_OK;
284 NS_IMETHODIMP_(nsIView *)
285 nsViewManager::CreateView(const nsRect& aBounds,
286 const nsIView* aParent,
287 nsViewVisibility aVisibilityFlag)
289 nsView *v = new nsView(this, aVisibilityFlag);
290 if (v) {
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)));
296 return v;
299 NS_IMETHODIMP_(nsIScrollableView *)
300 nsViewManager::CreateScrollableView(const nsRect& aBounds,
301 const nsIView* aParent)
303 nsScrollPortView *v = new nsScrollPortView(this);
304 if (v) {
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)));
310 return v;
313 NS_IMETHODIMP nsViewManager::GetRootView(nsIView *&aView)
315 aView = mRootView;
316 return NS_OK;
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
327 // to destroy it
328 mRootView = view;
330 if (mRootView) {
331 nsView* parent = mRootView->GetParent();
332 if (parent) {
333 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
334 // no need to set mRootViewManager ourselves here.
335 parent->InsertChild(mRootView, nsnull);
336 } else {
337 InvalidateHierarchy();
340 mRootView->SetZIndex(PR_FALSE, 0, PR_FALSE);
342 // Else don't touch mRootViewManager
344 return NS_OK;
347 NS_IMETHODIMP nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
349 if (nsnull != mRootView) {
350 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
351 nsRect dim;
352 mRootView->GetDimensions(dim);
353 *aWidth = dim.width;
354 *aHeight = dim.height;
355 } else {
356 *aWidth = mDelayedResize.width;
357 *aHeight = mDelayedResize.height;
360 else
362 *aWidth = 0;
363 *aHeight = 0;
365 return NS_OK;
368 NS_IMETHODIMP nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
370 if (mRootView) {
371 if (IsViewVisible(mRootView)) {
372 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
373 DoSetWindowDimensions(aWidth, aHeight);
374 } else {
375 mDelayedResize.SizeTo(aWidth, aHeight);
379 return NS_OK;
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);
388 return NS_OK;
391 static void ConvertNativeRegionToAppRegion(nsIRegion* aIn, nsRegion* aOut,
392 nsIDeviceContext* context)
394 nsRegionRectSet* rects = nsnull;
395 aIn->GetRects(&rects);
396 if (!rects)
397 return;
399 PRInt32 p2a = context->AppUnitsPerDevPixel();
401 PRUint32 i;
402 for (i = 0; i < rects->mNumRects; i++) {
403 const nsRegionRect& inR = rects->mRects[i];
404 nsRect outR;
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;
418 for (;;) {
419 nsView *displayParent = displayRoot->GetParent();
420 if (!displayParent)
421 return displayRoot;
423 if (displayRoot->GetFloating() && !displayParent->GetFloating())
424 return displayRoot;
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())
438 return;
440 nsRect viewRect;
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()) {
452 #ifdef DEBUG_roc
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);
457 #endif
458 return;
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);
467 #endif
469 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
470 if (IsPainting()) {
471 RootViewManager()->mRecursiveRefreshPending = PR_TRUE;
472 return;
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);
489 return;
491 } else {
492 // plain assignment grabs another reference.
493 localcx = aContext;
496 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
498 nsRefPtr<gfxContext> ctx = localcx->ThebesContext();
500 ctx->Save();
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);
514 ctx->Restore();
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);
531 #endif
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);
540 if (! widget)
541 return;
543 nsCOMPtr<nsIRenderingContext> context = aContext;
544 if (! aContext)
545 context = CreateRenderingContext(*aView);
547 if (! context)
548 return;
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!");
554 return;
557 context->SetColor(bgcolor);
558 context->FillRect(*aRect);
561 void nsViewManager::AddCoveringWidgetsToOpaqueRegion(nsRegion &aRgn, nsIDeviceContext* aContext,
562 nsView* aRootView) {
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
577 aRgn.SetEmpty();
579 nsIWidget* widget = aRootView->GetNearestWidget(nsnull);
580 if (!widget) {
581 return;
584 for (nsIWidget* childWidget = widget->GetFirstChild();
585 childWidget;
586 childWidget = childWidget->GetNextSibling()) {
587 PRBool widgetVisible;
588 childWidget->IsVisible(widgetVisible);
589 if (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
605 if (viewParent) {
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)
618 if (mObserver) {
619 nsView* displayRoot = GetDisplayRootFor(aView);
620 nsPoint offsetToRoot = aView->GetOffsetTo(displayRoot);
621 nsRegion damageRegion(aRegion);
622 damageRegion.MoveBy(offsetToRoot);
624 aRC.PushState();
625 aRC.Translate(-offsetToRoot.x, -offsetToRoot.y);
626 mObserver->Paint(displayRoot, &aRC, damageRegion);
627 aRC.PopState();
631 void nsViewManager::ProcessPendingUpdates(nsView* aView, PRBool aDoInvalidate)
633 NS_ASSERTION(IsRootVM(), "Updates will be missed");
635 // Protect against a null-view.
636 if (!aView) {
637 return;
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();
655 if (dirtyRegion) {
656 UpdateWidgetArea(aView, *dirtyRegion, nsnull);
657 dirtyRegion->SetEmpty();
662 NS_IMETHODIMP nsViewManager::Composite()
664 if (!IsRootVM()) {
665 return RootViewManager()->Composite();
668 if (UpdateCount() > 0)
670 ForceUpdate();
671 ClearUpdateCount();
674 return NS_OK;
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.
690 static void
691 AccumulateIntersectionsIntoDirtyRegion(nsView* aTargetView,
692 nsView* aSourceView,
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();
703 if (targetRegion) {
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
714 return;
717 for (nsView* kid = aSourceView->GetFirstChild();
718 kid;
719 kid = kid->GetNextSibling()) {
720 AccumulateIntersectionsIntoDirtyRegion(aTargetView, kid, aOffset);
724 nsresult
725 nsViewManager::WillBitBlit(nsView* aView, nsPoint aScrollAmount)
727 if (!IsRootVM()) {
728 RootViewManager()->WillBitBlit(aView, aScrollAmount);
729 return NS_OK;
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");
736 ++mScrollCnt;
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);
741 return NS_OK;
744 // Invalidate all widgets which overlap the view, other than the view's own widgets.
745 void
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;
756 return;
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();
773 Composite();
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.
783 void
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
789 // process it later.
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();
799 return;
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()) {
809 return;
812 // If the widget is hidden, it don't cover nothing
813 if (nsViewVisibility_kHide == aWidgetView->GetVisibility()) {
814 #ifdef DEBUG
815 // Assert if view is hidden but widget is visible
816 nsIWidget* widget = aWidgetView->GetNearestWidget(nsnull);
817 if (widget) {
818 PRBool visible;
819 widget->IsVisible(visible);
820 NS_ASSERTION(!visible, "View is hidden but widget is visible!");
822 #endif
823 return;
826 if (aWidgetView == aIgnoreWidgetView) {
827 // the widget for aIgnoreWidgetView (and its children) should be treated as already updated.
828 return;
831 nsIWidget* widget = aWidgetView->GetNearestWidget(nsnull);
832 if (!widget) {
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.
836 return;
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.
842 nsRegion children;
843 for (nsIWidget* childWidget = widget->GetFirstChild();
844 childWidget;
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
850 // manager trees
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);
863 nsRegion leftOver;
864 leftOver.Sub(intersection, children);
866 if (!leftOver.IsEmpty()) {
867 NS_ASSERTION(IsRefreshEnabled(), "Can only get here with refresh enabled, I hope");
869 const nsRect* r;
870 for (nsRegionRectIterator iter(leftOver); (r = iter.Next());) {
871 nsRect bounds = *r;
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
889 // can be expensive.
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) {
894 return NS_OK;
897 nsView* displayRoot = GetDisplayRootFor(view);
898 // Propagate the update to the displayRoot, since iframes, for example,
899 // can overlap each other and be translucent. So we have to possibly
900 // invalidate our rect in each of the widgets we have lying about.
901 damagedRect.MoveBy(view->GetOffsetTo(displayRoot));
902 UpdateWidgetArea(displayRoot, nsRegion(damagedRect), nsnull);
904 RootViewManager()->IncrementUpdateCount();
906 if (!IsRefreshEnabled()) {
907 return NS_OK;
910 // See if we should do an immediate refresh or wait
911 if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
912 Composite();
915 return NS_OK;
918 NS_IMETHODIMP nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags)
920 if (RootViewManager() != this) {
921 return RootViewManager()->UpdateAllViews(aUpdateFlags);
924 UpdateViews(mRootView, aUpdateFlags);
925 return NS_OK;
928 void nsViewManager::UpdateViews(nsView *aView, PRUint32 aUpdateFlags)
930 // update this view.
931 UpdateView(aView, aUpdateFlags);
933 // update all children as well.
934 nsView* childView = aView->GetFirstChild();
935 while (nsnull != childView) {
936 UpdateViews(childView, aUpdateFlags);
937 childView = childView->GetNextSibling();
941 nsView *nsViewManager::sCurrentlyFocusView = nsnull;
942 nsView *nsViewManager::sViewFocusedBeforeSuppression = nsnull;
943 PRBool nsViewManager::sFocusSuppressed = PR_FALSE;
945 void nsViewManager::SuppressFocusEvents(PRBool aSuppress)
947 if (aSuppress) {
948 sFocusSuppressed = PR_TRUE;
949 SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView());
950 return;
953 sFocusSuppressed = PR_FALSE;
954 if (GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression()) {
955 return;
958 // We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS.
959 nsIWidget *widget = nsnull;
960 nsEventStatus status;
962 // Backup what is focused before we send the blur. If the
963 // blur causes a focus change, keep that new focus change,
964 // don't overwrite with the old "currently focused view".
965 nsIView *currentFocusBeforeBlur = GetCurrentlyFocusedView();
967 // Send NS_LOSTFOCUS to widget that was focused before
968 // focus/blur suppression.
969 if (GetViewFocusedBeforeSuppression()) {
970 widget = GetViewFocusedBeforeSuppression()->GetWidget();
971 if (widget) {
972 #ifdef DEBUG_FOCUS_SUPPRESSION
973 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_LOSTFOCUS\n");
974 #endif
975 nsGUIEvent event(PR_TRUE, NS_LOSTFOCUS, widget);
976 widget->DispatchEvent(&event, status);
980 // Send NS_GOTFOCUS to the widget that we think should be focused.
981 if (GetCurrentlyFocusedView() &&
982 currentFocusBeforeBlur == GetCurrentlyFocusedView())
984 widget = GetCurrentlyFocusedView()->GetWidget();
985 if (widget) {
986 #ifdef DEBUG_FOCUS_SUPPRESSION
987 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_GOTFOCUS\n");
988 #endif
989 nsGUIEvent event(PR_TRUE, NS_GOTFOCUS, widget);
990 widget->DispatchEvent(&event, status);
996 static void ConvertRectAppUnitsToIntPixels(nsRect& aRect, PRInt32 p2a)
998 aRect.x = NSAppUnitsToIntPixels(aRect.x, p2a);
999 aRect.y = NSAppUnitsToIntPixels(aRect.y, p2a);
1000 aRect.width = NSAppUnitsToIntPixels(aRect.width, p2a);
1001 aRect.height = NSAppUnitsToIntPixels(aRect.height, p2a);
1004 NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus)
1006 *aStatus = nsEventStatus_eIgnore;
1008 switch(aEvent->message)
1010 case NS_SIZE:
1012 nsView* view = nsView::GetViewFor(aEvent->widget);
1014 if (nsnull != view)
1016 nscoord width = ((nsSizeEvent*)aEvent)->windowSize->width;
1017 nscoord height = ((nsSizeEvent*)aEvent)->windowSize->height;
1018 width = ((nsSizeEvent*)aEvent)->mWinWidth;
1019 height = ((nsSizeEvent*)aEvent)->mWinHeight;
1021 // The root view may not be set if this is the resize associated with
1022 // window creation
1024 if (view == mRootView)
1026 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
1027 SetWindowDimensions(NSIntPixelsToAppUnits(width, p2a),
1028 NSIntPixelsToAppUnits(height, p2a));
1029 *aStatus = nsEventStatus_eConsumeNoDefault;
1033 break;
1036 case NS_PAINT:
1038 nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
1039 nsView *view = nsView::GetViewFor(aEvent->widget);
1041 if (!view || !mContext)
1042 break;
1044 *aStatus = nsEventStatus_eConsumeNoDefault;
1046 // The rect is in device units, and it's in the coordinate space of its
1047 // associated window.
1048 nsCOMPtr<nsIRegion> region = event->region;
1049 if (!region) {
1050 if (NS_FAILED(CreateRegion(getter_AddRefs(region))))
1051 break;
1053 const nsRect& damrect = *event->rect;
1054 region->SetTo(damrect.x, damrect.y, damrect.width, damrect.height);
1057 if (region->IsEmpty())
1058 break;
1060 // Refresh the view
1061 if (IsRefreshEnabled()) {
1062 // If an ancestor widget was hidden and then shown, we could
1063 // have a delayed resize to handle.
1064 PRBool didResize = PR_FALSE;
1065 for (nsViewManager *vm = this; vm;
1066 vm = vm->mRootView->GetParent()
1067 ? vm->mRootView->GetParent()->GetViewManager()
1068 : nsnull) {
1069 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
1070 IsViewVisible(vm->mRootView)) {
1071 vm->FlushDelayedResize();
1073 // Paint later.
1074 vm->UpdateView(vm->mRootView, NS_VMREFRESH_NO_SYNC);
1075 didResize = PR_TRUE;
1077 // not sure if it's valid for us to claim that we
1078 // ignored this, but we're going to do so anyway, since
1079 // we didn't actually paint anything
1080 *aStatus = nsEventStatus_eIgnore;
1084 if (!didResize) {
1085 //NS_ASSERTION(IsViewVisible(view), "painting an invisible view");
1087 // Just notify our own view observer that we're about to paint
1088 // XXXbz do we need to notify other view observers for viewmanagers
1089 // in our tree?
1090 // Make sure to not send WillPaint notifications while scrolling
1091 nsRefPtr<nsViewManager> rootVM = RootViewManager();
1093 nsIWidget *widget = mRootView->GetWidget();
1094 PRBool transparentWindow = PR_FALSE;
1095 if (widget)
1096 transparentWindow = widget->GetTransparencyMode() == eTransparencyTransparent;
1098 if (rootVM->mScrollCnt == 0 && !transparentWindow) {
1099 nsIViewObserver* observer = GetViewObserver();
1100 if (observer) {
1101 // Do an update view batch. Make sure not to do it DEFERRED,
1102 // since that would effectively delay any invalidates that are
1103 // triggered by the WillPaint notification (they'd happen when
1104 // the invalid event fires, which is later than the reflow
1105 // event would fire and could end up being after some timer
1106 // events, leading to frame dropping in DHTML). Note that the
1107 // observer may try to reenter this code from inside
1108 // WillPaint() by trying to do a synchronous paint, but since
1109 // refresh will be disabled it won't be able to do the paint.
1110 // We should really sort out the rules on our synch painting
1111 // api....
1112 UpdateViewBatch batch(this);
1113 observer->WillPaint();
1114 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
1116 // Get the view pointer again since the code above might have
1117 // destroyed it (bug 378273).
1118 view = nsView::GetViewFor(aEvent->widget);
1121 // Make sure to sync up any widget geometry changes we
1122 // have pending before we paint.
1123 if (rootVM->mHasPendingUpdates) {
1124 rootVM->ProcessPendingUpdates(mRootView, PR_FALSE);
1127 if (view) {
1128 Refresh(view, event->renderingContext, region,
1129 NS_VMREFRESH_DOUBLE_BUFFER);
1132 } else {
1133 // since we got an NS_PAINT event, we need to
1134 // draw something so we don't get blank areas.
1135 nsRect damRect;
1136 region->GetBoundingBox(&damRect.x, &damRect.y, &damRect.width, &damRect.height);
1137 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
1138 damRect.ScaleRoundOut(float(p2a));
1139 DefaultRefresh(view, event->renderingContext, &damRect);
1141 // Clients like the editor can trigger multiple
1142 // reflows during what the user perceives as a single
1143 // edit operation, so it disables view manager
1144 // refreshing until the edit operation is complete
1145 // so that users don't see the intermediate steps.
1147 // Unfortunately some of these reflows can trigger
1148 // nsScrollPortView and nsScrollingView Scroll() calls
1149 // which in most cases force an immediate BitBlt and
1150 // synchronous paint to happen even if the view manager's
1151 // refresh is disabled. (Bug 97674)
1153 // Calling UpdateView() here, is necessary to add
1154 // the exposed region specified in the synchronous paint
1155 // event to the view's damaged region so that it gets
1156 // painted properly when refresh is enabled.
1158 // Note that calling UpdateView() here was deemed
1159 // to have the least impact on performance, since the
1160 // other alternative was to make Scroll() post an
1161 // async paint event for the *entire* ScrollPort or
1162 // ScrollingView's viewable area. (See bug 97674 for this
1163 // alternate patch.)
1165 UpdateView(view, damRect, NS_VMREFRESH_NO_SYNC);
1168 break;
1171 case NS_CREATE:
1172 case NS_DESTROY:
1173 case NS_SETZLEVEL:
1174 case NS_MOVE:
1175 /* Don't pass these events through. Passing them through
1176 causes performance problems on pages with lots of views/frames
1177 @see bug 112861 */
1178 *aStatus = nsEventStatus_eConsumeNoDefault;
1179 break;
1182 case NS_DISPLAYCHANGED:
1184 //Destroy the cached backbuffer to force a new backbuffer
1185 //be constructed with the appropriate display depth.
1186 //@see bugzilla bug 6061
1187 *aStatus = nsEventStatus_eConsumeDoDefault;
1188 break;
1190 case NS_SYSCOLORCHANGED:
1192 // Hold a refcount to the observer. The continued existence of the observer will
1193 // delay deletion of this view hierarchy should the event want to cause its
1194 // destruction in, say, some JavaScript event handler.
1195 nsView *view = nsView::GetViewFor(aEvent->widget);
1196 nsCOMPtr<nsIViewObserver> obs = GetViewObserver();
1197 if (obs) {
1198 obs->HandleEvent(view, aEvent, aStatus);
1201 break;
1203 default:
1205 if (aEvent->message == NS_GOTFOCUS) {
1206 #ifdef DEBUG_FOCUS_SUPPRESSION
1207 printf("*** 0 INFO TODO [CPEARCE] Focus changing%s\n",
1208 (nsViewManager::IsFocusSuppressed() ? " while suppressed" : ""));
1209 #endif
1210 SetCurrentlyFocusedView(nsView::GetViewFor(aEvent->widget));
1212 if ((aEvent->message == NS_GOTFOCUS || aEvent->message == NS_LOSTFOCUS) &&
1213 nsViewManager::IsFocusSuppressed())
1215 #ifdef DEBUG_FOCUS_SUPPRESSION
1216 printf("*** 0 INFO TODO [CPEARCE] Suppressing %s\n",
1217 (aEvent->message == NS_GOTFOCUS ? "NS_GOTFOCUS" : "NS_LOSTFOCUS"));
1218 #endif
1219 break;
1222 if ((NS_IS_MOUSE_EVENT(aEvent) &&
1223 // Ignore moves that we synthesize.
1224 static_cast<nsMouseEvent*>(aEvent)->reason ==
1225 nsMouseEvent::eReal &&
1226 // Ignore mouse exit and enter (we'll get moves if the user
1227 // is really moving the mouse) since we get them when we
1228 // create and destroy widgets.
1229 aEvent->message != NS_MOUSE_EXIT &&
1230 aEvent->message != NS_MOUSE_ENTER) ||
1231 NS_IS_KEY_EVENT(aEvent) ||
1232 NS_IS_IME_EVENT(aEvent)) {
1233 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1236 if (aEvent->message == NS_DEACTIVATE) {
1237 PRBool result;
1238 GrabMouseEvents(nsnull, result);
1241 //Find the view whose coordinates system we're in.
1242 nsView* baseView = nsView::GetViewFor(aEvent->widget);
1243 nsView* view = baseView;
1244 PRBool capturedEvent = PR_FALSE;
1246 if (!NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_EVENT(aEvent) &&
1247 !NS_IS_CONTEXT_MENU_KEY(aEvent) && !NS_IS_FOCUS_EVENT(aEvent) &&
1248 !NS_IS_QUERY_CONTENT_EVENT(aEvent) &&
1249 aEvent->eventStructType != NS_ACCESSIBLE_EVENT) {
1250 // will dispatch using coordinates. Pretty bogus but it's consistent
1251 // with what presshell does.
1252 view = GetDisplayRootFor(baseView);
1255 //Find the view to which we're initially going to send the event
1256 //for hittesting.
1257 if (NS_IS_MOUSE_EVENT(aEvent) || NS_IS_DRAG_EVENT(aEvent)) {
1258 nsView* mouseGrabber = GetMouseEventGrabber();
1259 if (mouseGrabber) {
1260 view = mouseGrabber;
1261 capturedEvent = PR_TRUE;
1265 if (nsnull != view) {
1266 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
1268 if ((aEvent->message == NS_MOUSE_MOVE &&
1269 static_cast<nsMouseEvent*>(aEvent)->reason ==
1270 nsMouseEvent::eReal) ||
1271 aEvent->message == NS_MOUSE_ENTER ||
1272 aEvent->message == NS_MOUSE_BUTTON_DOWN ||
1273 aEvent->message == NS_MOUSE_BUTTON_UP) {
1274 // aEvent->point is relative to the widget, i.e. the view top-left,
1275 // so we need to add the offset to the view origin
1276 nsPoint rootOffset = baseView->GetDimensions().TopLeft();
1277 rootOffset += baseView->GetOffsetTo(RootViewManager()->mRootView);
1278 RootViewManager()->mMouseLocation = aEvent->refPoint +
1279 nsPoint(NSAppUnitsToIntPixels(rootOffset.x, p2a),
1280 NSAppUnitsToIntPixels(rootOffset.y, p2a));
1281 #ifdef DEBUG_MOUSE_LOCATION
1282 if (aEvent->message == NS_MOUSE_ENTER)
1283 printf("[vm=%p]got mouse enter for %p\n",
1284 this, aEvent->widget);
1285 printf("[vm=%p]setting mouse location to (%d,%d)\n",
1286 this, mMouseLocation.x, mMouseLocation.y);
1287 #endif
1288 if (aEvent->message == NS_MOUSE_ENTER)
1289 SynthesizeMouseMove(PR_FALSE);
1290 } else if (aEvent->message == NS_MOUSE_EXIT) {
1291 // Although we only care about the mouse moving into an area
1292 // for which this view manager doesn't receive mouse move
1293 // events, we don't check which view the mouse exit was for
1294 // since this seems to vary by platform. Hopefully this
1295 // won't matter at all since we'll get the mouse move or
1296 // enter after the mouse exit when the mouse moves from one
1297 // of our widgets into another.
1298 RootViewManager()->mMouseLocation = nsPoint(NSCOORD_NONE, NSCOORD_NONE);
1299 #ifdef DEBUG_MOUSE_LOCATION
1300 printf("[vm=%p]got mouse exit for %p\n",
1301 this, aEvent->widget);
1302 printf("[vm=%p]clearing mouse location\n",
1303 this);
1304 #endif
1307 //Calculate the proper offset for the view we're going to
1308 nsPoint offset(0, 0);
1310 if (view != baseView) {
1311 //Get offset from root of baseView
1312 nsView *parent;
1313 for (parent = baseView; parent; parent = parent->GetParent())
1314 parent->ConvertToParentCoords(&offset.x, &offset.y);
1316 //Subtract back offset from root of view
1317 for (parent = view; parent; parent = parent->GetParent())
1318 parent->ConvertFromParentCoords(&offset.x, &offset.y);
1321 // Dispatch the event
1322 nsRect baseViewDimensions;
1323 if (baseView != nsnull) {
1324 baseView->GetDimensions(baseViewDimensions);
1327 nsPoint pt;
1328 pt.x = baseViewDimensions.x +
1329 NSFloatPixelsToAppUnits(float(aEvent->refPoint.x) + 0.5f, p2a);
1330 pt.y = baseViewDimensions.y +
1331 NSFloatPixelsToAppUnits(float(aEvent->refPoint.y) + 0.5f, p2a);
1332 pt += offset;
1334 *aStatus = HandleEvent(view, pt, aEvent, capturedEvent);
1337 // need to map the reply back into platform coordinates
1339 switch (aEvent->message) {
1340 case NS_TEXT_TEXT:
1341 ConvertRectAppUnitsToIntPixels(
1342 ((nsTextEvent*)aEvent)->theReply.mCursorPosition, p2a);
1343 break;
1344 case NS_COMPOSITION_START:
1345 case NS_COMPOSITION_QUERY:
1346 ConvertRectAppUnitsToIntPixels(
1347 ((nsCompositionEvent*)aEvent)->theReply.mCursorPosition, p2a);
1348 break;
1349 case NS_QUERY_CHARACTER_RECT:
1350 case NS_QUERY_CARET_RECT:
1351 ConvertRectAppUnitsToIntPixels(
1352 ((nsQueryContentEvent*)aEvent)->mReply.mRect, p2a);
1353 break;
1357 break;
1361 return NS_OK;
1364 nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsPoint aPoint,
1365 nsGUIEvent* aEvent, PRBool aCaptured) {
1366 //printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports,
1367 // event->message, event->point.x, event->point.y);
1369 // Hold a refcount to the observer. The continued existence of the observer will
1370 // delay deletion of this view hierarchy should the event want to cause its
1371 // destruction in, say, some JavaScript event handler.
1372 nsCOMPtr<nsIViewObserver> obs = aView->GetViewManager()->GetViewObserver();
1373 nsEventStatus status = nsEventStatus_eIgnore;
1374 if (obs) {
1375 obs->HandleEvent(aView, aEvent, &status);
1378 return status;
1381 NS_IMETHODIMP nsViewManager::GrabMouseEvents(nsIView *aView, PRBool &aResult)
1383 if (!IsRootVM()) {
1384 return RootViewManager()->GrabMouseEvents(aView, aResult);
1387 // Along with nsView::SetVisibility, we enforce that the mouse grabber
1388 // can never be a hidden view.
1389 if (aView && static_cast<nsView*>(aView)->GetVisibility()
1390 == nsViewVisibility_kHide) {
1391 aView = nsnull;
1394 #ifdef DEBUG_mjudge
1395 if (aView)
1397 printf("capturing mouse events for view %x\n",aView);
1399 printf("removing mouse capture from view %x\n",mMouseGrabber);
1400 #endif
1402 mMouseGrabber = static_cast<nsView*>(aView);
1403 aResult = PR_TRUE;
1404 return NS_OK;
1407 NS_IMETHODIMP nsViewManager::GetMouseEventGrabber(nsIView *&aView)
1409 aView = GetMouseEventGrabber();
1410 return NS_OK;
1413 // Recursively reparent widgets if necessary
1415 void nsViewManager::ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget)
1417 if (aView->HasWidget()) {
1418 // Check to see if the parent widget is the
1419 // same as the new parent. If not then reparent
1420 // the widget, otherwise there is nothing more
1421 // to do for the view and its descendants
1422 nsIWidget* widget = aView->GetWidget();
1423 nsIWidget* parentWidget = widget->GetParent();
1424 // Toplevel widgets should not be reparented!
1425 if (parentWidget && parentWidget != aNewWidget) {
1426 #ifdef DEBUG
1427 nsresult rv =
1428 #endif
1429 widget->SetParent(aNewWidget);
1430 NS_ASSERTION(NS_SUCCEEDED(rv), "SetParent failed!");
1432 return;
1435 // Need to check each of the views children to see
1436 // if they have a widget and reparent it.
1438 nsView* view = static_cast<nsView*>(aView);
1439 for (nsView *kid = view->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
1440 ReparentChildWidgets(kid, aNewWidget);
1444 // Reparent a view and its descendant views widgets if necessary
1446 void nsViewManager::ReparentWidgets(nsIView* aView, nsIView *aParent)
1448 NS_PRECONDITION(aParent, "Must have a parent");
1449 NS_PRECONDITION(aView, "Must have a view");
1451 // Quickly determine whether the view has pre-existing children or a
1452 // widget. In most cases the view will not have any pre-existing
1453 // children when this is called. Only in the case
1454 // where a view has been reparented by removing it from
1455 // a reinserting it into a new location in the view hierarchy do we
1456 // have to consider reparenting the existing widgets for the view and
1457 // it's descendants.
1458 nsView* view = static_cast<nsView*>(aView);
1459 if (view->HasWidget() || view->GetFirstChild()) {
1460 nsIWidget* parentWidget = aParent->GetNearestWidget(nsnull);
1461 if (parentWidget) {
1462 ReparentChildWidgets(aView, parentWidget);
1463 return;
1465 NS_WARNING("Can not find a widget for the parent view");
1469 NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, nsIView *aSibling,
1470 PRBool aAfter)
1472 nsView* parent = static_cast<nsView*>(aParent);
1473 nsView* child = static_cast<nsView*>(aChild);
1474 nsView* sibling = static_cast<nsView*>(aSibling);
1476 NS_PRECONDITION(nsnull != parent, "null ptr");
1477 NS_PRECONDITION(nsnull != child, "null ptr");
1478 NS_ASSERTION(sibling == nsnull || sibling->GetParent() == parent,
1479 "tried to insert view with invalid sibling");
1480 NS_ASSERTION(!IsViewInserted(child), "tried to insert an already-inserted view");
1482 if ((nsnull != parent) && (nsnull != child))
1484 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
1485 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
1487 #if 1
1488 if (nsnull == aSibling) {
1489 if (aAfter) {
1490 // insert at end of document order, i.e., before first view
1491 // this is the common case, by far
1492 parent->InsertChild(child, nsnull);
1493 ReparentWidgets(child, parent);
1494 } else {
1495 // insert at beginning of document order, i.e., after last view
1496 nsView *kid = parent->GetFirstChild();
1497 nsView *prev = nsnull;
1498 while (kid) {
1499 prev = kid;
1500 kid = kid->GetNextSibling();
1502 // prev is last view or null if there are no children
1503 parent->InsertChild(child, prev);
1504 ReparentWidgets(child, parent);
1506 } else {
1507 nsView *kid = parent->GetFirstChild();
1508 nsView *prev = nsnull;
1509 while (kid && sibling != kid) {
1510 //get the next sibling view
1511 prev = kid;
1512 kid = kid->GetNextSibling();
1514 NS_ASSERTION(kid != nsnull,
1515 "couldn't find sibling in child list");
1516 if (aAfter) {
1517 // insert after 'kid' in document order, i.e. before in view order
1518 parent->InsertChild(child, prev);
1519 ReparentWidgets(child, parent);
1520 } else {
1521 // insert before 'kid' in document order, i.e. after in view order
1522 parent->InsertChild(child, kid);
1523 ReparentWidgets(child, parent);
1526 #else // don't keep consistent document order, but order things by z-index instead
1527 // essentially we're emulating the old InsertChild(parent, child, zindex)
1528 PRInt32 zIndex = child->GetZIndex();
1529 while (nsnull != kid)
1531 PRInt32 idx = kid->GetZIndex();
1533 if (CompareZIndex(zIndex, child->IsTopMost(), child->GetZIndexIsAuto(),
1534 idx, kid->IsTopMost(), kid->GetZIndexIsAuto()) >= 0)
1535 break;
1537 prev = kid;
1538 kid = kid->GetNextSibling();
1541 parent->InsertChild(child, prev);
1542 ReparentWidgets(child, parent);
1543 #endif
1545 // if the parent view is marked as "floating", make the newly added view float as well.
1546 if (parent->GetFloating())
1547 child->SetFloating(PR_TRUE);
1549 //and mark this area as dirty if the view is visible...
1551 if (nsViewVisibility_kHide != child->GetVisibility())
1552 UpdateView(child, NS_VMREFRESH_NO_SYNC);
1554 return NS_OK;
1557 NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, PRInt32 aZIndex)
1559 // no-one really calls this with anything other than aZIndex == 0 on a fresh view
1560 // XXX this method should simply be eliminated and its callers redirected to the real method
1561 SetViewZIndex(aChild, PR_FALSE, aZIndex, PR_FALSE);
1562 return InsertChild(aParent, aChild, nsnull, PR_TRUE);
1565 NS_IMETHODIMP nsViewManager::RemoveChild(nsIView *aChild)
1567 nsView* child = static_cast<nsView*>(aChild);
1568 NS_ENSURE_ARG_POINTER(child);
1570 nsView* parent = child->GetParent();
1572 if (nsnull != parent)
1574 UpdateView(child, NS_VMREFRESH_NO_SYNC);
1575 parent->RemoveChild(child);
1578 return NS_OK;
1581 NS_IMETHODIMP nsViewManager::MoveViewBy(nsIView *aView, nscoord aX, nscoord aY)
1583 nsView* view = static_cast<nsView*>(aView);
1585 nsPoint pt = view->GetPosition();
1586 MoveViewTo(view, aX + pt.x, aY + pt.y);
1587 return NS_OK;
1590 NS_IMETHODIMP nsViewManager::MoveViewTo(nsIView *aView, nscoord aX, nscoord aY)
1592 nsView* view = static_cast<nsView*>(aView);
1593 nsPoint oldPt = view->GetPosition();
1594 nsRect oldArea = view->GetBounds();
1595 view->SetPosition(aX, aY);
1597 // only do damage control if the view is visible
1599 if ((aX != oldPt.x) || (aY != oldPt.y)) {
1600 if (view->GetVisibility() != nsViewVisibility_kHide) {
1601 nsView* parentView = view->GetParent();
1602 UpdateView(parentView, oldArea, NS_VMREFRESH_NO_SYNC);
1603 UpdateView(parentView, view->GetBounds(), NS_VMREFRESH_NO_SYNC);
1606 return NS_OK;
1609 void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
1610 PRUint32 aUpdateFlags, nscoord aY1, nscoord aY2, PRBool aInCutOut) {
1611 nscoord height = aY2 - aY1;
1612 if (aRect.x < aCutOut.x) {
1613 nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height);
1614 UpdateView(aView, r, aUpdateFlags);
1616 if (!aInCutOut && aCutOut.x < aCutOut.XMost()) {
1617 nsRect r(aCutOut.x, aY1, aCutOut.width, height);
1618 UpdateView(aView, r, aUpdateFlags);
1620 if (aCutOut.XMost() < aRect.XMost()) {
1621 nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height);
1622 UpdateView(aView, r, aUpdateFlags);
1626 void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
1627 PRUint32 aUpdateFlags) {
1628 if (aRect.y < aCutOut.y) {
1629 InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aRect.y, aCutOut.y, PR_FALSE);
1631 if (aCutOut.y < aCutOut.YMost()) {
1632 InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.y, aCutOut.YMost(), PR_TRUE);
1634 if (aCutOut.YMost() < aRect.YMost()) {
1635 InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.YMost(), aRect.YMost(), PR_FALSE);
1639 NS_IMETHODIMP nsViewManager::ResizeView(nsIView *aView, const nsRect &aRect, PRBool aRepaintExposedAreaOnly)
1641 nsView* view = static_cast<nsView*>(aView);
1642 nsRect oldDimensions;
1644 view->GetDimensions(oldDimensions);
1645 if (!oldDimensions.IsExactEqual(aRect)) {
1646 nsView* parentView = view->GetParent();
1647 if (parentView == nsnull)
1648 parentView = view;
1650 // resize the view.
1651 // Prevent Invalidation of hidden views
1652 if (view->GetVisibility() == nsViewVisibility_kHide) {
1653 view->SetDimensions(aRect, PR_FALSE);
1654 } else {
1655 if (!aRepaintExposedAreaOnly) {
1656 //Invalidate the union of the old and new size
1657 view->SetDimensions(aRect, PR_TRUE);
1659 UpdateView(view, aRect, NS_VMREFRESH_NO_SYNC);
1660 view->ConvertToParentCoords(&oldDimensions.x, &oldDimensions.y);
1661 UpdateView(parentView, oldDimensions, NS_VMREFRESH_NO_SYNC);
1662 } else {
1663 view->SetDimensions(aRect, PR_TRUE);
1665 InvalidateRectDifference(view, aRect, oldDimensions, NS_VMREFRESH_NO_SYNC);
1666 nsRect r = aRect;
1667 view->ConvertToParentCoords(&r.x, &r.y);
1668 view->ConvertToParentCoords(&oldDimensions.x, &oldDimensions.y);
1669 InvalidateRectDifference(parentView, oldDimensions, r, NS_VMREFRESH_NO_SYNC);
1674 // Note that if layout resizes the view and the view has a custom clip
1675 // region set, then we expect layout to update the clip region too. Thus
1676 // in the case where mClipRect has been optimized away to just be a null
1677 // pointer, and this resize is implicitly changing the clip rect, it's OK
1678 // because layout will change it back again if necessary.
1680 return NS_OK;
1683 static double GetArea(const nsRect& aRect)
1685 return double(aRect.width)*double(aRect.height);
1688 PRBool nsViewManager::CanScrollWithBitBlt(nsView* aView, nsPoint aDelta,
1689 nsRegion* aUpdateRegion)
1691 NS_ASSERTION(!IsPainting(),
1692 "View manager shouldn't be scrolling during a paint");
1693 if (IsPainting() || !mObserver) {
1694 return PR_FALSE; // do the safe thing
1697 nsView* displayRoot = GetDisplayRootFor(aView);
1698 nsPoint displayOffset = aView->GetParent()->GetOffsetTo(displayRoot);
1699 nsRect parentBounds = aView->GetParent()->GetDimensions() + displayOffset;
1700 // The rect we're going to scroll is intersection of the parent bounds with its
1701 // preimage
1702 nsRect toScroll;
1703 toScroll.IntersectRect(parentBounds + aDelta, parentBounds);
1704 nsresult rv =
1705 mObserver->ComputeRepaintRegionForCopy(displayRoot, aView, -aDelta, toScroll,
1706 aUpdateRegion);
1707 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1709 aUpdateRegion->MoveBy(-displayOffset);
1711 #if defined(MOZ_WIDGET_GTK2) || defined(XP_OS2)
1712 return aUpdateRegion->IsEmpty();
1713 #else
1714 return GetArea(aUpdateRegion->GetBounds()) < GetArea(parentBounds)/2;
1715 #endif
1718 NS_IMETHODIMP nsViewManager::SetViewFloating(nsIView *aView, PRBool aFloating)
1720 nsView* view = static_cast<nsView*>(aView);
1722 NS_ASSERTION(!(nsnull == view), "no view");
1724 view->SetFloating(aFloating);
1726 return NS_OK;
1729 NS_IMETHODIMP nsViewManager::SetViewVisibility(nsIView *aView, nsViewVisibility aVisible)
1731 nsView* view = static_cast<nsView*>(aView);
1733 if (aVisible != view->GetVisibility()) {
1734 view->SetVisibility(aVisible);
1736 if (IsViewInserted(view)) {
1737 if (!view->HasWidget()) {
1738 if (nsViewVisibility_kHide == aVisible) {
1739 nsView* parentView = view->GetParent();
1740 if (parentView) {
1741 UpdateView(parentView, view->GetBounds(), NS_VMREFRESH_NO_SYNC);
1744 else {
1745 UpdateView(view, NS_VMREFRESH_NO_SYNC);
1750 // Any child views not associated with frames might not get their visibility
1751 // updated, so propagate our visibility to them. This is important because
1752 // hidden views should have all hidden children.
1753 for (nsView* childView = view->GetFirstChild(); childView;
1754 childView = childView->GetNextSibling()) {
1755 if (!childView->GetClientData()) {
1756 childView->SetVisibility(aVisible);
1760 return NS_OK;
1763 void nsViewManager::UpdateWidgetsForView(nsView* aView)
1765 NS_PRECONDITION(aView, "Must have view!");
1767 nsWeakView parentWeakView = aView;
1768 if (aView->HasWidget()) {
1769 aView->GetWidget()->Update(); // Flushes Layout!
1770 if (!parentWeakView.IsAlive()) {
1771 return;
1775 nsView* childView = aView->GetFirstChild();
1776 while (childView) {
1777 nsWeakView childWeakView = childView;
1778 UpdateWidgetsForView(childView);
1779 if (NS_LIKELY(childWeakView.IsAlive())) {
1780 childView = childView->GetNextSibling();
1782 else {
1783 // The current view was destroyed - restart at the first child if the
1784 // parent is still alive.
1785 childView = parentWeakView.IsAlive() ? aView->GetFirstChild() : nsnull;
1790 PRBool nsViewManager::IsViewInserted(nsView *aView)
1792 if (mRootView == aView) {
1793 return PR_TRUE;
1794 } else if (aView->GetParent() == nsnull) {
1795 return PR_FALSE;
1796 } else {
1797 nsView* view = aView->GetParent()->GetFirstChild();
1798 while (view != nsnull) {
1799 if (view == aView) {
1800 return PR_TRUE;
1802 view = view->GetNextSibling();
1804 return PR_FALSE;
1808 NS_IMETHODIMP nsViewManager::SetViewZIndex(nsIView *aView, PRBool aAutoZIndex, PRInt32 aZIndex, PRBool aTopMost)
1810 nsView* view = static_cast<nsView*>(aView);
1811 nsresult rv = NS_OK;
1813 NS_ASSERTION((view != nsnull), "no view");
1815 // don't allow the root view's z-index to be changed. It should always be zero.
1816 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1817 if (aView == mRootView) {
1818 return rv;
1821 PRBool oldTopMost = view->IsTopMost();
1822 PRBool oldIsAuto = view->GetZIndexIsAuto();
1824 if (aAutoZIndex) {
1825 aZIndex = 0;
1828 PRInt32 oldidx = view->GetZIndex();
1829 view->SetZIndex(aAutoZIndex, aZIndex, aTopMost);
1831 if (oldidx != aZIndex || oldTopMost != aTopMost ||
1832 oldIsAuto != aAutoZIndex) {
1833 UpdateView(view, NS_VMREFRESH_NO_SYNC);
1836 return rv;
1839 NS_IMETHODIMP nsViewManager::SetViewObserver(nsIViewObserver *aObserver)
1841 mObserver = aObserver;
1842 return NS_OK;
1845 NS_IMETHODIMP nsViewManager::GetViewObserver(nsIViewObserver *&aObserver)
1847 if (nsnull != mObserver) {
1848 aObserver = mObserver;
1849 NS_ADDREF(mObserver);
1850 return NS_OK;
1851 } else
1852 return NS_ERROR_NO_INTERFACE;
1855 NS_IMETHODIMP nsViewManager::GetDeviceContext(nsIDeviceContext *&aContext)
1857 NS_IF_ADDREF(mContext);
1858 aContext = mContext;
1859 return NS_OK;
1862 already_AddRefed<nsIRenderingContext>
1863 nsViewManager::CreateRenderingContext(nsView &aView)
1865 nsView *par = &aView;
1866 nsIWidget* win;
1867 nsIRenderingContext *cx = nsnull;
1868 nscoord ax = 0, ay = 0;
1872 win = par->GetWidget();
1873 if (win)
1874 break;
1876 //get absolute coordinates of view, but don't
1877 //add in view pos since the first thing you ever
1878 //need to do when painting a view is to translate
1879 //the rendering context by the views pos and other parts
1880 //of the code do this for us...
1882 if (par != &aView)
1884 par->ConvertToParentCoords(&ax, &ay);
1887 par = par->GetParent();
1889 while (nsnull != par);
1891 if (nsnull != win)
1893 // XXXkt this has an origin at top-left of win ...
1894 mContext->CreateRenderingContext(par, cx);
1896 // XXXkt ... but the translation is between the origins of views
1897 NS_ASSERTION(aView.ViewToWidgetOffset()
1898 - aView.GetDimensions().TopLeft() ==
1899 par->ViewToWidgetOffset()
1900 - par->GetDimensions().TopLeft(),
1901 "ViewToWidgetOffset not handled!");
1902 if (nsnull != cx)
1903 cx->Translate(ax, ay);
1906 return cx;
1909 NS_IMETHODIMP nsViewManager::DisableRefresh(void)
1911 if (!IsRootVM()) {
1912 return RootViewManager()->DisableRefresh();
1915 if (mUpdateBatchCnt > 0)
1916 return NS_OK;
1918 mRefreshEnabled = PR_FALSE;
1919 return NS_OK;
1922 NS_IMETHODIMP nsViewManager::EnableRefresh(PRUint32 aUpdateFlags)
1924 if (!IsRootVM()) {
1925 return RootViewManager()->EnableRefresh(aUpdateFlags);
1928 if (mUpdateBatchCnt > 0)
1929 return NS_OK;
1931 mRefreshEnabled = PR_TRUE;
1933 // nested batching can combine IMMEDIATE with DEFERRED. Favour
1934 // IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC. We need to
1935 // check for IMMEDIATE before checking mHasPendingUpdates, because
1936 // the latter might be false as far as gecko is concerned but the OS
1937 // might still have queued up expose events that it hasn't sent yet.
1938 if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
1939 FlushPendingInvalidates();
1940 Composite();
1941 } else if (!mHasPendingUpdates) {
1942 // Nothing to do
1943 } else if (aUpdateFlags & NS_VMREFRESH_DEFERRED) {
1944 PostInvalidateEvent();
1945 } else { // NO_SYNC
1946 FlushPendingInvalidates();
1949 return NS_OK;
1952 nsIViewManager* nsViewManager::BeginUpdateViewBatch(void)
1954 if (!IsRootVM()) {
1955 return RootViewManager()->BeginUpdateViewBatch();
1958 nsresult result = NS_OK;
1960 if (mUpdateBatchCnt == 0) {
1961 mUpdateBatchFlags = 0;
1962 result = DisableRefresh();
1965 if (NS_SUCCEEDED(result))
1966 ++mUpdateBatchCnt;
1968 return this;
1971 NS_IMETHODIMP nsViewManager::EndUpdateViewBatch(PRUint32 aUpdateFlags)
1973 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1975 nsresult result = NS_OK;
1977 --mUpdateBatchCnt;
1979 NS_ASSERTION(mUpdateBatchCnt >= 0, "Invalid batch count!");
1981 if (mUpdateBatchCnt < 0)
1983 mUpdateBatchCnt = 0;
1984 return NS_ERROR_FAILURE;
1987 mUpdateBatchFlags |= aUpdateFlags;
1988 if (mUpdateBatchCnt == 0) {
1989 result = EnableRefresh(mUpdateBatchFlags);
1992 return result;
1995 NS_IMETHODIMP nsViewManager::SetRootScrollableView(nsIScrollableView *aScrollable)
1997 mRootScrollable = aScrollable;
1998 return NS_OK;
2001 NS_IMETHODIMP nsViewManager::GetRootScrollableView(nsIScrollableView **aScrollable)
2003 *aScrollable = mRootScrollable;
2004 return NS_OK;
2007 NS_IMETHODIMP nsViewManager::GetWidget(nsIWidget **aWidget)
2009 *aWidget = GetWidget();
2010 NS_IF_ADDREF(*aWidget);
2011 return NS_OK;
2014 NS_IMETHODIMP nsViewManager::ForceUpdate()
2016 if (!IsRootVM()) {
2017 return RootViewManager()->ForceUpdate();
2020 // Walk the view tree looking for widgets, and call Update() on each one
2021 if (mRootView) {
2022 UpdateWidgetsForView(mRootView);
2025 return NS_OK;
2028 void nsViewManager::ViewToWidget(nsView *aView, nsView* aWidgetView, nsRect &aRect) const
2030 while (aView != aWidgetView) {
2031 aView->ConvertToParentCoords(&aRect.x, &aRect.y);
2032 aView = aView->GetParent();
2035 // intersect aRect with bounds of aWidgetView, to prevent generating any illegal rectangles.
2036 nsRect bounds;
2037 aWidgetView->GetDimensions(bounds);
2038 aRect.IntersectRect(aRect, bounds);
2039 // account for the view's origin not lining up with the widget's
2040 aRect.x -= bounds.x;
2041 aRect.y -= bounds.y;
2043 aRect += aView->ViewToWidgetOffset();
2045 // finally, convert to device coordinates.
2046 aRect.ScaleRoundOut(1.0f / mContext->AppUnitsPerDevPixel());
2049 nsresult nsViewManager::GetVisibleRect(nsRect& aVisibleRect)
2051 nsresult rv = NS_OK;
2053 // Get the viewport scroller
2054 nsIScrollableView* scrollingView;
2055 GetRootScrollableView(&scrollingView);
2057 if (scrollingView) {
2058 // Determine the visible rect in the scrolled view's coordinate space.
2059 // The size of the visible area is the clip view size
2060 nsScrollPortView* clipView = static_cast<nsScrollPortView*>(scrollingView);
2061 clipView->GetDimensions(aVisibleRect);
2063 scrollingView->GetScrollPosition(aVisibleRect.x, aVisibleRect.y);
2064 } else {
2065 rv = NS_ERROR_FAILURE;
2068 return rv;
2071 nsresult nsViewManager::GetAbsoluteRect(nsView *aView, const nsRect &aRect,
2072 nsRect& aAbsRect)
2074 nsIScrollableView* scrollingView = nsnull;
2075 GetRootScrollableView(&scrollingView);
2076 if (nsnull == scrollingView) {
2077 return NS_ERROR_FAILURE;
2080 nsIView* scrolledIView = nsnull;
2081 scrollingView->GetScrolledView(scrolledIView);
2083 nsView* scrolledView = static_cast<nsView*>(scrolledIView);
2085 // Calculate the absolute coordinates of the aRect passed in.
2086 // aRects values are relative to aView
2087 aAbsRect = aRect;
2088 nsView *parentView = aView;
2089 while ((parentView != nsnull) && (parentView != scrolledView)) {
2090 parentView->ConvertToParentCoords(&aAbsRect.x, &aAbsRect.y);
2091 parentView = parentView->GetParent();
2094 if (parentView != scrolledView) {
2095 return NS_ERROR_FAILURE;
2098 return NS_OK;
2102 NS_IMETHODIMP nsViewManager::GetRectVisibility(nsIView *aView,
2103 const nsRect &aRect,
2104 PRUint16 aMinTwips,
2105 nsRectVisibility *aRectVisibility)
2107 nsView* view = static_cast<nsView*>(aView);
2109 // The parameter aMinTwips determines how many rows/cols of pixels must be visible on each side of the element,
2110 // in order to be counted as visible
2112 *aRectVisibility = nsRectVisibility_kZeroAreaRect;
2113 if (aRect.width == 0 || aRect.height == 0) {
2114 return NS_OK;
2117 // is this view even visible?
2118 if (view->GetVisibility() == nsViewVisibility_kHide) {
2119 return NS_OK;
2122 // nsViewManager::InsertChild ensures that descendants of floating views
2123 // are also marked floating.
2124 if (view->GetFloating()) {
2125 *aRectVisibility = nsRectVisibility_kVisible;
2126 return NS_OK;
2129 // Calculate the absolute coordinates for the visible rectangle
2130 nsRect visibleRect;
2131 if (GetVisibleRect(visibleRect) == NS_ERROR_FAILURE) {
2132 *aRectVisibility = nsRectVisibility_kVisible;
2133 return NS_OK;
2136 // Calculate the absolute coordinates of the aRect passed in.
2137 // aRects values are relative to aView
2138 nsRect absRect;
2139 if ((GetAbsoluteRect(view, aRect, absRect)) == NS_ERROR_FAILURE) {
2140 *aRectVisibility = nsRectVisibility_kVisible;
2141 return NS_OK;
2145 * If aMinTwips > 0, ensure at least aMinTwips of space around object is visible
2146 * The object is not visible if:
2147 * ((objectTop < windowTop && objectBottom < windowTop) ||
2148 * (objectBottom > windowBottom && objectTop > windowBottom) ||
2149 * (objectLeft < windowLeft && objectRight < windowLeft) ||
2150 * (objectRight > windowRight && objectLeft > windowRight))
2153 if (absRect.y < visibleRect.y &&
2154 absRect.y + absRect.height < visibleRect.y + aMinTwips)
2155 *aRectVisibility = nsRectVisibility_kAboveViewport;
2156 else if (absRect.y + absRect.height > visibleRect.y + visibleRect.height &&
2157 absRect.y > visibleRect.y + visibleRect.height - aMinTwips)
2158 *aRectVisibility = nsRectVisibility_kBelowViewport;
2159 else if (absRect.x < visibleRect.x &&
2160 absRect.x + absRect.width < visibleRect.x + aMinTwips)
2161 *aRectVisibility = nsRectVisibility_kLeftOfViewport;
2162 else if (absRect.x + absRect.width > visibleRect.x + visibleRect.width &&
2163 absRect.x > visibleRect.x + visibleRect.width - aMinTwips)
2164 *aRectVisibility = nsRectVisibility_kRightOfViewport;
2165 else
2166 *aRectVisibility = nsRectVisibility_kVisible;
2168 return NS_OK;
2171 NS_IMETHODIMP
2172 nsViewManager::IsPainting(PRBool& aIsPainting)
2174 aIsPainting = IsPainting();
2175 return NS_OK;
2178 void
2179 nsViewManager::FlushPendingInvalidates()
2181 NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!\n");
2182 NS_ASSERTION(mUpdateBatchCnt == 0, "Must not be in an update batch!");
2183 // XXXbz this is probably not quite OK yet, if callers can explicitly
2184 // DisableRefresh while we have an event posted.
2185 // NS_ASSERTION(mRefreshEnabled, "How did we get here?");
2187 // Let all the view observers of all viewmanagers in this tree know that
2188 // we're about to "paint" (this lets them get in their invalidates now so
2189 // we don't go through two invalidate-processing cycles).
2190 NS_ASSERTION(gViewManagers, "Better have a viewmanagers array!");
2192 // Make sure to not send WillPaint notifications while scrolling
2193 if (mScrollCnt == 0) {
2194 // Disable refresh while we notify our view observers, so that if they do
2195 // view update batches we don't reenter this code and so that we batch
2196 // all of them together. We don't use
2197 // BeginUpdateViewBatch/EndUpdateViewBatch, since that would reenter this
2198 // exact code, but we want the effect of a single big update batch.
2199 PRBool refreshEnabled = mRefreshEnabled;
2200 mRefreshEnabled = PR_FALSE;
2201 ++mUpdateBatchCnt;
2203 PRInt32 index;
2204 for (index = 0; index < mVMCount; index++) {
2205 nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index);
2206 if (vm->RootViewManager() == this) {
2207 // One of our kids
2208 nsIViewObserver* observer = vm->GetViewObserver();
2209 if (observer) {
2210 observer->WillPaint();
2211 NS_ASSERTION(mUpdateBatchCnt == 1,
2212 "Observer did not end view batch?");
2217 --mUpdateBatchCnt;
2218 // Someone could have called EnableRefresh on us from inside WillPaint().
2219 // Only reset the old mRefreshEnabled value if the current value is false.
2220 if (!mRefreshEnabled) {
2221 mRefreshEnabled = refreshEnabled;
2225 if (mHasPendingUpdates) {
2226 ProcessPendingUpdates(mRootView, PR_TRUE);
2227 mHasPendingUpdates = PR_FALSE;
2231 void
2232 nsViewManager::ProcessInvalidateEvent()
2234 NS_ASSERTION(IsRootVM(),
2235 "Incorrectly targeted invalidate event");
2236 // If we're in the middle of an update batch, just repost the event,
2237 // to be processed when the batch ends.
2238 PRBool processEvent = (mUpdateBatchCnt == 0);
2239 if (processEvent) {
2240 FlushPendingInvalidates();
2242 mInvalidateEvent.Forget();
2243 if (!processEvent) {
2244 // We didn't actually process this event... post a new one
2245 PostInvalidateEvent();
2249 NS_IMETHODIMP
2250 nsViewManager::SetDefaultBackgroundColor(nscolor aColor)
2252 mDefaultBackgroundColor = aColor;
2253 return NS_OK;
2256 NS_IMETHODIMP
2257 nsViewManager::GetDefaultBackgroundColor(nscolor* aColor)
2259 *aColor = mDefaultBackgroundColor;
2260 return NS_OK;
2264 NS_IMETHODIMP
2265 nsViewManager::GetLastUserEventTime(PRUint32& aTime)
2267 aTime = gLastUserEventTime;
2268 return NS_OK;
2271 class nsSynthMouseMoveEvent : public nsViewManagerEvent {
2272 public:
2273 nsSynthMouseMoveEvent(nsViewManager *aViewManager,
2274 PRBool aFromScroll)
2275 : nsViewManagerEvent(aViewManager),
2276 mFromScroll(aFromScroll) {
2279 NS_IMETHOD Run() {
2280 if (mViewManager)
2281 mViewManager->ProcessSynthMouseMoveEvent(mFromScroll);
2282 return NS_OK;
2285 private:
2286 PRBool mFromScroll;
2289 NS_IMETHODIMP
2290 nsViewManager::SynthesizeMouseMove(PRBool aFromScroll)
2292 if (!IsRootVM())
2293 return RootViewManager()->SynthesizeMouseMove(aFromScroll);
2295 if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE))
2296 return NS_OK;
2298 if (!mSynthMouseMoveEvent.IsPending()) {
2299 nsRefPtr<nsViewManagerEvent> ev =
2300 new nsSynthMouseMoveEvent(this, aFromScroll);
2302 if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
2303 NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
2304 return NS_ERROR_UNEXPECTED;
2307 mSynthMouseMoveEvent = ev;
2310 return NS_OK;
2314 * Find the first floating view with a widget in a postorder traversal of the
2315 * view tree that contains the point. Thus more deeply nested floating views
2316 * are preferred over their ancestors, and floating views earlier in the
2317 * view hierarchy (i.e., added later) are preferred over their siblings.
2318 * This is adequate for finding the "topmost" floating view under a point,
2319 * given that floating views don't supporting having a specific z-index.
2321 * We cannot exit early when aPt is outside the view bounds, because floating
2322 * views aren't necessarily included in their parent's bounds, so this could
2323 * traverse the entire view hierarchy --- use carefully.
2325 static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt)
2327 if (aView->GetVisibility() == nsViewVisibility_kHide)
2328 // No need to look into descendants.
2329 return nsnull;
2331 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
2332 nsView* r = FindFloatingViewContaining(v, aPt - v->GetOffsetTo(aView));
2333 if (r)
2334 return r;
2337 if (aView->GetFloating() && aView->HasWidget() &&
2338 aView->GetDimensions().Contains(aPt))
2339 return aView;
2341 return nsnull;
2345 * This finds the first view containing the given point in a postorder
2346 * traversal of the view tree that contains the point, assuming that the
2347 * point is not in a floating view. It assumes that only floating views
2348 * extend outside the bounds of their parents.
2350 * This methods should only be called if FindFloatingViewContaining
2351 * returns null.
2353 static nsView* FindViewContaining(nsView* aView, nsPoint aPt)
2355 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
2356 if (aView->GetDimensions().Contains(aPt) &&
2357 aView->GetVisibility() != nsViewVisibility_kHide) {
2358 nsView* r = FindViewContaining(v, aPt - v->GetOffsetTo(aView));
2359 if (r)
2360 return r;
2361 return v;
2365 return nsnull;
2368 void
2369 nsViewManager::ProcessSynthMouseMoveEvent(PRBool aFromScroll)
2371 // allow new event to be posted while handling this one only if the
2372 // source of the event is a scroll (to prevent infinite reflow loops)
2373 if (aFromScroll)
2374 mSynthMouseMoveEvent.Forget();
2376 NS_ASSERTION(IsRootVM(), "Only the root view manager should be here");
2378 if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE) || !mRootView) {
2379 mSynthMouseMoveEvent.Forget();
2380 return;
2383 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
2384 // we need to access members after we call DispatchEvent).
2385 nsCOMPtr<nsIViewManager> kungFuDeathGrip(this);
2387 #ifdef DEBUG_MOUSE_LOCATION
2388 printf("[vm=%p]synthesizing mouse move to (%d,%d)\n",
2389 this, mMouseLocation.x, mMouseLocation.y);
2390 #endif
2392 nsPoint pt = mMouseLocation;
2393 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
2394 pt.x = NSIntPixelsToAppUnits(mMouseLocation.x, p2a);
2395 pt.y = NSIntPixelsToAppUnits(mMouseLocation.y, p2a);
2396 // This could be a bit slow (traverses entire view hierarchy)
2397 // but it's OK to do it once per synthetic mouse event
2398 nsView* view = FindFloatingViewContaining(mRootView, pt);
2399 nsPoint offset(0, 0);
2400 nsViewManager *pointVM;
2401 if (!view) {
2402 view = mRootView;
2403 nsView *pointView = FindViewContaining(mRootView, pt);
2404 // pointView can be null in situations related to mouse capture
2405 pointVM = (pointView ? pointView : view)->GetViewManager();
2406 } else {
2407 offset = view->GetOffsetTo(mRootView);
2408 offset.x = NSAppUnitsToIntPixels(offset.x, p2a);
2409 offset.y = NSAppUnitsToIntPixels(offset.y, p2a);
2410 pointVM = view->GetViewManager();
2412 nsMouseEvent event(PR_TRUE, NS_MOUSE_MOVE, view->GetWidget(),
2413 nsMouseEvent::eSynthesized);
2414 event.refPoint = mMouseLocation - offset;
2415 event.time = PR_IntervalNow();
2416 // XXX set event.isShift, event.isControl, event.isAlt, event.isMeta ?
2418 pointVM->GetViewObserver()->DispatchSynthMouseMove(&event, !aFromScroll);
2420 if (!aFromScroll)
2421 mSynthMouseMoveEvent.Forget();
2424 void
2425 nsViewManager::InvalidateHierarchy()
2427 if (mRootView) {
2428 if (!IsRootVM()) {
2429 NS_RELEASE(mRootViewManager);
2431 nsView *parent = mRootView->GetParent();
2432 if (parent) {
2433 mRootViewManager = parent->GetViewManager()->RootViewManager();
2434 NS_ADDREF(mRootViewManager);
2435 NS_ASSERTION(mRootViewManager != this,
2436 "Root view had a parent, but it has the same view manager");
2437 } else {
2438 mRootViewManager = this;