Bug 451504 js/src/jslock.cpp failed to compile on Solaris x86 r=igor
[wine-gecko.git] / view / src / nsViewManager.cpp
blob8348beb733eee70264007754c21f4d34be42fdbb
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;
126 // Find out if the root view is visible by asking the view observer
127 // (this won't be needed anymore if we link view trees across chrome /
128 // content boundaries in DocumentViewerImpl::MakeWindow).
129 nsIViewObserver* vo = aView->GetViewManager()->GetViewObserver();
130 return vo && vo->IsVisible();
133 void
134 nsViewManager::PostInvalidateEvent()
136 NS_ASSERTION(IsRootVM(), "Caller screwed up");
138 if (!mInvalidateEvent.IsPending()) {
139 nsRefPtr<nsViewManagerEvent> ev = new nsInvalidateEvent(this);
140 if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
141 NS_WARNING("failed to dispatch nsInvalidateEvent");
142 } else {
143 mInvalidateEvent = ev;
148 #undef DEBUG_MOUSE_LOCATION
150 PRInt32 nsViewManager::mVMCount = 0;
151 nsIRenderingContext* nsViewManager::gCleanupContext = nsnull;
153 // Weakly held references to all of the view managers
154 nsVoidArray* nsViewManager::gViewManagers = nsnull;
155 PRUint32 nsViewManager::gLastUserEventTime = 0;
157 nsViewManager::nsViewManager()
158 : mMouseLocation(NSCOORD_NONE, NSCOORD_NONE)
159 , mDelayedResize(NSCOORD_NONE, NSCOORD_NONE)
160 , mRootViewManager(this)
162 if (gViewManagers == nsnull) {
163 NS_ASSERTION(mVMCount == 0, "View Manager count is incorrect");
164 // Create an array to hold a list of view managers
165 gViewManagers = new nsVoidArray;
168 if (gCleanupContext == nsnull) {
169 /* XXX: This should use a device to create a matching |nsIRenderingContext| object */
170 CallCreateInstance(kRenderingContextCID, &gCleanupContext);
171 NS_ASSERTION(gCleanupContext,
172 "Wasn't able to create a graphics context for cleanup");
175 gViewManagers->AppendElement(this);
177 if (++mVMCount == 1) {
178 NS_AddFocusSuppressorCallback(&nsViewManager::SuppressFocusEvents);
180 // NOTE: we use a zeroing operator new, so all data members are
181 // assumed to be cleared here.
182 mDefaultBackgroundColor = NS_RGBA(0, 0, 0, 0);
183 mHasPendingUpdates = PR_FALSE;
184 mRecursiveRefreshPending = PR_FALSE;
185 mUpdateBatchFlags = 0;
188 nsViewManager::~nsViewManager()
190 if (mRootView) {
191 // Destroy any remaining views
192 mRootView->Destroy();
193 mRootView = nsnull;
196 // Make sure to revoke pending events for all viewmanagers, since some events
197 // are posted by a non-root viewmanager.
198 mInvalidateEvent.Revoke();
199 mSynthMouseMoveEvent.Revoke();
201 if (!IsRootVM()) {
202 // We have a strong ref to mRootViewManager
203 NS_RELEASE(mRootViewManager);
206 mRootScrollable = nsnull;
208 NS_ASSERTION((mVMCount > 0), "underflow of viewmanagers");
209 --mVMCount;
211 #ifdef DEBUG
212 PRBool removed =
213 #endif
214 gViewManagers->RemoveElement(this);
215 NS_ASSERTION(removed, "Viewmanager instance not was not in the global list of viewmanagers");
217 if (0 == mVMCount) {
218 // There aren't any more view managers so
219 // release the global array of view managers
221 NS_ASSERTION(gViewManagers != nsnull, "About to delete null gViewManagers");
222 delete gViewManagers;
223 gViewManagers = nsnull;
225 // Cleanup all of the offscreen drawing surfaces if the last view manager
226 // has been destroyed and there is something to cleanup
228 // Note: A global rendering context is needed because it is not possible
229 // to create a nsIRenderingContext during the shutdown of XPCOM. The last
230 // viewmanager is typically destroyed during XPCOM shutdown.
231 NS_IF_RELEASE(gCleanupContext);
234 mObserver = nsnull;
235 mContext = nsnull;
238 NS_IMPL_ISUPPORTS1(nsViewManager, nsIViewManager)
240 nsresult
241 nsViewManager::CreateRegion(nsIRegion* *result)
243 nsresult rv;
245 if (!mRegionFactory) {
246 mRegionFactory = do_GetClassObject(kRegionCID, &rv);
247 if (NS_FAILED(rv)) {
248 *result = nsnull;
249 return rv;
253 nsIRegion* region = nsnull;
254 rv = CallCreateInstance(mRegionFactory.get(), &region);
255 if (NS_SUCCEEDED(rv)) {
256 rv = region->Init();
257 *result = region;
259 return rv;
262 // We don't hold a reference to the presentation context because it
263 // holds a reference to us.
264 NS_IMETHODIMP nsViewManager::Init(nsIDeviceContext* aContext)
266 NS_PRECONDITION(nsnull != aContext, "null ptr");
268 if (nsnull == aContext) {
269 return NS_ERROR_NULL_POINTER;
271 if (nsnull != mContext) {
272 return NS_ERROR_ALREADY_INITIALIZED;
274 mContext = aContext;
276 mRefreshEnabled = PR_TRUE;
278 mMouseGrabber = nsnull;
280 return NS_OK;
283 NS_IMETHODIMP_(nsIView *)
284 nsViewManager::CreateView(const nsRect& aBounds,
285 const nsIView* aParent,
286 nsViewVisibility aVisibilityFlag)
288 nsView *v = new nsView(this, aVisibilityFlag);
289 if (v) {
290 v->SetPosition(aBounds.x, aBounds.y);
291 nsRect dim(0, 0, aBounds.width, aBounds.height);
292 v->SetDimensions(dim, PR_FALSE);
293 v->SetParent(static_cast<nsView*>(const_cast<nsIView*>(aParent)));
295 return v;
298 NS_IMETHODIMP_(nsIScrollableView *)
299 nsViewManager::CreateScrollableView(const nsRect& aBounds,
300 const nsIView* aParent)
302 nsScrollPortView *v = new nsScrollPortView(this);
303 if (v) {
304 v->SetPosition(aBounds.x, aBounds.y);
305 nsRect dim(0, 0, aBounds.width, aBounds.height);
306 v->SetDimensions(dim, PR_FALSE);
307 v->SetParent(static_cast<nsView*>(const_cast<nsIView*>(aParent)));
309 return v;
312 NS_IMETHODIMP nsViewManager::GetRootView(nsIView *&aView)
314 aView = mRootView;
315 return NS_OK;
318 NS_IMETHODIMP nsViewManager::SetRootView(nsIView *aView)
320 nsView* view = static_cast<nsView*>(aView);
322 NS_PRECONDITION(!view || view->GetViewManager() == this,
323 "Unexpected viewmanager on root view");
325 // Do NOT destroy the current root view. It's the caller's responsibility
326 // to destroy it
327 mRootView = view;
329 if (mRootView) {
330 nsView* parent = mRootView->GetParent();
331 if (parent) {
332 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
333 // no need to set mRootViewManager ourselves here.
334 parent->InsertChild(mRootView, nsnull);
335 } else {
336 InvalidateHierarchy();
339 mRootView->SetZIndex(PR_FALSE, 0, PR_FALSE);
341 // Else don't touch mRootViewManager
343 return NS_OK;
346 NS_IMETHODIMP nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
348 if (nsnull != mRootView) {
349 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
350 nsRect dim;
351 mRootView->GetDimensions(dim);
352 *aWidth = dim.width;
353 *aHeight = dim.height;
354 } else {
355 *aWidth = mDelayedResize.width;
356 *aHeight = mDelayedResize.height;
359 else
361 *aWidth = 0;
362 *aHeight = 0;
364 return NS_OK;
367 NS_IMETHODIMP nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
369 if (mRootView) {
370 if (IsViewVisible(mRootView)) {
371 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
372 DoSetWindowDimensions(aWidth, aHeight);
373 } else {
374 mDelayedResize.SizeTo(aWidth, aHeight);
378 return NS_OK;
381 static void ConvertNativeRegionToAppRegion(nsIRegion* aIn, nsRegion* aOut,
382 nsIDeviceContext* context)
384 nsRegionRectSet* rects = nsnull;
385 aIn->GetRects(&rects);
386 if (!rects)
387 return;
389 PRInt32 p2a = context->AppUnitsPerDevPixel();
391 PRUint32 i;
392 for (i = 0; i < rects->mNumRects; i++) {
393 const nsRegionRect& inR = rects->mRects[i];
394 nsRect outR;
395 outR.x = NSIntPixelsToAppUnits(inR.x, p2a);
396 outR.y = NSIntPixelsToAppUnits(inR.y, p2a);
397 outR.width = NSIntPixelsToAppUnits(inR.width, p2a);
398 outR.height = NSIntPixelsToAppUnits(inR.height, p2a);
399 aOut->Or(*aOut, outR);
402 aIn->FreeRects(rects);
405 static nsView* GetDisplayRootFor(nsView* aView)
407 nsView *displayRoot = aView;
408 for (;;) {
409 nsView *displayParent = displayRoot->GetParent();
410 if (!displayParent)
411 return displayRoot;
413 if (displayRoot->GetFloating() && !displayParent->GetFloating())
414 return displayRoot;
415 displayRoot = displayParent;
420 aRegion is given in device coordinates!!
422 void nsViewManager::Refresh(nsView *aView, nsIRenderingContext *aContext,
423 nsIRegion *aRegion, PRUint32 aUpdateFlags)
425 NS_ASSERTION(aRegion != nsnull, "Null aRegion");
427 if (! IsRefreshEnabled())
428 return;
430 nsRect viewRect;
431 aView->GetDimensions(viewRect);
432 nsPoint vtowoffset = aView->ViewToWidgetOffset();
434 // damageRegion is the damaged area, in twips, relative to the view origin
435 nsRegion damageRegion;
436 // convert pixels-relative-to-widget-origin to twips-relative-to-widget-origin
437 ConvertNativeRegionToAppRegion(aRegion, &damageRegion, mContext);
438 // move it from widget coordinates into view coordinates
439 damageRegion.MoveBy(viewRect.TopLeft() - vtowoffset);
441 if (damageRegion.IsEmpty()) {
442 #ifdef DEBUG_roc
443 nsRect damageRect = damageRegion.GetBounds();
444 printf("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
445 damageRect.x, damageRect.y, damageRect.width, damageRect.height,
446 viewRect.x, viewRect.y, viewRect.width, viewRect.height);
447 #endif
448 return;
451 #ifdef NS_VM_PERF_METRICS
452 MOZ_TIMER_DEBUGLOG(("Reset nsViewManager::Refresh(region), this=%p\n", this));
453 MOZ_TIMER_RESET(mWatch);
455 MOZ_TIMER_DEBUGLOG(("Start: nsViewManager::Refresh(region)\n"));
456 MOZ_TIMER_START(mWatch);
457 #endif
459 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
460 if (IsPainting()) {
461 RootViewManager()->mRecursiveRefreshPending = PR_TRUE;
462 return;
466 nsAutoScriptBlocker scriptBlocker;
467 SetPainting(PR_TRUE);
469 nsCOMPtr<nsIRenderingContext> localcx;
470 NS_ASSERTION(aView->GetWidget(),
471 "Must have a widget to calculate coordinates correctly");
472 if (nsnull == aContext)
474 localcx = CreateRenderingContext(*aView);
476 //couldn't get rendering context. this is ok at init time atleast
477 if (nsnull == localcx) {
478 SetPainting(PR_FALSE);
479 return;
481 } else {
482 // plain assignment grabs another reference.
483 localcx = aContext;
486 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
488 nsRefPtr<gfxContext> ctx = localcx->ThebesContext();
490 ctx->Save();
492 ctx->Translate(gfxPoint(gfxFloat(vtowoffset.x) / p2a,
493 gfxFloat(vtowoffset.y) / p2a));
495 ctx->Translate(gfxPoint(-gfxFloat(viewRect.x) / p2a,
496 -gfxFloat(viewRect.y) / p2a));
498 nsRegion opaqueRegion;
499 AddCoveringWidgetsToOpaqueRegion(opaqueRegion, mContext, aView);
500 damageRegion.Sub(damageRegion, opaqueRegion);
502 RenderViews(aView, *localcx, damageRegion);
504 ctx->Restore();
506 SetPainting(PR_FALSE);
509 if (RootViewManager()->mRecursiveRefreshPending) {
510 // Unset this flag first, since if aUpdateFlags includes NS_VMREFRESH_IMMEDIATE
511 // we'll reenter this code from the UpdateAllViews call.
512 RootViewManager()->mRecursiveRefreshPending = PR_FALSE;
513 UpdateAllViews(aUpdateFlags);
516 #ifdef NS_VM_PERF_METRICS
517 MOZ_TIMER_DEBUGLOG(("Stop: nsViewManager::Refresh(region), this=%p\n", this));
518 MOZ_TIMER_STOP(mWatch);
519 MOZ_TIMER_LOG(("vm2 Paint time (this=%p): ", this));
520 MOZ_TIMER_PRINT(mWatch);
521 #endif
525 // aRect is in app units and relative to the top-left of the aView->GetWidget()
526 void nsViewManager::DefaultRefresh(nsView* aView, nsIRenderingContext *aContext, const nsRect* aRect)
528 NS_PRECONDITION(aView, "Must have a view to work with!");
529 nsIWidget* widget = aView->GetNearestWidget(nsnull);
530 if (! widget)
531 return;
533 nsCOMPtr<nsIRenderingContext> context = aContext;
534 if (! aContext)
535 context = CreateRenderingContext(*aView);
537 if (! context)
538 return;
540 nscolor bgcolor = mDefaultBackgroundColor;
542 if (NS_GET_A(mDefaultBackgroundColor) == 0) {
543 NS_WARNING("nsViewManager: Asked to paint a default background, but no default background color is set!");
544 return;
547 context->SetColor(bgcolor);
548 context->FillRect(*aRect);
551 void nsViewManager::AddCoveringWidgetsToOpaqueRegion(nsRegion &aRgn, nsIDeviceContext* aContext,
552 nsView* aRootView) {
553 NS_PRECONDITION(aRootView, "Must have root view");
555 // We accumulate the bounds of widgets obscuring aRootView's widget into opaqueRgn.
556 // In OptimizeDisplayList, display list elements which lie behind obscuring native
557 // widgets are dropped.
558 // This shouldn't really be necessary, since the GFX/Widget toolkit should remove these
559 // covering widgets from the clip region passed into the paint command. But right now
560 // they only give us a paint rect and not a region, so we can't access that information.
561 // It's important to identifying areas that are covered by native widgets to avoid
562 // painting their views many times as we process invalidates from the root widget all the
563 // way down to the nested widgets.
565 // NB: we must NOT add widgets that correspond to floating views!
566 // We may be required to paint behind them
567 aRgn.SetEmpty();
569 nsIWidget* widget = aRootView->GetNearestWidget(nsnull);
570 if (!widget) {
571 return;
574 for (nsIWidget* childWidget = widget->GetFirstChild();
575 childWidget;
576 childWidget = childWidget->GetNextSibling()) {
577 PRBool widgetVisible;
578 childWidget->IsVisible(widgetVisible);
579 if (widgetVisible) {
580 nsView* view = nsView::GetViewFor(childWidget);
581 if (view && view->GetVisibility() == nsViewVisibility_kShow
582 && !view->GetFloating()) {
583 nsRect bounds = view->GetBounds();
584 if (bounds.width > 0 && bounds.height > 0) {
585 nsView* viewParent = view->GetParent();
587 while (viewParent && viewParent != aRootView) {
588 viewParent->ConvertToParentCoords(&bounds.x, &bounds.y);
589 viewParent = viewParent->GetParent();
592 // maybe we couldn't get the view into the coordinate
593 // system of aRootView (maybe it's not a descendant
594 // view of aRootView?); if so, don't use it
595 if (viewParent) {
596 aRgn.Or(aRgn, bounds);
604 // aRC and aRegion are in view coordinates
605 void nsViewManager::RenderViews(nsView *aView, nsIRenderingContext& aRC,
606 const nsRegion& aRegion)
608 if (mObserver) {
609 nsView* displayRoot = GetDisplayRootFor(aView);
610 nsPoint offsetToRoot = aView->GetOffsetTo(displayRoot);
611 nsRegion damageRegion(aRegion);
612 damageRegion.MoveBy(offsetToRoot);
614 aRC.PushState();
615 aRC.Translate(-offsetToRoot.x, -offsetToRoot.y);
616 mObserver->Paint(displayRoot, &aRC, damageRegion);
617 aRC.PopState();
621 void nsViewManager::ProcessPendingUpdates(nsView* aView, PRBool aDoInvalidate)
623 NS_ASSERTION(IsRootVM(), "Updates will be missed");
625 // Protect against a null-view.
626 if (!aView) {
627 return;
630 if (aView->HasWidget()) {
631 aView->ResetWidgetBounds(PR_FALSE, PR_FALSE, PR_TRUE);
634 // process pending updates in child view.
635 for (nsView* childView = aView->GetFirstChild(); childView;
636 childView = childView->GetNextSibling()) {
637 ProcessPendingUpdates(childView, aDoInvalidate);
640 if (aDoInvalidate && aView->HasNonEmptyDirtyRegion()) {
641 // Push out updates after we've processed the children; ensures that
642 // damage is applied based on the final widget geometry
643 NS_ASSERTION(mRefreshEnabled, "Cannot process pending updates with refresh disabled");
644 nsRegion* dirtyRegion = aView->GetDirtyRegion();
645 if (dirtyRegion) {
646 UpdateWidgetArea(aView, *dirtyRegion, nsnull);
647 dirtyRegion->SetEmpty();
652 NS_IMETHODIMP nsViewManager::Composite()
654 if (!IsRootVM()) {
655 return RootViewManager()->Composite();
658 if (UpdateCount() > 0)
660 ForceUpdate();
661 ClearUpdateCount();
664 return NS_OK;
667 NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, PRUint32 aUpdateFlags)
669 // Mark the entire view as damaged
670 nsView* view = static_cast<nsView*>(aView);
672 nsRect bounds = view->GetBounds();
673 view->ConvertFromParentCoords(&bounds.x, &bounds.y);
674 return UpdateView(view, bounds, aUpdateFlags);
677 // This method accumulates the intersectons of all dirty regions attached to
678 // descendants of aSourceView with the cliprect of aTargetView into the dirty
679 // region of aTargetView, after offseting said intersections by aOffset.
680 static void
681 AccumulateIntersectionsIntoDirtyRegion(nsView* aTargetView,
682 nsView* aSourceView,
683 const nsPoint& aOffset)
685 if (aSourceView->HasNonEmptyDirtyRegion()) {
686 // In most cases, aSourceView is an ancestor of aTargetView, since most
687 // commonly we have dirty rects on the root view.
688 nsPoint offset = aTargetView->GetOffsetTo(aSourceView);
689 nsRegion intersection;
690 intersection = *aSourceView->GetDirtyRegion();
691 if (!intersection.IsEmpty()) {
692 nsRegion* targetRegion = aTargetView->GetDirtyRegion();
693 if (targetRegion) {
694 intersection.MoveBy(-offset + aOffset);
695 targetRegion->Or(*targetRegion, intersection);
696 // Random simplification number...
697 targetRegion->SimplifyOutward(20);
702 if (aSourceView == aTargetView) {
703 // No need to do this with kids of aTargetView
704 return;
707 for (nsView* kid = aSourceView->GetFirstChild();
708 kid;
709 kid = kid->GetNextSibling()) {
710 AccumulateIntersectionsIntoDirtyRegion(aTargetView, kid, aOffset);
714 nsresult
715 nsViewManager::WillBitBlit(nsView* aView, nsPoint aScrollAmount)
717 if (!IsRootVM()) {
718 RootViewManager()->WillBitBlit(aView, aScrollAmount);
719 return NS_OK;
722 NS_PRECONDITION(aView, "Must have a view");
723 NS_PRECONDITION(aView->HasWidget(), "View must have a widget");
725 ++mScrollCnt;
727 // Since the view is actually moving the widget by -aScrollAmount, that's the
728 // offset we want to use when accumulating dirty rects.
729 AccumulateIntersectionsIntoDirtyRegion(aView, GetRootView(), -aScrollAmount);
730 return NS_OK;
733 // Invalidate all widgets which overlap the view, other than the view's own widgets.
734 void
735 nsViewManager::UpdateViewAfterScroll(nsView *aView, const nsRegion& aUpdateRegion)
737 NS_ASSERTION(RootViewManager()->mScrollCnt > 0,
738 "Someone forgot to call WillBitBlit()");
739 // Look at the view's clipped rect. It may be that part of the view is clipped out
740 // in which case we don't need to worry about invalidating the clipped-out part.
741 nsRect damageRect = aView->GetDimensions();
742 if (damageRect.IsEmpty()) {
743 // Don't forget to undo mScrollCnt!
744 --RootViewManager()->mScrollCnt;
745 return;
748 nsView* displayRoot = GetDisplayRootFor(aView);
749 nsPoint offset = aView->GetOffsetTo(displayRoot);
750 damageRect.MoveBy(offset);
752 UpdateWidgetArea(displayRoot, nsRegion(damageRect), aView);
753 if (!aUpdateRegion.IsEmpty()) {
754 // XXX We should update the region, not the bounds rect, but that requires
755 // a little more work. Fix this when we reshuffle this code.
756 nsRegion update(aUpdateRegion);
757 update.MoveBy(offset);
758 UpdateWidgetArea(displayRoot, update, nsnull);
759 // FlushPendingInvalidates();
762 Composite();
763 --RootViewManager()->mScrollCnt;
767 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
768 * every widget child of aWidgetView, plus aWidgetView's own widget
769 * @param aIgnoreWidgetView if non-null, the aIgnoreWidgetView's widget and its
770 * children are not updated.
772 void
773 nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion,
774 nsView* aIgnoreWidgetView)
776 if (!IsRefreshEnabled()) {
777 // accumulate this rectangle in the view's dirty region, so we can
778 // process it later.
779 nsRegion* dirtyRegion = aWidgetView->GetDirtyRegion();
780 if (!dirtyRegion) return;
782 dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
783 // Don't let dirtyRegion grow beyond 8 rects
784 dirtyRegion->SimplifyOutward(8);
785 nsViewManager* rootVM = RootViewManager();
786 rootVM->mHasPendingUpdates = PR_TRUE;
787 rootVM->IncrementUpdateCount();
788 return;
789 // this should only happen at the top level, and this result
790 // should not be consumed by top-level callers, so it doesn't
791 // really matter what we return
794 // If the bounds don't overlap at all, there's nothing to do
795 nsRegion intersection;
796 intersection.And(aWidgetView->GetDimensions(), aDamagedRegion);
797 if (intersection.IsEmpty()) {
798 return;
801 // If the widget is hidden, it don't cover nothing
802 if (nsViewVisibility_kHide == aWidgetView->GetVisibility()) {
803 #ifdef DEBUG
804 // Assert if view is hidden but widget is visible
805 nsIWidget* widget = aWidgetView->GetNearestWidget(nsnull);
806 if (widget) {
807 PRBool visible;
808 widget->IsVisible(visible);
809 NS_ASSERTION(!visible, "View is hidden but widget is visible!");
811 #endif
812 return;
815 if (aWidgetView == aIgnoreWidgetView) {
816 // the widget for aIgnoreWidgetView (and its children) should be treated as already updated.
817 return;
820 nsIWidget* widget = aWidgetView->GetNearestWidget(nsnull);
821 if (!widget) {
822 // The root view or a scrolling view might not have a widget
823 // (for example, during printing). We get here when we scroll
824 // during printing to show selected options in a listbox, for example.
825 return;
828 // Update all child widgets with the damage. In the process,
829 // accumulate the union of all the child widget areas, or at least
830 // some subset of that.
831 nsRegion children;
832 for (nsIWidget* childWidget = widget->GetFirstChild();
833 childWidget;
834 childWidget = childWidget->GetNextSibling()) {
835 nsView* view = nsView::GetViewFor(childWidget);
836 NS_ASSERTION(view != aWidgetView, "will recur infinitely");
837 if (view && view->GetVisibility() == nsViewVisibility_kShow) {
838 // Don't mess with views that are in completely different view
839 // manager trees
840 if (view->GetViewManager()->RootViewManager() == RootViewManager()) {
841 // get the damage region into 'view's coordinate system
842 nsRegion damage = intersection;
843 nsPoint offset = view->GetOffsetTo(aWidgetView);
844 damage.MoveBy(-offset);
845 UpdateWidgetArea(view, damage, aIgnoreWidgetView);
846 children.Or(children, view->GetDimensions() + offset);
847 children.SimplifyInward(20);
852 nsRegion leftOver;
853 leftOver.Sub(intersection, children);
855 if (!leftOver.IsEmpty()) {
856 NS_ASSERTION(IsRefreshEnabled(), "Can only get here with refresh enabled, I hope");
858 const nsRect* r;
859 for (nsRegionRectIterator iter(leftOver); (r = iter.Next());) {
860 nsRect bounds = *r;
861 ViewToWidget(aWidgetView, aWidgetView, bounds);
862 widget->Invalidate(bounds, PR_FALSE);
867 NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, const nsRect &aRect, PRUint32 aUpdateFlags)
869 NS_PRECONDITION(nsnull != aView, "null view");
871 nsView* view = static_cast<nsView*>(aView);
873 nsRect damagedRect(aRect);
875 // If the rectangle is not visible then abort
876 // without invalidating. This is a performance
877 // enhancement since invalidating a native widget
878 // can be expensive.
879 // This also checks for silly request like damagedRect.width = 0 or damagedRect.height = 0
880 nsRectVisibility rectVisibility;
881 GetRectVisibility(view, damagedRect, 0, &rectVisibility);
882 if (rectVisibility != nsRectVisibility_kVisible) {
883 return NS_OK;
886 // if this is a floating view, it isn't covered by any widgets other than
887 // its children. In that case we walk up to its parent widget and use
888 // that as the root to update from. This also means we update areas that
889 // may be outside the parent view(s), which is necessary for floats.
890 if (view->GetFloating()) {
891 nsView* widgetParent = view;
893 while (!widgetParent->HasWidget()) {
894 widgetParent->ConvertToParentCoords(&damagedRect.x, &damagedRect.y);
895 widgetParent = widgetParent->GetParent();
898 UpdateWidgetArea(widgetParent, nsRegion(damagedRect), nsnull);
899 } else {
900 // Propagate the update to the root widget of the root view manager, since
901 // iframes, for example, can overlap each other and be translucent. So we
902 // have to possibly invalidate our rect in each of the widgets we have
903 // lying about.
904 damagedRect.MoveBy(ComputeViewOffset(view));
906 UpdateWidgetArea(RootViewManager()->GetRootView(), nsRegion(damagedRect), nsnull);
909 RootViewManager()->IncrementUpdateCount();
911 if (!IsRefreshEnabled()) {
912 return NS_OK;
915 // See if we should do an immediate refresh or wait
916 if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
917 Composite();
920 return NS_OK;
923 NS_IMETHODIMP nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags)
925 if (RootViewManager() != this) {
926 return RootViewManager()->UpdateAllViews(aUpdateFlags);
929 UpdateViews(mRootView, aUpdateFlags);
930 return NS_OK;
933 void nsViewManager::UpdateViews(nsView *aView, PRUint32 aUpdateFlags)
935 // update this view.
936 UpdateView(aView, aUpdateFlags);
938 // update all children as well.
939 nsView* childView = aView->GetFirstChild();
940 while (nsnull != childView) {
941 UpdateViews(childView, aUpdateFlags);
942 childView = childView->GetNextSibling();
946 nsView *nsViewManager::sCurrentlyFocusView = nsnull;
947 nsView *nsViewManager::sViewFocusedBeforeSuppression = nsnull;
948 PRBool nsViewManager::sFocusSuppressed = PR_FALSE;
950 void nsViewManager::SuppressFocusEvents(PRBool aSuppress)
952 if (aSuppress) {
953 sFocusSuppressed = PR_TRUE;
954 SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView());
955 return;
958 sFocusSuppressed = PR_FALSE;
959 if (GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression()) {
960 return;
963 // We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS.
964 nsIWidget *widget = nsnull;
965 nsEventStatus status;
967 // Backup what is focused before we send the blur. If the
968 // blur causes a focus change, keep that new focus change,
969 // don't overwrite with the old "currently focused view".
970 nsIView *currentFocusBeforeBlur = GetCurrentlyFocusedView();
972 // Send NS_LOSTFOCUS to widget that was focused before
973 // focus/blur suppression.
974 if (GetViewFocusedBeforeSuppression()) {
975 widget = GetViewFocusedBeforeSuppression()->GetWidget();
976 if (widget) {
977 #ifdef DEBUG_FOCUS_SUPPRESSION
978 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_LOSTFOCUS\n");
979 #endif
980 nsGUIEvent event(PR_TRUE, NS_LOSTFOCUS, widget);
981 widget->DispatchEvent(&event, status);
985 // Send NS_GOTFOCUS to the widget that we think should be focused.
986 if (GetCurrentlyFocusedView() &&
987 currentFocusBeforeBlur == GetCurrentlyFocusedView())
989 widget = GetCurrentlyFocusedView()->GetWidget();
990 if (widget) {
991 #ifdef DEBUG_FOCUS_SUPPRESSION
992 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_GOTFOCUS\n");
993 #endif
994 nsGUIEvent event(PR_TRUE, NS_GOTFOCUS, widget);
995 widget->DispatchEvent(&event, status);
1001 static void ConvertRectAppUnitsToIntPixels(nsRect& aRect, PRInt32 p2a)
1003 aRect.x = NSAppUnitsToIntPixels(aRect.x, p2a);
1004 aRect.y = NSAppUnitsToIntPixels(aRect.y, p2a);
1005 aRect.width = NSAppUnitsToIntPixels(aRect.width, p2a);
1006 aRect.height = NSAppUnitsToIntPixels(aRect.height, p2a);
1009 NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus)
1011 *aStatus = nsEventStatus_eIgnore;
1013 switch(aEvent->message)
1015 case NS_SIZE:
1017 nsView* view = nsView::GetViewFor(aEvent->widget);
1019 if (nsnull != view)
1021 nscoord width = ((nsSizeEvent*)aEvent)->windowSize->width;
1022 nscoord height = ((nsSizeEvent*)aEvent)->windowSize->height;
1023 width = ((nsSizeEvent*)aEvent)->mWinWidth;
1024 height = ((nsSizeEvent*)aEvent)->mWinHeight;
1026 // The root view may not be set if this is the resize associated with
1027 // window creation
1029 if (view == mRootView)
1031 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
1032 SetWindowDimensions(NSIntPixelsToAppUnits(width, p2a),
1033 NSIntPixelsToAppUnits(height, p2a));
1034 *aStatus = nsEventStatus_eConsumeNoDefault;
1038 break;
1041 case NS_PAINT:
1043 nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
1044 nsView *view = nsView::GetViewFor(aEvent->widget);
1046 if (!view || !mContext)
1047 break;
1049 *aStatus = nsEventStatus_eConsumeNoDefault;
1051 // The rect is in device units, and it's in the coordinate space of its
1052 // associated window.
1053 nsCOMPtr<nsIRegion> region = event->region;
1054 if (!region) {
1055 if (NS_FAILED(CreateRegion(getter_AddRefs(region))))
1056 break;
1058 const nsRect& damrect = *event->rect;
1059 region->SetTo(damrect.x, damrect.y, damrect.width, damrect.height);
1062 if (region->IsEmpty())
1063 break;
1065 // Refresh the view
1066 if (IsRefreshEnabled()) {
1067 // If an ancestor widget was hidden and then shown, we could
1068 // have a delayed resize to handle.
1069 PRBool didResize = PR_FALSE;
1070 for (nsViewManager *vm = this; vm;
1071 vm = vm->mRootView->GetParent()
1072 ? vm->mRootView->GetParent()->GetViewManager()
1073 : nsnull) {
1074 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
1075 IsViewVisible(vm->mRootView)) {
1076 vm->DoSetWindowDimensions(vm->mDelayedResize.width,
1077 vm->mDelayedResize.height);
1078 vm->mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
1080 // Paint later.
1081 vm->UpdateView(vm->mRootView, NS_VMREFRESH_NO_SYNC);
1082 didResize = PR_TRUE;
1084 // not sure if it's valid for us to claim that we
1085 // ignored this, but we're going to do so anyway, since
1086 // we didn't actually paint anything
1087 *aStatus = nsEventStatus_eIgnore;
1091 if (!didResize) {
1092 //NS_ASSERTION(IsViewVisible(view), "painting an invisible view");
1094 // Just notify our own view observer that we're about to paint
1095 // XXXbz do we need to notify other view observers for viewmanagers
1096 // in our tree?
1097 // Make sure to not send WillPaint notifications while scrolling
1098 nsRefPtr<nsViewManager> rootVM = RootViewManager();
1100 nsIWidget *widget = mRootView->GetWidget();
1101 PRBool transparentWindow = PR_FALSE;
1102 if (widget)
1103 transparentWindow = widget->GetTransparencyMode() == eTransparencyTransparent;
1105 if (rootVM->mScrollCnt == 0 && !transparentWindow) {
1106 nsIViewObserver* observer = GetViewObserver();
1107 if (observer) {
1108 // Do an update view batch. Make sure not to do it DEFERRED,
1109 // since that would effectively delay any invalidates that are
1110 // triggered by the WillPaint notification (they'd happen when
1111 // the invalid event fires, which is later than the reflow
1112 // event would fire and could end up being after some timer
1113 // events, leading to frame dropping in DHTML). Note that the
1114 // observer may try to reenter this code from inside
1115 // WillPaint() by trying to do a synchronous paint, but since
1116 // refresh will be disabled it won't be able to do the paint.
1117 // We should really sort out the rules on our synch painting
1118 // api....
1119 UpdateViewBatch batch(this);
1120 observer->WillPaint();
1121 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
1123 // Get the view pointer again since the code above might have
1124 // destroyed it (bug 378273).
1125 view = nsView::GetViewFor(aEvent->widget);
1128 // Make sure to sync up any widget geometry changes we
1129 // have pending before we paint.
1130 if (rootVM->mHasPendingUpdates) {
1131 rootVM->ProcessPendingUpdates(mRootView, PR_FALSE);
1134 if (view) {
1135 Refresh(view, event->renderingContext, region,
1136 NS_VMREFRESH_DOUBLE_BUFFER);
1139 } else {
1140 // since we got an NS_PAINT event, we need to
1141 // draw something so we don't get blank areas.
1142 nsRect damRect;
1143 region->GetBoundingBox(&damRect.x, &damRect.y, &damRect.width, &damRect.height);
1144 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
1145 damRect.ScaleRoundOut(float(p2a));
1146 DefaultRefresh(view, event->renderingContext, &damRect);
1148 // Clients like the editor can trigger multiple
1149 // reflows during what the user perceives as a single
1150 // edit operation, so it disables view manager
1151 // refreshing until the edit operation is complete
1152 // so that users don't see the intermediate steps.
1154 // Unfortunately some of these reflows can trigger
1155 // nsScrollPortView and nsScrollingView Scroll() calls
1156 // which in most cases force an immediate BitBlt and
1157 // synchronous paint to happen even if the view manager's
1158 // refresh is disabled. (Bug 97674)
1160 // Calling UpdateView() here, is necessary to add
1161 // the exposed region specified in the synchronous paint
1162 // event to the view's damaged region so that it gets
1163 // painted properly when refresh is enabled.
1165 // Note that calling UpdateView() here was deemed
1166 // to have the least impact on performance, since the
1167 // other alternative was to make Scroll() post an
1168 // async paint event for the *entire* ScrollPort or
1169 // ScrollingView's viewable area. (See bug 97674 for this
1170 // alternate patch.)
1172 UpdateView(view, damRect, NS_VMREFRESH_NO_SYNC);
1175 break;
1178 case NS_CREATE:
1179 case NS_DESTROY:
1180 case NS_SETZLEVEL:
1181 case NS_MOVE:
1182 /* Don't pass these events through. Passing them through
1183 causes performance problems on pages with lots of views/frames
1184 @see bug 112861 */
1185 *aStatus = nsEventStatus_eConsumeNoDefault;
1186 break;
1189 case NS_DISPLAYCHANGED:
1191 //Destroy the cached backbuffer to force a new backbuffer
1192 //be constructed with the appropriate display depth.
1193 //@see bugzilla bug 6061
1194 *aStatus = nsEventStatus_eConsumeDoDefault;
1195 break;
1197 case NS_SYSCOLORCHANGED:
1199 // Hold a refcount to the observer. The continued existence of the observer will
1200 // delay deletion of this view hierarchy should the event want to cause its
1201 // destruction in, say, some JavaScript event handler.
1202 nsView *view = nsView::GetViewFor(aEvent->widget);
1203 nsCOMPtr<nsIViewObserver> obs = GetViewObserver();
1204 if (obs) {
1205 obs->HandleEvent(view, aEvent, aStatus);
1208 break;
1210 default:
1212 if (aEvent->message == NS_GOTFOCUS) {
1213 #ifdef DEBUG_FOCUS_SUPPRESSION
1214 printf("*** 0 INFO TODO [CPEARCE] Focus changing%s\n",
1215 (nsViewManager::IsFocusSuppressed() ? " while suppressed" : ""));
1216 #endif
1217 SetCurrentlyFocusedView(nsView::GetViewFor(aEvent->widget));
1219 if ((aEvent->message == NS_GOTFOCUS || aEvent->message == NS_LOSTFOCUS) &&
1220 nsViewManager::IsFocusSuppressed())
1222 #ifdef DEBUG_FOCUS_SUPPRESSION
1223 printf("*** 0 INFO TODO [CPEARCE] Suppressing %s\n",
1224 (aEvent->message == NS_GOTFOCUS ? "NS_GOTFOCUS" : "NS_LOSTFOCUS"));
1225 #endif
1226 break;
1229 if ((NS_IS_MOUSE_EVENT(aEvent) &&
1230 // Ignore moves that we synthesize.
1231 static_cast<nsMouseEvent*>(aEvent)->reason ==
1232 nsMouseEvent::eReal &&
1233 // Ignore mouse exit and enter (we'll get moves if the user
1234 // is really moving the mouse) since we get them when we
1235 // create and destroy widgets.
1236 aEvent->message != NS_MOUSE_EXIT &&
1237 aEvent->message != NS_MOUSE_ENTER) ||
1238 NS_IS_KEY_EVENT(aEvent) ||
1239 NS_IS_IME_EVENT(aEvent)) {
1240 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1243 if (aEvent->message == NS_DEACTIVATE) {
1244 PRBool result;
1245 GrabMouseEvents(nsnull, result);
1248 //Find the view whose coordinates system we're in.
1249 nsView* baseView = nsView::GetViewFor(aEvent->widget);
1250 nsView* view = baseView;
1251 PRBool capturedEvent = PR_FALSE;
1253 if (!NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_EVENT(aEvent) &&
1254 !NS_IS_CONTEXT_MENU_KEY(aEvent) && !NS_IS_FOCUS_EVENT(aEvent) &&
1255 !NS_IS_QUERY_CONTENT_EVENT(aEvent) &&
1256 aEvent->eventStructType != NS_ACCESSIBLE_EVENT) {
1257 // will dispatch using coordinates. Pretty bogus but it's consistent
1258 // with what presshell does.
1259 view = GetDisplayRootFor(baseView);
1262 //Find the view to which we're initially going to send the event
1263 //for hittesting.
1264 if (NS_IS_MOUSE_EVENT(aEvent) || NS_IS_DRAG_EVENT(aEvent)) {
1265 nsView* mouseGrabber = GetMouseEventGrabber();
1266 if (mouseGrabber) {
1267 view = mouseGrabber;
1268 capturedEvent = PR_TRUE;
1272 if (nsnull != view) {
1273 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
1275 if ((aEvent->message == NS_MOUSE_MOVE &&
1276 static_cast<nsMouseEvent*>(aEvent)->reason ==
1277 nsMouseEvent::eReal) ||
1278 aEvent->message == NS_MOUSE_ENTER) {
1279 // aEvent->point is relative to the widget, i.e. the view top-left,
1280 // so we need to add the offset to the view origin
1281 nsPoint rootOffset = baseView->GetDimensions().TopLeft();
1282 rootOffset += baseView->GetOffsetTo(RootViewManager()->mRootView);
1283 RootViewManager()->mMouseLocation = aEvent->refPoint +
1284 nsPoint(NSAppUnitsToIntPixels(rootOffset.x, p2a),
1285 NSAppUnitsToIntPixels(rootOffset.y, p2a));
1286 #ifdef DEBUG_MOUSE_LOCATION
1287 if (aEvent->message == NS_MOUSE_ENTER)
1288 printf("[vm=%p]got mouse enter for %p\n",
1289 this, aEvent->widget);
1290 printf("[vm=%p]setting mouse location to (%d,%d)\n",
1291 this, mMouseLocation.x, mMouseLocation.y);
1292 #endif
1293 if (aEvent->message == NS_MOUSE_ENTER)
1294 SynthesizeMouseMove(PR_FALSE);
1295 } else if (aEvent->message == NS_MOUSE_EXIT) {
1296 // Although we only care about the mouse moving into an area
1297 // for which this view manager doesn't receive mouse move
1298 // events, we don't check which view the mouse exit was for
1299 // since this seems to vary by platform. Hopefully this
1300 // won't matter at all since we'll get the mouse move or
1301 // enter after the mouse exit when the mouse moves from one
1302 // of our widgets into another.
1303 RootViewManager()->mMouseLocation = nsPoint(NSCOORD_NONE, NSCOORD_NONE);
1304 #ifdef DEBUG_MOUSE_LOCATION
1305 printf("[vm=%p]got mouse exit for %p\n",
1306 this, aEvent->widget);
1307 printf("[vm=%p]clearing mouse location\n",
1308 this);
1309 #endif
1312 //Calculate the proper offset for the view we're going to
1313 nsPoint offset(0, 0);
1315 if (view != baseView) {
1316 //Get offset from root of baseView
1317 nsView *parent;
1318 for (parent = baseView; parent; parent = parent->GetParent())
1319 parent->ConvertToParentCoords(&offset.x, &offset.y);
1321 //Subtract back offset from root of view
1322 for (parent = view; parent; parent = parent->GetParent())
1323 parent->ConvertFromParentCoords(&offset.x, &offset.y);
1326 // Dispatch the event
1327 nsRect baseViewDimensions;
1328 if (baseView != nsnull) {
1329 baseView->GetDimensions(baseViewDimensions);
1332 nsPoint pt;
1333 pt.x = baseViewDimensions.x +
1334 NSFloatPixelsToAppUnits(float(aEvent->refPoint.x) + 0.5f, p2a);
1335 pt.y = baseViewDimensions.y +
1336 NSFloatPixelsToAppUnits(float(aEvent->refPoint.y) + 0.5f, p2a);
1337 pt += offset;
1339 *aStatus = HandleEvent(view, pt, aEvent, capturedEvent);
1342 // need to map the reply back into platform coordinates
1344 switch (aEvent->message) {
1345 case NS_TEXT_TEXT:
1346 ConvertRectAppUnitsToIntPixels(
1347 ((nsTextEvent*)aEvent)->theReply.mCursorPosition, p2a);
1348 break;
1349 case NS_COMPOSITION_START:
1350 case NS_COMPOSITION_QUERY:
1351 ConvertRectAppUnitsToIntPixels(
1352 ((nsCompositionEvent*)aEvent)->theReply.mCursorPosition, p2a);
1353 break;
1354 case NS_QUERY_CHARACTER_RECT:
1355 case NS_QUERY_CARET_RECT:
1356 ConvertRectAppUnitsToIntPixels(
1357 ((nsQueryContentEvent*)aEvent)->mReply.mRect, p2a);
1358 break;
1362 break;
1366 return NS_OK;
1369 nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsPoint aPoint,
1370 nsGUIEvent* aEvent, PRBool aCaptured) {
1371 //printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports,
1372 // event->message, event->point.x, event->point.y);
1374 // Hold a refcount to the observer. The continued existence of the observer will
1375 // delay deletion of this view hierarchy should the event want to cause its
1376 // destruction in, say, some JavaScript event handler.
1377 nsCOMPtr<nsIViewObserver> obs = aView->GetViewManager()->GetViewObserver();
1378 nsEventStatus status = nsEventStatus_eIgnore;
1379 if (obs) {
1380 obs->HandleEvent(aView, aEvent, &status);
1383 return status;
1386 NS_IMETHODIMP nsViewManager::GrabMouseEvents(nsIView *aView, PRBool &aResult)
1388 if (!IsRootVM()) {
1389 return RootViewManager()->GrabMouseEvents(aView, aResult);
1392 // Along with nsView::SetVisibility, we enforce that the mouse grabber
1393 // can never be a hidden view.
1394 if (aView && static_cast<nsView*>(aView)->GetVisibility()
1395 == nsViewVisibility_kHide) {
1396 aView = nsnull;
1399 #ifdef DEBUG_mjudge
1400 if (aView)
1402 printf("capturing mouse events for view %x\n",aView);
1404 printf("removing mouse capture from view %x\n",mMouseGrabber);
1405 #endif
1407 mMouseGrabber = static_cast<nsView*>(aView);
1408 aResult = PR_TRUE;
1409 return NS_OK;
1412 NS_IMETHODIMP nsViewManager::GetMouseEventGrabber(nsIView *&aView)
1414 aView = GetMouseEventGrabber();
1415 return NS_OK;
1418 // Recursively reparent widgets if necessary
1420 void nsViewManager::ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget)
1422 if (aView->HasWidget()) {
1423 // Check to see if the parent widget is the
1424 // same as the new parent. If not then reparent
1425 // the widget, otherwise there is nothing more
1426 // to do for the view and its descendants
1427 nsIWidget* widget = aView->GetWidget();
1428 nsIWidget* parentWidget = widget->GetParent();
1429 // Toplevel widgets should not be reparented!
1430 if (parentWidget && parentWidget != aNewWidget) {
1431 #ifdef DEBUG
1432 nsresult rv =
1433 #endif
1434 widget->SetParent(aNewWidget);
1435 NS_ASSERTION(NS_SUCCEEDED(rv), "SetParent failed!");
1437 return;
1440 // Need to check each of the views children to see
1441 // if they have a widget and reparent it.
1443 nsView* view = static_cast<nsView*>(aView);
1444 for (nsView *kid = view->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
1445 ReparentChildWidgets(kid, aNewWidget);
1449 // Reparent a view and its descendant views widgets if necessary
1451 void nsViewManager::ReparentWidgets(nsIView* aView, nsIView *aParent)
1453 NS_PRECONDITION(aParent, "Must have a parent");
1454 NS_PRECONDITION(aView, "Must have a view");
1456 // Quickly determine whether the view has pre-existing children or a
1457 // widget. In most cases the view will not have any pre-existing
1458 // children when this is called. Only in the case
1459 // where a view has been reparented by removing it from
1460 // a reinserting it into a new location in the view hierarchy do we
1461 // have to consider reparenting the existing widgets for the view and
1462 // it's descendants.
1463 nsView* view = static_cast<nsView*>(aView);
1464 if (view->HasWidget() || view->GetFirstChild()) {
1465 nsIWidget* parentWidget = aParent->GetNearestWidget(nsnull);
1466 if (parentWidget) {
1467 ReparentChildWidgets(aView, parentWidget);
1468 return;
1470 NS_WARNING("Can not find a widget for the parent view");
1474 NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, nsIView *aSibling,
1475 PRBool aAfter)
1477 nsView* parent = static_cast<nsView*>(aParent);
1478 nsView* child = static_cast<nsView*>(aChild);
1479 nsView* sibling = static_cast<nsView*>(aSibling);
1481 NS_PRECONDITION(nsnull != parent, "null ptr");
1482 NS_PRECONDITION(nsnull != child, "null ptr");
1483 NS_ASSERTION(sibling == nsnull || sibling->GetParent() == parent,
1484 "tried to insert view with invalid sibling");
1485 NS_ASSERTION(!IsViewInserted(child), "tried to insert an already-inserted view");
1487 if ((nsnull != parent) && (nsnull != child))
1489 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
1490 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
1492 #if 1
1493 if (nsnull == aSibling) {
1494 if (aAfter) {
1495 // insert at end of document order, i.e., before first view
1496 // this is the common case, by far
1497 parent->InsertChild(child, nsnull);
1498 ReparentWidgets(child, parent);
1499 } else {
1500 // insert at beginning of document order, i.e., after last view
1501 nsView *kid = parent->GetFirstChild();
1502 nsView *prev = nsnull;
1503 while (kid) {
1504 prev = kid;
1505 kid = kid->GetNextSibling();
1507 // prev is last view or null if there are no children
1508 parent->InsertChild(child, prev);
1509 ReparentWidgets(child, parent);
1511 } else {
1512 nsView *kid = parent->GetFirstChild();
1513 nsView *prev = nsnull;
1514 while (kid && sibling != kid) {
1515 //get the next sibling view
1516 prev = kid;
1517 kid = kid->GetNextSibling();
1519 NS_ASSERTION(kid != nsnull,
1520 "couldn't find sibling in child list");
1521 if (aAfter) {
1522 // insert after 'kid' in document order, i.e. before in view order
1523 parent->InsertChild(child, prev);
1524 ReparentWidgets(child, parent);
1525 } else {
1526 // insert before 'kid' in document order, i.e. after in view order
1527 parent->InsertChild(child, kid);
1528 ReparentWidgets(child, parent);
1531 #else // don't keep consistent document order, but order things by z-index instead
1532 // essentially we're emulating the old InsertChild(parent, child, zindex)
1533 PRInt32 zIndex = child->GetZIndex();
1534 while (nsnull != kid)
1536 PRInt32 idx = kid->GetZIndex();
1538 if (CompareZIndex(zIndex, child->IsTopMost(), child->GetZIndexIsAuto(),
1539 idx, kid->IsTopMost(), kid->GetZIndexIsAuto()) >= 0)
1540 break;
1542 prev = kid;
1543 kid = kid->GetNextSibling();
1546 parent->InsertChild(child, prev);
1547 ReparentWidgets(child, parent);
1548 #endif
1550 // if the parent view is marked as "floating", make the newly added view float as well.
1551 if (parent->GetFloating())
1552 child->SetFloating(PR_TRUE);
1554 //and mark this area as dirty if the view is visible...
1556 if (nsViewVisibility_kHide != child->GetVisibility())
1557 UpdateView(child, NS_VMREFRESH_NO_SYNC);
1559 return NS_OK;
1562 NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, PRInt32 aZIndex)
1564 // no-one really calls this with anything other than aZIndex == 0 on a fresh view
1565 // XXX this method should simply be eliminated and its callers redirected to the real method
1566 SetViewZIndex(aChild, PR_FALSE, aZIndex, PR_FALSE);
1567 return InsertChild(aParent, aChild, nsnull, PR_TRUE);
1570 NS_IMETHODIMP nsViewManager::RemoveChild(nsIView *aChild)
1572 nsView* child = static_cast<nsView*>(aChild);
1573 NS_ENSURE_ARG_POINTER(child);
1575 nsView* parent = child->GetParent();
1577 if (nsnull != parent)
1579 UpdateView(child, NS_VMREFRESH_NO_SYNC);
1580 parent->RemoveChild(child);
1583 return NS_OK;
1586 NS_IMETHODIMP nsViewManager::MoveViewBy(nsIView *aView, nscoord aX, nscoord aY)
1588 nsView* view = static_cast<nsView*>(aView);
1590 nsPoint pt = view->GetPosition();
1591 MoveViewTo(view, aX + pt.x, aY + pt.y);
1592 return NS_OK;
1595 NS_IMETHODIMP nsViewManager::MoveViewTo(nsIView *aView, nscoord aX, nscoord aY)
1597 nsView* view = static_cast<nsView*>(aView);
1598 nsPoint oldPt = view->GetPosition();
1599 nsRect oldArea = view->GetBounds();
1600 view->SetPosition(aX, aY);
1602 // only do damage control if the view is visible
1604 if ((aX != oldPt.x) || (aY != oldPt.y)) {
1605 if (view->GetVisibility() != nsViewVisibility_kHide) {
1606 nsView* parentView = view->GetParent();
1607 UpdateView(parentView, oldArea, NS_VMREFRESH_NO_SYNC);
1608 UpdateView(parentView, view->GetBounds(), NS_VMREFRESH_NO_SYNC);
1611 return NS_OK;
1614 void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
1615 PRUint32 aUpdateFlags, nscoord aY1, nscoord aY2, PRBool aInCutOut) {
1616 nscoord height = aY2 - aY1;
1617 if (aRect.x < aCutOut.x) {
1618 nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height);
1619 UpdateView(aView, r, aUpdateFlags);
1621 if (!aInCutOut && aCutOut.x < aCutOut.XMost()) {
1622 nsRect r(aCutOut.x, aY1, aCutOut.width, height);
1623 UpdateView(aView, r, aUpdateFlags);
1625 if (aCutOut.XMost() < aRect.XMost()) {
1626 nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height);
1627 UpdateView(aView, r, aUpdateFlags);
1631 void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
1632 PRUint32 aUpdateFlags) {
1633 if (aRect.y < aCutOut.y) {
1634 InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aRect.y, aCutOut.y, PR_FALSE);
1636 if (aCutOut.y < aCutOut.YMost()) {
1637 InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.y, aCutOut.YMost(), PR_TRUE);
1639 if (aCutOut.YMost() < aRect.YMost()) {
1640 InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.YMost(), aRect.YMost(), PR_FALSE);
1644 NS_IMETHODIMP nsViewManager::ResizeView(nsIView *aView, const nsRect &aRect, PRBool aRepaintExposedAreaOnly)
1646 nsView* view = static_cast<nsView*>(aView);
1647 nsRect oldDimensions;
1649 view->GetDimensions(oldDimensions);
1650 if (!oldDimensions.IsExactEqual(aRect)) {
1651 nsView* parentView = view->GetParent();
1652 if (parentView == nsnull)
1653 parentView = view;
1655 // resize the view.
1656 // Prevent Invalidation of hidden views
1657 if (view->GetVisibility() == nsViewVisibility_kHide) {
1658 view->SetDimensions(aRect, PR_FALSE);
1659 } else {
1660 if (!aRepaintExposedAreaOnly) {
1661 //Invalidate the union of the old and new size
1662 view->SetDimensions(aRect, PR_TRUE);
1664 UpdateView(view, aRect, NS_VMREFRESH_NO_SYNC);
1665 view->ConvertToParentCoords(&oldDimensions.x, &oldDimensions.y);
1666 UpdateView(parentView, oldDimensions, NS_VMREFRESH_NO_SYNC);
1667 } else {
1668 view->SetDimensions(aRect, PR_TRUE);
1670 InvalidateRectDifference(view, aRect, oldDimensions, NS_VMREFRESH_NO_SYNC);
1671 nsRect r = aRect;
1672 view->ConvertToParentCoords(&r.x, &r.y);
1673 view->ConvertToParentCoords(&oldDimensions.x, &oldDimensions.y);
1674 InvalidateRectDifference(parentView, oldDimensions, r, NS_VMREFRESH_NO_SYNC);
1679 // Note that if layout resizes the view and the view has a custom clip
1680 // region set, then we expect layout to update the clip region too. Thus
1681 // in the case where mClipRect has been optimized away to just be a null
1682 // pointer, and this resize is implicitly changing the clip rect, it's OK
1683 // because layout will change it back again if necessary.
1685 return NS_OK;
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 PR_TRUE;
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 if (aView->HasWidget()) {
1768 aView->GetWidget()->Update();
1771 for (nsView* childView = aView->GetFirstChild();
1772 childView;
1773 childView = childView->GetNextSibling()) {
1774 UpdateWidgetsForView(childView);
1778 PRBool nsViewManager::IsViewInserted(nsView *aView)
1780 if (mRootView == aView) {
1781 return PR_TRUE;
1782 } else if (aView->GetParent() == nsnull) {
1783 return PR_FALSE;
1784 } else {
1785 nsView* view = aView->GetParent()->GetFirstChild();
1786 while (view != nsnull) {
1787 if (view == aView) {
1788 return PR_TRUE;
1790 view = view->GetNextSibling();
1792 return PR_FALSE;
1796 NS_IMETHODIMP nsViewManager::SetViewZIndex(nsIView *aView, PRBool aAutoZIndex, PRInt32 aZIndex, PRBool aTopMost)
1798 nsView* view = static_cast<nsView*>(aView);
1799 nsresult rv = NS_OK;
1801 NS_ASSERTION((view != nsnull), "no view");
1803 // don't allow the root view's z-index to be changed. It should always be zero.
1804 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1805 if (aView == mRootView) {
1806 return rv;
1809 PRBool oldTopMost = view->IsTopMost();
1810 PRBool oldIsAuto = view->GetZIndexIsAuto();
1812 if (aAutoZIndex) {
1813 aZIndex = 0;
1816 PRInt32 oldidx = view->GetZIndex();
1817 view->SetZIndex(aAutoZIndex, aZIndex, aTopMost);
1819 if (oldidx != aZIndex || oldTopMost != aTopMost ||
1820 oldIsAuto != aAutoZIndex) {
1821 UpdateView(view, NS_VMREFRESH_NO_SYNC);
1824 return rv;
1827 NS_IMETHODIMP nsViewManager::SetViewObserver(nsIViewObserver *aObserver)
1829 mObserver = aObserver;
1830 return NS_OK;
1833 NS_IMETHODIMP nsViewManager::GetViewObserver(nsIViewObserver *&aObserver)
1835 if (nsnull != mObserver) {
1836 aObserver = mObserver;
1837 NS_ADDREF(mObserver);
1838 return NS_OK;
1839 } else
1840 return NS_ERROR_NO_INTERFACE;
1843 NS_IMETHODIMP nsViewManager::GetDeviceContext(nsIDeviceContext *&aContext)
1845 NS_IF_ADDREF(mContext);
1846 aContext = mContext;
1847 return NS_OK;
1850 already_AddRefed<nsIRenderingContext>
1851 nsViewManager::CreateRenderingContext(nsView &aView)
1853 nsView *par = &aView;
1854 nsIWidget* win;
1855 nsIRenderingContext *cx = nsnull;
1856 nscoord ax = 0, ay = 0;
1860 win = par->GetWidget();
1861 if (win)
1862 break;
1864 //get absolute coordinates of view, but don't
1865 //add in view pos since the first thing you ever
1866 //need to do when painting a view is to translate
1867 //the rendering context by the views pos and other parts
1868 //of the code do this for us...
1870 if (par != &aView)
1872 par->ConvertToParentCoords(&ax, &ay);
1875 par = par->GetParent();
1877 while (nsnull != par);
1879 if (nsnull != win)
1881 // XXXkt this has an origin at top-left of win ...
1882 mContext->CreateRenderingContext(par, cx);
1884 // XXXkt ... but the translation is between the origins of views
1885 NS_ASSERTION(aView.ViewToWidgetOffset()
1886 - aView.GetDimensions().TopLeft() ==
1887 par->ViewToWidgetOffset()
1888 - par->GetDimensions().TopLeft(),
1889 "ViewToWidgetOffset not handled!");
1890 if (nsnull != cx)
1891 cx->Translate(ax, ay);
1894 return cx;
1897 NS_IMETHODIMP nsViewManager::DisableRefresh(void)
1899 if (!IsRootVM()) {
1900 return RootViewManager()->DisableRefresh();
1903 if (mUpdateBatchCnt > 0)
1904 return NS_OK;
1906 mRefreshEnabled = PR_FALSE;
1907 return NS_OK;
1910 NS_IMETHODIMP nsViewManager::EnableRefresh(PRUint32 aUpdateFlags)
1912 if (!IsRootVM()) {
1913 return RootViewManager()->EnableRefresh(aUpdateFlags);
1916 if (mUpdateBatchCnt > 0)
1917 return NS_OK;
1919 mRefreshEnabled = PR_TRUE;
1921 if (!mHasPendingUpdates) {
1922 // Nothing to do
1923 return NS_OK;
1926 // nested batching can combine IMMEDIATE with DEFERRED. Favour
1927 // IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC.
1928 if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
1929 FlushPendingInvalidates();
1930 Composite();
1931 } else if (aUpdateFlags & NS_VMREFRESH_DEFERRED) {
1932 PostInvalidateEvent();
1933 } else { // NO_SYNC
1934 FlushPendingInvalidates();
1937 return NS_OK;
1940 nsIViewManager* nsViewManager::BeginUpdateViewBatch(void)
1942 if (!IsRootVM()) {
1943 return RootViewManager()->BeginUpdateViewBatch();
1946 nsresult result = NS_OK;
1948 if (mUpdateBatchCnt == 0) {
1949 mUpdateBatchFlags = 0;
1950 result = DisableRefresh();
1953 if (NS_SUCCEEDED(result))
1954 ++mUpdateBatchCnt;
1956 return this;
1959 NS_IMETHODIMP nsViewManager::EndUpdateViewBatch(PRUint32 aUpdateFlags)
1961 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1963 nsresult result = NS_OK;
1965 --mUpdateBatchCnt;
1967 NS_ASSERTION(mUpdateBatchCnt >= 0, "Invalid batch count!");
1969 if (mUpdateBatchCnt < 0)
1971 mUpdateBatchCnt = 0;
1972 return NS_ERROR_FAILURE;
1975 mUpdateBatchFlags |= aUpdateFlags;
1976 if (mUpdateBatchCnt == 0) {
1977 result = EnableRefresh(mUpdateBatchFlags);
1980 return result;
1983 NS_IMETHODIMP nsViewManager::SetRootScrollableView(nsIScrollableView *aScrollable)
1985 mRootScrollable = aScrollable;
1986 return NS_OK;
1989 NS_IMETHODIMP nsViewManager::GetRootScrollableView(nsIScrollableView **aScrollable)
1991 *aScrollable = mRootScrollable;
1992 return NS_OK;
1995 NS_IMETHODIMP nsViewManager::GetWidget(nsIWidget **aWidget)
1997 *aWidget = GetWidget();
1998 NS_IF_ADDREF(*aWidget);
1999 return NS_OK;
2002 NS_IMETHODIMP nsViewManager::ForceUpdate()
2004 if (!IsRootVM()) {
2005 return RootViewManager()->ForceUpdate();
2008 // Walk the view tree looking for widgets, and call Update() on each one
2009 if (mRootView) {
2010 UpdateWidgetsForView(mRootView);
2013 return NS_OK;
2016 nsPoint nsViewManager::ComputeViewOffset(const nsView *aView)
2018 NS_PRECONDITION(aView, "Null view in ComputeViewOffset?");
2020 nsPoint origin(0, 0);
2021 #ifdef DEBUG
2022 const nsView* rootView;
2023 const nsView* origView = aView;
2024 #endif
2026 while (aView) {
2027 #ifdef DEBUG
2028 rootView = aView;
2029 #endif
2030 origin += aView->GetPosition();
2031 aView = aView->GetParent();
2033 NS_ASSERTION(rootView ==
2034 origView->GetViewManager()->RootViewManager()->GetRootView(),
2035 "Unexpected root view");
2036 return origin;
2039 void nsViewManager::ViewToWidget(nsView *aView, nsView* aWidgetView, nsRect &aRect) const
2041 while (aView != aWidgetView) {
2042 aView->ConvertToParentCoords(&aRect.x, &aRect.y);
2043 aView = aView->GetParent();
2046 // intersect aRect with bounds of aWidgetView, to prevent generating any illegal rectangles.
2047 nsRect bounds;
2048 aWidgetView->GetDimensions(bounds);
2049 aRect.IntersectRect(aRect, bounds);
2050 // account for the view's origin not lining up with the widget's
2051 aRect.x -= bounds.x;
2052 aRect.y -= bounds.y;
2054 aRect += aView->ViewToWidgetOffset();
2056 // finally, convert to device coordinates.
2057 aRect.ScaleRoundOut(1.0f / mContext->AppUnitsPerDevPixel());
2060 nsresult nsViewManager::GetVisibleRect(nsRect& aVisibleRect)
2062 nsresult rv = NS_OK;
2064 // Get the viewport scroller
2065 nsIScrollableView* scrollingView;
2066 GetRootScrollableView(&scrollingView);
2068 if (scrollingView) {
2069 // Determine the visible rect in the scrolled view's coordinate space.
2070 // The size of the visible area is the clip view size
2071 nsScrollPortView* clipView = static_cast<nsScrollPortView*>(scrollingView);
2072 clipView->GetDimensions(aVisibleRect);
2074 scrollingView->GetScrollPosition(aVisibleRect.x, aVisibleRect.y);
2075 } else {
2076 rv = NS_ERROR_FAILURE;
2079 return rv;
2082 nsresult nsViewManager::GetAbsoluteRect(nsView *aView, const nsRect &aRect,
2083 nsRect& aAbsRect)
2085 nsIScrollableView* scrollingView = nsnull;
2086 GetRootScrollableView(&scrollingView);
2087 if (nsnull == scrollingView) {
2088 return NS_ERROR_FAILURE;
2091 nsIView* scrolledIView = nsnull;
2092 scrollingView->GetScrolledView(scrolledIView);
2094 nsView* scrolledView = static_cast<nsView*>(scrolledIView);
2096 // Calculate the absolute coordinates of the aRect passed in.
2097 // aRects values are relative to aView
2098 aAbsRect = aRect;
2099 nsView *parentView = aView;
2100 while ((parentView != nsnull) && (parentView != scrolledView)) {
2101 parentView->ConvertToParentCoords(&aAbsRect.x, &aAbsRect.y);
2102 parentView = parentView->GetParent();
2105 if (parentView != scrolledView) {
2106 return NS_ERROR_FAILURE;
2109 return NS_OK;
2113 NS_IMETHODIMP nsViewManager::GetRectVisibility(nsIView *aView,
2114 const nsRect &aRect,
2115 PRUint16 aMinTwips,
2116 nsRectVisibility *aRectVisibility)
2118 nsView* view = static_cast<nsView*>(aView);
2120 // The parameter aMinTwips determines how many rows/cols of pixels must be visible on each side of the element,
2121 // in order to be counted as visible
2123 *aRectVisibility = nsRectVisibility_kZeroAreaRect;
2124 if (aRect.width == 0 || aRect.height == 0) {
2125 return NS_OK;
2128 // is this view even visible?
2129 if (view->GetVisibility() == nsViewVisibility_kHide) {
2130 return NS_OK;
2133 // nsViewManager::InsertChild ensures that descendants of floating views
2134 // are also marked floating.
2135 if (view->GetFloating()) {
2136 *aRectVisibility = nsRectVisibility_kVisible;
2137 return NS_OK;
2140 // Calculate the absolute coordinates for the visible rectangle
2141 nsRect visibleRect;
2142 if (GetVisibleRect(visibleRect) == NS_ERROR_FAILURE) {
2143 *aRectVisibility = nsRectVisibility_kVisible;
2144 return NS_OK;
2147 // Calculate the absolute coordinates of the aRect passed in.
2148 // aRects values are relative to aView
2149 nsRect absRect;
2150 if ((GetAbsoluteRect(view, aRect, absRect)) == NS_ERROR_FAILURE) {
2151 *aRectVisibility = nsRectVisibility_kVisible;
2152 return NS_OK;
2156 * If aMinTwips > 0, ensure at least aMinTwips of space around object is visible
2157 * The object is not visible if:
2158 * ((objectTop < windowTop && objectBottom < windowTop) ||
2159 * (objectBottom > windowBottom && objectTop > windowBottom) ||
2160 * (objectLeft < windowLeft && objectRight < windowLeft) ||
2161 * (objectRight > windowRight && objectLeft > windowRight))
2164 if (absRect.y < visibleRect.y &&
2165 absRect.y + absRect.height < visibleRect.y + aMinTwips)
2166 *aRectVisibility = nsRectVisibility_kAboveViewport;
2167 else if (absRect.y + absRect.height > visibleRect.y + visibleRect.height &&
2168 absRect.y > visibleRect.y + visibleRect.height - aMinTwips)
2169 *aRectVisibility = nsRectVisibility_kBelowViewport;
2170 else if (absRect.x < visibleRect.x &&
2171 absRect.x + absRect.width < visibleRect.x + aMinTwips)
2172 *aRectVisibility = nsRectVisibility_kLeftOfViewport;
2173 else if (absRect.x + absRect.width > visibleRect.x + visibleRect.width &&
2174 absRect.x > visibleRect.x + visibleRect.width - aMinTwips)
2175 *aRectVisibility = nsRectVisibility_kRightOfViewport;
2176 else
2177 *aRectVisibility = nsRectVisibility_kVisible;
2179 return NS_OK;
2182 NS_IMETHODIMP
2183 nsViewManager::IsPainting(PRBool& aIsPainting)
2185 aIsPainting = IsPainting();
2186 return NS_OK;
2189 void
2190 nsViewManager::FlushPendingInvalidates()
2192 NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!\n");
2193 NS_ASSERTION(mUpdateBatchCnt == 0, "Must not be in an update batch!");
2194 // XXXbz this is probably not quite OK yet, if callers can explicitly
2195 // DisableRefresh while we have an event posted.
2196 // NS_ASSERTION(mRefreshEnabled, "How did we get here?");
2198 // Let all the view observers of all viewmanagers in this tree know that
2199 // we're about to "paint" (this lets them get in their invalidates now so
2200 // we don't go through two invalidate-processing cycles).
2201 NS_ASSERTION(gViewManagers, "Better have a viewmanagers array!");
2203 // Make sure to not send WillPaint notifications while scrolling
2204 if (mScrollCnt == 0) {
2205 // Disable refresh while we notify our view observers, so that if they do
2206 // view update batches we don't reenter this code and so that we batch
2207 // all of them together. We don't use
2208 // BeginUpdateViewBatch/EndUpdateViewBatch, since that would reenter this
2209 // exact code, but we want the effect of a single big update batch.
2210 PRBool refreshEnabled = mRefreshEnabled;
2211 mRefreshEnabled = PR_FALSE;
2212 ++mUpdateBatchCnt;
2214 PRInt32 index;
2215 for (index = 0; index < mVMCount; index++) {
2216 nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index);
2217 if (vm->RootViewManager() == this) {
2218 // One of our kids
2219 nsIViewObserver* observer = vm->GetViewObserver();
2220 if (observer) {
2221 observer->WillPaint();
2222 NS_ASSERTION(mUpdateBatchCnt == 1,
2223 "Observer did not end view batch?");
2228 --mUpdateBatchCnt;
2229 // Someone could have called EnableRefresh on us from inside WillPaint().
2230 // Only reset the old mRefreshEnabled value if the current value is false.
2231 if (!mRefreshEnabled) {
2232 mRefreshEnabled = refreshEnabled;
2236 if (mHasPendingUpdates) {
2237 ProcessPendingUpdates(mRootView, PR_TRUE);
2238 mHasPendingUpdates = PR_FALSE;
2242 void
2243 nsViewManager::ProcessInvalidateEvent()
2245 NS_ASSERTION(IsRootVM(),
2246 "Incorrectly targeted invalidate event");
2247 // If we're in the middle of an update batch, just repost the event,
2248 // to be processed when the batch ends.
2249 PRBool processEvent = (mUpdateBatchCnt == 0);
2250 if (processEvent) {
2251 FlushPendingInvalidates();
2253 mInvalidateEvent.Forget();
2254 if (!processEvent) {
2255 // We didn't actually process this event... post a new one
2256 PostInvalidateEvent();
2260 NS_IMETHODIMP
2261 nsViewManager::SetDefaultBackgroundColor(nscolor aColor)
2263 mDefaultBackgroundColor = aColor;
2264 return NS_OK;
2267 NS_IMETHODIMP
2268 nsViewManager::GetDefaultBackgroundColor(nscolor* aColor)
2270 *aColor = mDefaultBackgroundColor;
2271 return NS_OK;
2275 NS_IMETHODIMP
2276 nsViewManager::GetLastUserEventTime(PRUint32& aTime)
2278 aTime = gLastUserEventTime;
2279 return NS_OK;
2282 class nsSynthMouseMoveEvent : public nsViewManagerEvent {
2283 public:
2284 nsSynthMouseMoveEvent(nsViewManager *aViewManager,
2285 PRBool aFromScroll)
2286 : nsViewManagerEvent(aViewManager),
2287 mFromScroll(aFromScroll) {
2290 NS_IMETHOD Run() {
2291 if (mViewManager)
2292 mViewManager->ProcessSynthMouseMoveEvent(mFromScroll);
2293 return NS_OK;
2296 private:
2297 PRBool mFromScroll;
2300 NS_IMETHODIMP
2301 nsViewManager::SynthesizeMouseMove(PRBool aFromScroll)
2303 if (!IsRootVM())
2304 return RootViewManager()->SynthesizeMouseMove(aFromScroll);
2306 if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE))
2307 return NS_OK;
2309 if (!mSynthMouseMoveEvent.IsPending()) {
2310 nsRefPtr<nsViewManagerEvent> ev =
2311 new nsSynthMouseMoveEvent(this, aFromScroll);
2313 if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
2314 NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
2315 return NS_ERROR_UNEXPECTED;
2318 mSynthMouseMoveEvent = ev;
2321 return NS_OK;
2325 * Find the first floating view with a widget in a postorder traversal of the
2326 * view tree that contains the point. Thus more deeply nested floating views
2327 * are preferred over their ancestors, and floating views earlier in the
2328 * view hierarchy (i.e., added later) are preferred over their siblings.
2329 * This is adequate for finding the "topmost" floating view under a point,
2330 * given that floating views don't supporting having a specific z-index.
2332 * We cannot exit early when aPt is outside the view bounds, because floating
2333 * views aren't necessarily included in their parent's bounds, so this could
2334 * traverse the entire view hierarchy --- use carefully.
2336 static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt)
2338 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
2339 nsView* r = FindFloatingViewContaining(v, aPt - v->GetOffsetTo(aView));
2340 if (r)
2341 return r;
2344 if (aView->GetFloating() && aView->HasWidget() &&
2345 aView->GetDimensions().Contains(aPt) && IsViewVisible(aView))
2346 return aView;
2348 return nsnull;
2351 void
2352 nsViewManager::ProcessSynthMouseMoveEvent(PRBool aFromScroll)
2354 // allow new event to be posted while handling this one only if the
2355 // source of the event is a scroll (to prevent infinite reflow loops)
2356 if (aFromScroll)
2357 mSynthMouseMoveEvent.Forget();
2359 NS_ASSERTION(IsRootVM(), "Only the root view manager should be here");
2361 if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE) || !mRootView) {
2362 mSynthMouseMoveEvent.Forget();
2363 return;
2366 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
2367 // we need to access members after we call DispatchEvent).
2368 nsCOMPtr<nsIViewManager> kungFuDeathGrip(this);
2370 #ifdef DEBUG_MOUSE_LOCATION
2371 printf("[vm=%p]synthesizing mouse move to (%d,%d)\n",
2372 this, mMouseLocation.x, mMouseLocation.y);
2373 #endif
2375 nsPoint pt = mMouseLocation;
2376 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
2377 pt.x = NSIntPixelsToAppUnits(mMouseLocation.x, p2a);
2378 pt.y = NSIntPixelsToAppUnits(mMouseLocation.y, p2a);
2379 // This could be a bit slow (traverses entire view hierarchy)
2380 // but it's OK to do it once per synthetic mouse event
2381 nsView* view = FindFloatingViewContaining(mRootView, pt);
2382 nsPoint offset(0, 0);
2383 if (!view) {
2384 view = mRootView;
2385 } else {
2386 offset = view->GetOffsetTo(mRootView);
2387 offset.x = NSAppUnitsToIntPixels(offset.x, p2a);
2388 offset.y = NSAppUnitsToIntPixels(offset.y, p2a);
2390 nsMouseEvent event(PR_TRUE, NS_MOUSE_MOVE, view->GetWidget(),
2391 nsMouseEvent::eSynthesized);
2392 event.refPoint = mMouseLocation - offset;
2393 event.time = PR_IntervalNow();
2394 // XXX set event.isShift, event.isControl, event.isAlt, event.isMeta ?
2396 nsEventStatus status;
2397 view->GetViewManager()->DispatchEvent(&event, &status);
2399 if (!aFromScroll)
2400 mSynthMouseMoveEvent.Forget();
2403 void
2404 nsViewManager::InvalidateHierarchy()
2406 if (mRootView) {
2407 if (!IsRootVM()) {
2408 NS_RELEASE(mRootViewManager);
2410 nsView *parent = mRootView->GetParent();
2411 if (parent) {
2412 mRootViewManager = parent->GetViewManager()->RootViewManager();
2413 NS_ADDREF(mRootViewManager);
2414 NS_ASSERTION(mRootViewManager != this,
2415 "Root view had a parent, but it has the same view manager");
2416 } else {
2417 mRootViewManager = this;