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