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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
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>
27 : *
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.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
43 : #include "plarena.h"
44 :
45 : #include "nsAutoPtr.h"
46 : #include "nsViewManager.h"
47 : #include "nsGfxCIID.h"
48 : #include "nsView.h"
49 : #include "nsISupportsArray.h"
50 : #include "nsCOMPtr.h"
51 : #include "nsGUIEvent.h"
52 : #include "nsRegion.h"
53 : #include "nsHashtable.h"
54 : #include "nsCOMArray.h"
55 : #include "nsThreadUtils.h"
56 : #include "nsContentUtils.h"
57 : #include "nsIPluginWidget.h"
58 : #include "nsXULPopupManager.h"
59 : #include "nsIPresShell.h"
60 : #include "nsPresContext.h"
61 : #include "nsEventStateManager.h"
62 : #include "mozilla/StartupTimeline.h"
63 : #include "sampler.h"
64 :
65 : /**
66 : XXX TODO XXX
67 :
68 : DeCOMify newly private methods
69 : Optimize view storage
70 : */
71 :
72 : /**
73 : A note about platform assumptions:
74 :
75 : We assume that a widget is z-ordered on top of its parent.
76 :
77 : We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
78 : we ask for a specific z-order, we don't assume that widget z-ordering actually works.
79 : */
80 :
81 : #define NSCOORD_NONE PR_INT32_MIN
82 :
83 : #undef DEBUG_MOUSE_LOCATION
84 :
85 : PRInt32 nsViewManager::mVMCount = 0;
86 :
87 : // Weakly held references to all of the view managers
88 : nsVoidArray* nsViewManager::gViewManagers = nsnull;
89 : PRUint32 nsViewManager::gLastUserEventTime = 0;
90 :
91 0 : nsViewManager::nsViewManager()
92 : : mDelayedResize(NSCOORD_NONE, NSCOORD_NONE)
93 0 : , mRootViewManager(this)
94 : {
95 0 : if (gViewManagers == nsnull) {
96 0 : NS_ASSERTION(mVMCount == 0, "View Manager count is incorrect");
97 : // Create an array to hold a list of view managers
98 0 : gViewManagers = new nsVoidArray;
99 : }
100 :
101 0 : gViewManagers->AppendElement(this);
102 :
103 0 : ++mVMCount;
104 :
105 : // NOTE: we use a zeroing operator new, so all data members are
106 : // assumed to be cleared here.
107 0 : mHasPendingUpdates = false;
108 0 : mHasPendingWidgetGeometryChanges = false;
109 0 : mRecursiveRefreshPending = false;
110 0 : }
111 :
112 0 : nsViewManager::~nsViewManager()
113 : {
114 0 : if (mRootView) {
115 : // Destroy any remaining views
116 0 : mRootView->Destroy();
117 0 : mRootView = nsnull;
118 : }
119 :
120 0 : if (!IsRootVM()) {
121 : // We have a strong ref to mRootViewManager
122 0 : NS_RELEASE(mRootViewManager);
123 : }
124 :
125 0 : NS_ASSERTION((mVMCount > 0), "underflow of viewmanagers");
126 0 : --mVMCount;
127 :
128 : #ifdef DEBUG
129 : bool removed =
130 : #endif
131 0 : gViewManagers->RemoveElement(this);
132 0 : NS_ASSERTION(removed, "Viewmanager instance not was not in the global list of viewmanagers");
133 :
134 0 : if (0 == mVMCount) {
135 : // There aren't any more view managers so
136 : // release the global array of view managers
137 :
138 0 : NS_ASSERTION(gViewManagers != nsnull, "About to delete null gViewManagers");
139 0 : delete gViewManagers;
140 0 : gViewManagers = nsnull;
141 : }
142 :
143 0 : mPresShell = nsnull;
144 0 : }
145 :
146 0 : NS_IMPL_ISUPPORTS1(nsViewManager, nsIViewManager)
147 :
148 : // We don't hold a reference to the presentation context because it
149 : // holds a reference to us.
150 0 : NS_IMETHODIMP nsViewManager::Init(nsDeviceContext* aContext)
151 : {
152 0 : NS_PRECONDITION(nsnull != aContext, "null ptr");
153 :
154 0 : if (nsnull == aContext) {
155 0 : return NS_ERROR_NULL_POINTER;
156 : }
157 0 : if (nsnull != mContext) {
158 0 : return NS_ERROR_ALREADY_INITIALIZED;
159 : }
160 0 : mContext = aContext;
161 :
162 0 : return NS_OK;
163 : }
164 :
165 : NS_IMETHODIMP_(nsIView *)
166 0 : nsViewManager::CreateView(const nsRect& aBounds,
167 : const nsIView* aParent,
168 : nsViewVisibility aVisibilityFlag)
169 : {
170 0 : nsView *v = new nsView(this, aVisibilityFlag);
171 0 : if (v) {
172 0 : v->SetParent(static_cast<nsView*>(const_cast<nsIView*>(aParent)));
173 0 : v->SetPosition(aBounds.x, aBounds.y);
174 0 : nsRect dim(0, 0, aBounds.width, aBounds.height);
175 0 : v->SetDimensions(dim, false);
176 : }
177 0 : return v;
178 : }
179 :
180 : NS_IMETHODIMP_(nsIView*)
181 0 : nsViewManager::GetRootView()
182 : {
183 0 : return mRootView;
184 : }
185 :
186 0 : NS_IMETHODIMP nsViewManager::SetRootView(nsIView *aView)
187 : {
188 0 : nsView* view = static_cast<nsView*>(aView);
189 :
190 0 : NS_PRECONDITION(!view || view->GetViewManager() == this,
191 : "Unexpected viewmanager on root view");
192 :
193 : // Do NOT destroy the current root view. It's the caller's responsibility
194 : // to destroy it
195 0 : mRootView = view;
196 :
197 0 : if (mRootView) {
198 0 : nsView* parent = mRootView->GetParent();
199 0 : if (parent) {
200 : // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
201 : // no need to set mRootViewManager ourselves here.
202 0 : parent->InsertChild(mRootView, nsnull);
203 : } else {
204 0 : InvalidateHierarchy();
205 : }
206 :
207 0 : mRootView->SetZIndex(false, 0, false);
208 : }
209 : // Else don't touch mRootViewManager
210 :
211 0 : return NS_OK;
212 : }
213 :
214 0 : NS_IMETHODIMP nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
215 : {
216 0 : if (nsnull != mRootView) {
217 0 : if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
218 0 : nsRect dim = mRootView->GetDimensions();
219 0 : *aWidth = dim.width;
220 0 : *aHeight = dim.height;
221 : } else {
222 0 : *aWidth = mDelayedResize.width;
223 0 : *aHeight = mDelayedResize.height;
224 : }
225 : }
226 : else
227 : {
228 0 : *aWidth = 0;
229 0 : *aHeight = 0;
230 : }
231 0 : return NS_OK;
232 : }
233 :
234 0 : void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
235 : {
236 0 : nsRect oldDim = mRootView->GetDimensions();
237 0 : nsRect newDim(0, 0, aWidth, aHeight);
238 : // We care about resizes even when one dimension is already zero.
239 0 : if (!oldDim.IsEqualEdges(newDim)) {
240 : // Don't resize the widget. It is already being set elsewhere.
241 0 : mRootView->SetDimensions(newDim, true, false);
242 0 : if (mPresShell)
243 0 : mPresShell->ResizeReflow(aWidth, aHeight);
244 : }
245 0 : }
246 :
247 0 : NS_IMETHODIMP nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
248 : {
249 0 : if (mRootView) {
250 0 : if (mRootView->IsEffectivelyVisible() && mPresShell && mPresShell->IsVisible()) {
251 0 : if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
252 0 : mDelayedResize != nsSize(aWidth, aHeight)) {
253 : // We have a delayed resize; that now obsolete size may already have
254 : // been flushed to the PresContext so we need to update the PresContext
255 : // with the new size because if the new size is exactly the same as the
256 : // root view's current size then DoSetWindowDimensions will not
257 : // request a resize reflow (which would correct it). See bug 617076.
258 0 : mDelayedResize = nsSize(aWidth, aHeight);
259 0 : FlushDelayedResize(false);
260 : }
261 0 : mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
262 0 : DoSetWindowDimensions(aWidth, aHeight);
263 : } else {
264 0 : mDelayedResize.SizeTo(aWidth, aHeight);
265 0 : if (mPresShell && mPresShell->GetDocument()) {
266 0 : mPresShell->GetDocument()->SetNeedStyleFlush();
267 : }
268 : }
269 : }
270 :
271 0 : return NS_OK;
272 : }
273 :
274 0 : NS_IMETHODIMP nsViewManager::FlushDelayedResize(bool aDoReflow)
275 : {
276 0 : if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
277 0 : if (aDoReflow) {
278 0 : DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height);
279 0 : mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
280 0 : } else if (mPresShell) {
281 0 : nsPresContext* presContext = mPresShell->GetPresContext();
282 0 : if (presContext) {
283 0 : presContext->SetVisibleArea(nsRect(nsPoint(0, 0), mDelayedResize));
284 : }
285 : }
286 : }
287 0 : return NS_OK;
288 : }
289 :
290 : // Convert aIn from being relative to and in appunits of aFromView, to being
291 : // relative to and in appunits of aToView.
292 0 : static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
293 : nsView* aFromView,
294 : nsView* aToView)
295 : {
296 0 : nsRegion out = aIn;
297 0 : out.MoveBy(aFromView->GetOffsetTo(aToView));
298 : out = out.ConvertAppUnitsRoundOut(
299 0 : aFromView->GetViewManager()->AppUnitsPerDevPixel(),
300 0 : aToView->GetViewManager()->AppUnitsPerDevPixel());
301 : return out;
302 : }
303 :
304 0 : nsIView* nsIViewManager::GetDisplayRootFor(nsIView* aView)
305 : {
306 0 : nsIView *displayRoot = aView;
307 0 : for (;;) {
308 0 : nsIView *displayParent = displayRoot->GetParent();
309 0 : if (!displayParent)
310 0 : return displayRoot;
311 :
312 0 : if (displayRoot->GetFloating() && !displayParent->GetFloating())
313 0 : return displayRoot;
314 :
315 : // If we have a combobox dropdown popup within a panel popup, both the view
316 : // for the dropdown popup and its parent will be floating, so we need to
317 : // distinguish this situation. We do this by looking for a widget. Any view
318 : // with a widget is a display root, except for plugins.
319 0 : nsIWidget* widget = displayRoot->GetWidget();
320 0 : if (widget) {
321 : nsWindowType type;
322 0 : widget->GetWindowType(type);
323 0 : if (type == eWindowType_popup) {
324 0 : NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
325 : "this should only happen with floating views that have floating parents");
326 0 : return displayRoot;
327 : }
328 : }
329 :
330 0 : displayRoot = displayParent;
331 : }
332 : }
333 :
334 : /**
335 : aRegion is given in device coordinates!!
336 : aContext may be null, in which case layers should be used for
337 : rendering.
338 : */
339 0 : void nsViewManager::Refresh(nsView *aView, nsIWidget *aWidget,
340 : const nsIntRegion& aRegion,
341 : bool aWillSendDidPaint)
342 : {
343 0 : NS_ASSERTION(aView == nsView::GetViewFor(aWidget), "view widget mismatch");
344 0 : NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
345 :
346 : // damageRegion is the damaged area, in twips, relative to the view origin
347 0 : nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
348 : // move region from widget coordinates into view coordinates
349 0 : damageRegion.MoveBy(-aView->ViewToWidgetOffset());
350 :
351 0 : if (damageRegion.IsEmpty()) {
352 : #ifdef DEBUG_roc
353 : nsRect viewRect = aView->GetDimensions();
354 : nsRect damageRect = damageRegion.GetBounds();
355 : printf("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
356 : damageRect.x, damageRect.y, damageRect.width, damageRect.height,
357 : viewRect.x, viewRect.y, viewRect.width, viewRect.height);
358 : #endif
359 : return;
360 : }
361 :
362 0 : NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
363 0 : if (IsPainting()) {
364 0 : RootViewManager()->mRecursiveRefreshPending = true;
365 : return;
366 : }
367 :
368 : {
369 0 : nsAutoScriptBlocker scriptBlocker;
370 0 : SetPainting(true);
371 :
372 0 : NS_ASSERTION(GetDisplayRootFor(aView) == aView,
373 : "Widgets that we paint must all be display roots");
374 :
375 0 : if (mPresShell) {
376 : mPresShell->Paint(aView, aWidget, damageRegion, aRegion,
377 0 : aWillSendDidPaint);
378 0 : mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
379 : }
380 :
381 0 : SetPainting(false);
382 : }
383 :
384 0 : if (RootViewManager()->mRecursiveRefreshPending) {
385 0 : RootViewManager()->mRecursiveRefreshPending = false;
386 0 : InvalidateAllViews();
387 : }
388 : }
389 :
390 0 : void nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
391 : bool aFlushDirtyRegion)
392 : {
393 0 : NS_ASSERTION(IsRootVM(), "Updates will be missed");
394 :
395 : // Protect against a null-view.
396 0 : if (!aView) {
397 0 : return;
398 : }
399 :
400 0 : if (aView->HasWidget()) {
401 0 : aView->ResetWidgetBounds(false, true);
402 : }
403 :
404 : // process pending updates in child view.
405 0 : for (nsView* childView = aView->GetFirstChild(); childView;
406 : childView = childView->GetNextSibling()) {
407 0 : ProcessPendingUpdatesForView(childView, aFlushDirtyRegion);
408 : }
409 :
410 : // Push out updates after we've processed the children; ensures that
411 : // damage is applied based on the final widget geometry
412 0 : if (aFlushDirtyRegion) {
413 0 : FlushDirtyRegionToWidget(aView);
414 : }
415 : }
416 :
417 0 : void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
418 : {
419 0 : if (!aView->HasNonEmptyDirtyRegion())
420 0 : return;
421 :
422 0 : nsRegion* dirtyRegion = aView->GetDirtyRegion();
423 0 : nsView* nearestViewWithWidget = aView;
424 0 : while (!nearestViewWithWidget->HasWidget() &&
425 0 : nearestViewWithWidget->GetParent()) {
426 0 : nearestViewWithWidget = nearestViewWithWidget->GetParent();
427 : }
428 : nsRegion r =
429 0 : ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
430 0 : nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
431 0 : widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
432 0 : dirtyRegion->SetEmpty();
433 : }
434 :
435 0 : NS_IMETHODIMP nsViewManager::InvalidateView(nsIView *aView)
436 : {
437 : // Mark the entire view as damaged
438 0 : return InvalidateView(aView, aView->GetDimensions());
439 : }
440 :
441 : static void
442 0 : AddDirtyRegion(nsView *aView, const nsRegion &aDamagedRegion)
443 : {
444 0 : nsRegion* dirtyRegion = aView->GetDirtyRegion();
445 0 : if (!dirtyRegion)
446 0 : return;
447 :
448 0 : dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
449 0 : dirtyRegion->SimplifyOutward(8);
450 : }
451 :
452 : void
453 0 : nsViewManager::PostPendingUpdate()
454 : {
455 0 : nsViewManager* rootVM = RootViewManager();
456 0 : rootVM->mHasPendingUpdates = true;
457 0 : rootVM->mHasPendingWidgetGeometryChanges = true;
458 0 : if (rootVM->mPresShell) {
459 0 : rootVM->mPresShell->ScheduleViewManagerFlush();
460 : }
461 0 : }
462 :
463 : /**
464 : * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
465 : * every widget child of aWidgetView, plus aWidgetView's own widget
466 : */
467 : void
468 0 : nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
469 : const nsRegion &aDamagedRegion)
470 : {
471 0 : NS_ASSERTION(aWidgetView->GetViewManager() == this,
472 : "InvalidateWidgetArea called on view we don't own");
473 0 : nsIWidget* widget = aWidgetView->GetWidget();
474 :
475 : #if 0
476 : nsRect dbgBounds = aDamagedRegion.GetBounds();
477 : printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
478 : aWidgetView, aWidgetView->IsAttachedToTopLevel(),
479 : widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
480 : #endif
481 :
482 : // If the bounds don't overlap at all, there's nothing to do
483 0 : nsRegion intersection;
484 0 : intersection.And(aWidgetView->GetInvalidationDimensions(), aDamagedRegion);
485 0 : if (intersection.IsEmpty()) {
486 : return;
487 : }
488 :
489 : // If the widget is hidden, it don't cover nothing
490 0 : if (widget) {
491 : bool visible;
492 0 : widget->IsVisible(visible);
493 0 : if (!visible)
494 : return;
495 : }
496 :
497 0 : if (!widget) {
498 : // The root view or a scrolling view might not have a widget
499 : // (for example, during printing). We get here when we scroll
500 : // during printing to show selected options in a listbox, for example.
501 : return;
502 : }
503 :
504 : // Update all child widgets with the damage. In the process,
505 : // accumulate the union of all the child widget areas, or at least
506 : // some subset of that.
507 0 : nsRegion children;
508 0 : if (widget->GetTransparencyMode() != eTransparencyTransparent) {
509 0 : for (nsIWidget* childWidget = widget->GetFirstChild();
510 : childWidget;
511 : childWidget = childWidget->GetNextSibling()) {
512 0 : nsView* view = nsView::GetViewFor(childWidget);
513 0 : NS_ASSERTION(view != aWidgetView, "will recur infinitely");
514 : bool visible;
515 0 : childWidget->IsVisible(visible);
516 : nsWindowType type;
517 0 : childWidget->GetWindowType(type);
518 0 : if (view && visible && type != eWindowType_popup) {
519 0 : NS_ASSERTION(type == eWindowType_plugin,
520 : "Only plugin or popup widgets can be children!");
521 :
522 : // We do not need to invalidate in plugin widgets, but we should
523 : // exclude them from the invalidation region IF we're not on
524 : // Mac. On Mac we need to draw under plugin widgets, because
525 : // plugin widgets are basically invisible
526 : #ifndef XP_MACOSX
527 : // GetBounds should compensate for chrome on a toplevel widget
528 0 : nsIntRect bounds;
529 0 : childWidget->GetBounds(bounds);
530 :
531 0 : nsTArray<nsIntRect> clipRects;
532 0 : childWidget->GetWindowClipRegion(&clipRects);
533 0 : for (PRUint32 i = 0; i < clipRects.Length(); ++i) {
534 0 : nsRect rr = (clipRects[i] + bounds.TopLeft()).
535 0 : ToAppUnits(AppUnitsPerDevPixel());
536 0 : children.Or(children, rr - aWidgetView->ViewToWidgetOffset());
537 0 : children.SimplifyInward(20);
538 : }
539 : #endif
540 : }
541 : }
542 : }
543 :
544 0 : nsRegion leftOver;
545 0 : leftOver.Sub(intersection, children);
546 :
547 0 : if (!leftOver.IsEmpty()) {
548 : const nsRect* r;
549 0 : for (nsRegionRectIterator iter(leftOver); (r = iter.Next());) {
550 0 : nsIntRect bounds = ViewToWidget(aWidgetView, *r);
551 0 : widget->Invalidate(bounds);
552 : }
553 : }
554 : }
555 :
556 : static bool
557 0 : ShouldIgnoreInvalidation(nsViewManager* aVM)
558 : {
559 0 : while (aVM) {
560 0 : nsIPresShell* shell = aVM->GetPresShell();
561 0 : if (!shell || shell->ShouldIgnoreInvalidation()) {
562 0 : return true;
563 : }
564 0 : nsView* view = aVM->GetRootViewImpl()->GetParent();
565 0 : aVM = view ? view->GetViewManager() : nsnull;
566 : }
567 0 : return false;
568 : }
569 :
570 0 : nsresult nsViewManager::InvalidateView(nsIView *aView, const nsRect &aRect)
571 : {
572 : // If painting is suppressed in the presshell or an ancestor drop all
573 : // invalidates, it will invalidate everything when it unsuppresses.
574 0 : if (ShouldIgnoreInvalidation(this)) {
575 0 : return NS_OK;
576 : }
577 :
578 0 : return InvalidateViewNoSuppression(aView, aRect);
579 : }
580 :
581 0 : NS_IMETHODIMP nsViewManager::InvalidateViewNoSuppression(nsIView *aView,
582 : const nsRect &aRect)
583 : {
584 0 : NS_PRECONDITION(nsnull != aView, "null view");
585 :
586 0 : nsView* view = static_cast<nsView*>(aView);
587 :
588 0 : NS_ASSERTION(view->GetViewManager() == this,
589 : "InvalidateViewNoSuppression called on view we don't own");
590 :
591 0 : nsRect damagedRect(aRect);
592 0 : if (damagedRect.IsEmpty()) {
593 0 : return NS_OK;
594 : }
595 :
596 0 : nsView* displayRoot = static_cast<nsView*>(GetDisplayRootFor(view));
597 0 : nsViewManager* displayRootVM = displayRoot->GetViewManager();
598 : // Propagate the update to the displayRoot, since iframes, for example,
599 : // can overlap each other and be translucent. So we have to possibly
600 : // invalidate our rect in each of the widgets we have lying about.
601 0 : damagedRect.MoveBy(view->GetOffsetTo(displayRoot));
602 0 : PRInt32 rootAPD = displayRootVM->AppUnitsPerDevPixel();
603 0 : PRInt32 APD = AppUnitsPerDevPixel();
604 0 : damagedRect = damagedRect.ConvertAppUnitsRoundOut(APD, rootAPD);
605 :
606 : // accumulate this rectangle in the view's dirty region, so we can
607 : // process it later.
608 0 : AddDirtyRegion(displayRoot, nsRegion(damagedRect));
609 :
610 : // Schedule an invalidation flush with the refresh driver.
611 0 : PostPendingUpdate();
612 :
613 0 : return NS_OK;
614 : }
615 :
616 0 : NS_IMETHODIMP nsViewManager::InvalidateAllViews()
617 : {
618 0 : if (RootViewManager() != this) {
619 0 : return RootViewManager()->InvalidateAllViews();
620 : }
621 :
622 0 : InvalidateViews(mRootView);
623 0 : return NS_OK;
624 : }
625 :
626 0 : void nsViewManager::InvalidateViews(nsView *aView)
627 : {
628 : // Invalidate this view.
629 0 : InvalidateView(aView);
630 :
631 : // Invalidate all children as well.
632 0 : nsView* childView = aView->GetFirstChild();
633 0 : while (nsnull != childView) {
634 0 : childView->GetViewManager()->InvalidateViews(childView);
635 0 : childView = childView->GetNextSibling();
636 : }
637 0 : }
638 :
639 : static bool
640 0 : IsViewForPopup(nsIView* aView)
641 : {
642 0 : nsIWidget* widget = aView->GetWidget();
643 0 : if (widget) {
644 : nsWindowType type;
645 0 : widget->GetWindowType(type);
646 0 : return (type == eWindowType_popup);
647 : }
648 :
649 0 : return false;
650 : }
651 :
652 0 : NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
653 : nsIView* aView, nsEventStatus *aStatus)
654 : {
655 0 : NS_ASSERTION(!aView || static_cast<nsView*>(aView)->GetViewManager() == this,
656 : "wrong view manager");
657 :
658 0 : SAMPLE_LABEL("event", "nsViewManager::DispatchEvent");
659 :
660 0 : *aStatus = nsEventStatus_eIgnore;
661 :
662 0 : switch(aEvent->message)
663 : {
664 : case NS_SIZE:
665 : {
666 0 : if (aView)
667 : {
668 : // client area dimensions are set on the view
669 0 : nscoord width = ((nsSizeEvent*)aEvent)->windowSize->width;
670 0 : nscoord height = ((nsSizeEvent*)aEvent)->windowSize->height;
671 :
672 : // The root view may not be set if this is the resize associated with
673 : // window creation
674 :
675 0 : if (aView == mRootView)
676 : {
677 0 : PRInt32 p2a = AppUnitsPerDevPixel();
678 : SetWindowDimensions(NSIntPixelsToAppUnits(width, p2a),
679 0 : NSIntPixelsToAppUnits(height, p2a));
680 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
681 : }
682 0 : else if (IsViewForPopup(aView))
683 : {
684 0 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
685 0 : if (pm)
686 : {
687 0 : pm->PopupResized(aView->GetFrame(), nsIntSize(width, height));
688 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
689 : }
690 : }
691 : }
692 : }
693 :
694 0 : break;
695 :
696 : case NS_MOVE:
697 : {
698 : // A popup's parent view is the root view for the parent window, so when
699 : // a popup moves, the popup's frame and view position must be updated
700 : // to match.
701 0 : if (aView && IsViewForPopup(aView))
702 : {
703 0 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
704 0 : if (pm)
705 : {
706 0 : pm->PopupMoved(aView->GetFrame(), aEvent->refPoint);
707 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
708 : }
709 : }
710 0 : break;
711 : }
712 :
713 : case NS_DONESIZEMOVE:
714 : {
715 0 : if (mPresShell) {
716 0 : nsPresContext* presContext = mPresShell->GetPresContext();
717 0 : if (presContext) {
718 0 : nsEventStateManager::ClearGlobalActiveContent(nsnull);
719 : }
720 :
721 : }
722 :
723 0 : nsIPresShell::ClearMouseCapture(nsnull);
724 : }
725 0 : break;
726 :
727 : case NS_XUL_CLOSE:
728 : {
729 : // if this is a popup, make a request to hide it. Note that a popuphidden
730 : // event listener may cancel the event and the popup will not be hidden.
731 0 : nsIWidget* widget = aView->GetWidget();
732 0 : if (widget) {
733 : nsWindowType type;
734 0 : widget->GetWindowType(type);
735 0 : if (type == eWindowType_popup) {
736 0 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
737 0 : if (pm) {
738 0 : pm->HidePopup(aView->GetFrame());
739 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
740 : }
741 : }
742 : }
743 : }
744 0 : break;
745 :
746 : case NS_WILL_PAINT:
747 : {
748 0 : if (!aView || !mContext)
749 0 : break;
750 :
751 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
752 :
753 0 : nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
754 :
755 0 : NS_ASSERTION(static_cast<nsView*>(aView) ==
756 : nsView::GetViewFor(event->widget),
757 : "view/widget mismatch");
758 :
759 : // If an ancestor widget was hidden and then shown, we could
760 : // have a delayed resize to handle.
761 0 : for (nsViewManager *vm = this; vm;
762 0 : vm = vm->mRootView->GetParent()
763 0 : ? vm->mRootView->GetParent()->GetViewManager()
764 : : nsnull) {
765 0 : if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
766 0 : vm->mRootView->IsEffectivelyVisible() &&
767 0 : mPresShell && mPresShell->IsVisible()) {
768 0 : vm->FlushDelayedResize(true);
769 0 : vm->InvalidateView(vm->mRootView);
770 : }
771 : }
772 :
773 : // Flush things like reflows and plugin widget geometry updates by
774 : // calling WillPaint on observer presShells.
775 0 : nsRefPtr<nsViewManager> rootVM = RootViewManager();
776 0 : if (mPresShell) {
777 0 : rootVM->CallWillPaintOnObservers(event->willSendDidPaint);
778 : }
779 : // Flush view widget geometry updates and invalidations.
780 0 : rootVM->ProcessPendingUpdates();
781 : }
782 0 : break;
783 :
784 : case NS_PAINT:
785 : {
786 0 : if (!aView || !mContext)
787 0 : break;
788 :
789 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
790 0 : nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
791 0 : nsView* view = static_cast<nsView*>(aView);
792 0 : NS_ASSERTION(view == nsView::GetViewFor(event->widget),
793 : "view/widget mismatch");
794 0 : NS_ASSERTION(IsPaintingAllowed(),
795 : "shouldn't be receiving paint events while painting is "
796 : "disallowed!");
797 :
798 0 : if (!event->didSendWillPaint) {
799 : // Send NS_WILL_PAINT event ourselves.
800 0 : nsPaintEvent willPaintEvent(true, NS_WILL_PAINT, event->widget);
801 0 : willPaintEvent.willSendDidPaint = event->willSendDidPaint;
802 0 : DispatchEvent(&willPaintEvent, view, aStatus);
803 :
804 : // Get the view pointer again since NS_WILL_PAINT might have
805 : // destroyed it during CallWillPaintOnObservers (bug 378273).
806 0 : view = nsView::GetViewFor(event->widget);
807 : }
808 :
809 0 : if (!view || event->region.IsEmpty())
810 0 : break;
811 :
812 : // Paint.
813 0 : Refresh(view, event->widget, event->region, event->willSendDidPaint);
814 :
815 0 : break;
816 : }
817 :
818 : case NS_DID_PAINT: {
819 0 : nsRefPtr<nsViewManager> rootVM = RootViewManager();
820 0 : rootVM->CallDidPaintOnObserver();
821 : break;
822 : }
823 :
824 : case NS_CREATE:
825 : case NS_DESTROY:
826 : case NS_SETZLEVEL:
827 : /* Don't pass these events through. Passing them through
828 : causes performance problems on pages with lots of views/frames
829 : @see bug 112861 */
830 0 : *aStatus = nsEventStatus_eConsumeNoDefault;
831 0 : break;
832 :
833 : case NS_DISPLAYCHANGED:
834 :
835 : //Destroy the cached backbuffer to force a new backbuffer
836 : //be constructed with the appropriate display depth.
837 : //@see bugzilla bug 6061
838 0 : *aStatus = nsEventStatus_eConsumeDoDefault;
839 0 : break;
840 :
841 : case NS_SYSCOLORCHANGED:
842 : {
843 0 : if (mPresShell) {
844 : // Hold a refcount to the presshell. The continued existence of the observer will
845 : // delay deletion of this view hierarchy should the event want to cause its
846 : // destruction in, say, some JavaScript event handler.
847 0 : nsCOMPtr<nsIPresShell> presShell = mPresShell;
848 0 : presShell->HandleEvent(aView->GetFrame(), aEvent, false, aStatus);
849 : }
850 : }
851 0 : break;
852 :
853 : default:
854 : {
855 0 : if ((NS_IS_MOUSE_EVENT(aEvent) &&
856 : // Ignore mouse events that we synthesize.
857 : static_cast<nsMouseEvent*>(aEvent)->reason ==
858 : nsMouseEvent::eReal &&
859 : // Ignore mouse exit and enter (we'll get moves if the user
860 : // is really moving the mouse) since we get them when we
861 : // create and destroy widgets.
862 : aEvent->message != NS_MOUSE_EXIT &&
863 : aEvent->message != NS_MOUSE_ENTER) ||
864 : NS_IS_KEY_EVENT(aEvent) ||
865 : NS_IS_IME_EVENT(aEvent) ||
866 : aEvent->message == NS_PLUGIN_INPUT_EVENT) {
867 0 : gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
868 : }
869 :
870 0 : if (aEvent->message == NS_DEACTIVATE) {
871 : // if a window is deactivated, clear the mouse capture regardless
872 : // of what is capturing
873 0 : nsIPresShell::ClearMouseCapture(nsnull);
874 : }
875 :
876 : // Find the view whose coordinates system we're in.
877 0 : nsIView* view = aView;
878 0 : bool dispatchUsingCoordinates = NS_IsEventUsingCoordinates(aEvent);
879 0 : if (dispatchUsingCoordinates) {
880 : // Will dispatch using coordinates. Pretty bogus but it's consistent
881 : // with what presshell does.
882 0 : view = GetDisplayRootFor(view);
883 : }
884 :
885 : // If the view has no frame, look for a view that does.
886 0 : nsIFrame* frame = view->GetFrame();
887 0 : if (!frame &&
888 : (dispatchUsingCoordinates || NS_IS_KEY_EVENT(aEvent) ||
889 : NS_IS_IME_RELATED_EVENT(aEvent) ||
890 0 : NS_IS_NON_RETARGETED_PLUGIN_EVENT(aEvent) ||
891 : aEvent->message == NS_PLUGIN_ACTIVATE ||
892 : aEvent->message == NS_PLUGIN_FOCUS)) {
893 0 : while (view && !view->GetFrame()) {
894 0 : view = view->GetParent();
895 : }
896 :
897 0 : if (view) {
898 0 : frame = view->GetFrame();
899 : }
900 : }
901 :
902 0 : if (nsnull != frame) {
903 : // Hold a refcount to the presshell. The continued existence of the
904 : // presshell will delay deletion of this view hierarchy should the event
905 : // want to cause its destruction in, say, some JavaScript event handler.
906 0 : nsCOMPtr<nsIPresShell> shell = view->GetViewManager()->GetPresShell();
907 0 : if (shell) {
908 0 : shell->HandleEvent(frame, aEvent, false, aStatus);
909 : }
910 : }
911 :
912 0 : break;
913 : }
914 : }
915 :
916 0 : return NS_OK;
917 : }
918 :
919 : // Recursively reparent widgets if necessary
920 :
921 0 : void nsViewManager::ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget)
922 : {
923 0 : NS_PRECONDITION(aNewWidget, "");
924 :
925 0 : if (aView->HasWidget()) {
926 : // Check to see if the parent widget is the
927 : // same as the new parent. If not then reparent
928 : // the widget, otherwise there is nothing more
929 : // to do for the view and its descendants
930 0 : nsIWidget* widget = aView->GetWidget();
931 0 : nsIWidget* parentWidget = widget->GetParent();
932 0 : if (parentWidget) {
933 : // Child widget
934 0 : if (parentWidget != aNewWidget) {
935 : #ifdef DEBUG
936 : nsresult rv =
937 : #endif
938 0 : widget->SetParent(aNewWidget);
939 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "SetParent failed!");
940 : }
941 : } else {
942 : // Toplevel widget (popup, dialog, etc)
943 0 : widget->ReparentNativeWidget(aNewWidget);
944 : }
945 0 : return;
946 : }
947 :
948 : // Need to check each of the views children to see
949 : // if they have a widget and reparent it.
950 :
951 0 : nsView* view = static_cast<nsView*>(aView);
952 0 : for (nsView *kid = view->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
953 0 : ReparentChildWidgets(kid, aNewWidget);
954 : }
955 : }
956 :
957 : // Reparent a view and its descendant views widgets if necessary
958 :
959 0 : void nsViewManager::ReparentWidgets(nsIView* aView, nsIView *aParent)
960 : {
961 0 : NS_PRECONDITION(aParent, "Must have a parent");
962 0 : NS_PRECONDITION(aView, "Must have a view");
963 :
964 : // Quickly determine whether the view has pre-existing children or a
965 : // widget. In most cases the view will not have any pre-existing
966 : // children when this is called. Only in the case
967 : // where a view has been reparented by removing it from
968 : // a reinserting it into a new location in the view hierarchy do we
969 : // have to consider reparenting the existing widgets for the view and
970 : // it's descendants.
971 0 : nsView* view = static_cast<nsView*>(aView);
972 0 : if (view->HasWidget() || view->GetFirstChild()) {
973 0 : nsIWidget* parentWidget = aParent->GetNearestWidget(nsnull);
974 0 : if (parentWidget) {
975 0 : ReparentChildWidgets(aView, parentWidget);
976 0 : return;
977 : }
978 0 : NS_WARNING("Can not find a widget for the parent view");
979 : }
980 : }
981 :
982 0 : NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, nsIView *aSibling,
983 : bool aAfter)
984 : {
985 0 : nsView* parent = static_cast<nsView*>(aParent);
986 0 : nsView* child = static_cast<nsView*>(aChild);
987 0 : nsView* sibling = static_cast<nsView*>(aSibling);
988 :
989 0 : NS_PRECONDITION(nsnull != parent, "null ptr");
990 0 : NS_PRECONDITION(nsnull != child, "null ptr");
991 0 : NS_ASSERTION(sibling == nsnull || sibling->GetParent() == parent,
992 : "tried to insert view with invalid sibling");
993 0 : NS_ASSERTION(!IsViewInserted(child), "tried to insert an already-inserted view");
994 :
995 0 : if ((nsnull != parent) && (nsnull != child))
996 : {
997 : // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
998 : // order, otherwise after 'kid' (i.e. before 'kid' in document order).
999 :
1000 : #if 1
1001 0 : if (nsnull == aSibling) {
1002 0 : if (aAfter) {
1003 : // insert at end of document order, i.e., before first view
1004 : // this is the common case, by far
1005 0 : parent->InsertChild(child, nsnull);
1006 0 : ReparentWidgets(child, parent);
1007 : } else {
1008 : // insert at beginning of document order, i.e., after last view
1009 0 : nsView *kid = parent->GetFirstChild();
1010 0 : nsView *prev = nsnull;
1011 0 : while (kid) {
1012 0 : prev = kid;
1013 0 : kid = kid->GetNextSibling();
1014 : }
1015 : // prev is last view or null if there are no children
1016 0 : parent->InsertChild(child, prev);
1017 0 : ReparentWidgets(child, parent);
1018 : }
1019 : } else {
1020 0 : nsView *kid = parent->GetFirstChild();
1021 0 : nsView *prev = nsnull;
1022 0 : while (kid && sibling != kid) {
1023 : //get the next sibling view
1024 0 : prev = kid;
1025 0 : kid = kid->GetNextSibling();
1026 : }
1027 0 : NS_ASSERTION(kid != nsnull,
1028 : "couldn't find sibling in child list");
1029 0 : if (aAfter) {
1030 : // insert after 'kid' in document order, i.e. before in view order
1031 0 : parent->InsertChild(child, prev);
1032 0 : ReparentWidgets(child, parent);
1033 : } else {
1034 : // insert before 'kid' in document order, i.e. after in view order
1035 0 : parent->InsertChild(child, kid);
1036 0 : ReparentWidgets(child, parent);
1037 : }
1038 : }
1039 : #else // don't keep consistent document order, but order things by z-index instead
1040 : // essentially we're emulating the old InsertChild(parent, child, zindex)
1041 : PRInt32 zIndex = child->GetZIndex();
1042 : while (nsnull != kid)
1043 : {
1044 : PRInt32 idx = kid->GetZIndex();
1045 :
1046 : if (CompareZIndex(zIndex, child->IsTopMost(), child->GetZIndexIsAuto(),
1047 : idx, kid->IsTopMost(), kid->GetZIndexIsAuto()) >= 0)
1048 : break;
1049 :
1050 : prev = kid;
1051 : kid = kid->GetNextSibling();
1052 : }
1053 :
1054 : parent->InsertChild(child, prev);
1055 : ReparentWidgets(child, parent);
1056 : #endif
1057 :
1058 : // if the parent view is marked as "floating", make the newly added view float as well.
1059 0 : if (parent->GetFloating())
1060 0 : child->SetFloating(true);
1061 :
1062 : //and mark this area as dirty if the view is visible...
1063 :
1064 0 : if (nsViewVisibility_kHide != child->GetVisibility())
1065 0 : child->GetViewManager()->InvalidateView(child);
1066 : }
1067 0 : return NS_OK;
1068 : }
1069 :
1070 0 : NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, PRInt32 aZIndex)
1071 : {
1072 : // no-one really calls this with anything other than aZIndex == 0 on a fresh view
1073 : // XXX this method should simply be eliminated and its callers redirected to the real method
1074 0 : SetViewZIndex(aChild, false, aZIndex, false);
1075 0 : return InsertChild(aParent, aChild, nsnull, true);
1076 : }
1077 :
1078 0 : NS_IMETHODIMP nsViewManager::RemoveChild(nsIView *aChild)
1079 : {
1080 0 : nsView* child = static_cast<nsView*>(aChild);
1081 0 : NS_ENSURE_ARG_POINTER(child);
1082 :
1083 0 : nsView* parent = child->GetParent();
1084 :
1085 0 : if (nsnull != parent) {
1086 0 : NS_ASSERTION(child->GetViewManager() == this ||
1087 : parent->GetViewManager() == this, "wrong view manager");
1088 0 : child->GetViewManager()->InvalidateView(child);
1089 0 : parent->RemoveChild(child);
1090 : }
1091 :
1092 0 : return NS_OK;
1093 : }
1094 :
1095 0 : NS_IMETHODIMP nsViewManager::MoveViewTo(nsIView *aView, nscoord aX, nscoord aY)
1096 : {
1097 0 : nsView* view = static_cast<nsView*>(aView);
1098 0 : NS_ASSERTION(view->GetViewManager() == this, "wrong view manager");
1099 0 : nsPoint oldPt = view->GetPosition();
1100 0 : nsRect oldBounds = view->GetBoundsInParentUnits();
1101 0 : view->SetPosition(aX, aY);
1102 :
1103 : // only do damage control if the view is visible
1104 :
1105 0 : if ((aX != oldPt.x) || (aY != oldPt.y)) {
1106 0 : if (view->GetVisibility() != nsViewVisibility_kHide) {
1107 0 : nsView* parentView = view->GetParent();
1108 0 : if (parentView) {
1109 0 : nsViewManager* parentVM = parentView->GetViewManager();
1110 0 : parentVM->InvalidateView(parentView, oldBounds);
1111 0 : parentVM->InvalidateView(parentView, view->GetBoundsInParentUnits());
1112 : }
1113 : }
1114 : }
1115 0 : return NS_OK;
1116 : }
1117 :
1118 0 : void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
1119 : nscoord aY1, nscoord aY2, bool aInCutOut) {
1120 0 : nscoord height = aY2 - aY1;
1121 0 : if (aRect.x < aCutOut.x) {
1122 0 : nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height);
1123 0 : InvalidateView(aView, r);
1124 : }
1125 0 : if (!aInCutOut && aCutOut.x < aCutOut.XMost()) {
1126 0 : nsRect r(aCutOut.x, aY1, aCutOut.width, height);
1127 0 : InvalidateView(aView, r);
1128 : }
1129 0 : if (aCutOut.XMost() < aRect.XMost()) {
1130 0 : nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height);
1131 0 : InvalidateView(aView, r);
1132 : }
1133 0 : }
1134 :
1135 0 : void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut) {
1136 0 : NS_ASSERTION(aView->GetViewManager() == this,
1137 : "InvalidateRectDifference called on view we don't own");
1138 0 : if (aRect.y < aCutOut.y) {
1139 0 : InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aRect.y, aCutOut.y, false);
1140 : }
1141 0 : if (aCutOut.y < aCutOut.YMost()) {
1142 0 : InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.y, aCutOut.YMost(), true);
1143 : }
1144 0 : if (aCutOut.YMost() < aRect.YMost()) {
1145 0 : InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.YMost(), aRect.YMost(), false);
1146 : }
1147 0 : }
1148 :
1149 0 : NS_IMETHODIMP nsViewManager::ResizeView(nsIView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly)
1150 : {
1151 0 : nsView* view = static_cast<nsView*>(aView);
1152 0 : NS_ASSERTION(view->GetViewManager() == this, "wrong view manager");
1153 :
1154 0 : nsRect oldDimensions = view->GetDimensions();
1155 0 : if (!oldDimensions.IsEqualEdges(aRect)) {
1156 : // resize the view.
1157 : // Prevent Invalidation of hidden views
1158 0 : if (view->GetVisibility() == nsViewVisibility_kHide) {
1159 0 : view->SetDimensions(aRect, false);
1160 : } else {
1161 0 : nsView* parentView = view->GetParent();
1162 0 : if (!parentView) {
1163 0 : parentView = view;
1164 : }
1165 0 : nsRect oldBounds = view->GetBoundsInParentUnits();
1166 0 : view->SetDimensions(aRect, true);
1167 0 : nsViewManager* parentVM = parentView->GetViewManager();
1168 0 : if (!aRepaintExposedAreaOnly) {
1169 : // Invalidate the union of the old and new size
1170 0 : InvalidateView(view, aRect);
1171 0 : parentVM->InvalidateView(parentView, oldBounds);
1172 : } else {
1173 0 : InvalidateRectDifference(view, aRect, oldDimensions);
1174 0 : nsRect newBounds = view->GetBoundsInParentUnits();
1175 0 : parentVM->InvalidateRectDifference(parentView, oldBounds, newBounds);
1176 : }
1177 : }
1178 : }
1179 :
1180 : // Note that if layout resizes the view and the view has a custom clip
1181 : // region set, then we expect layout to update the clip region too. Thus
1182 : // in the case where mClipRect has been optimized away to just be a null
1183 : // pointer, and this resize is implicitly changing the clip rect, it's OK
1184 : // because layout will change it back again if necessary.
1185 :
1186 0 : return NS_OK;
1187 : }
1188 :
1189 0 : NS_IMETHODIMP nsViewManager::SetViewFloating(nsIView *aView, bool aFloating)
1190 : {
1191 0 : nsView* view = static_cast<nsView*>(aView);
1192 :
1193 0 : NS_ASSERTION(!(nsnull == view), "no view");
1194 :
1195 0 : view->SetFloating(aFloating);
1196 :
1197 0 : return NS_OK;
1198 : }
1199 :
1200 0 : NS_IMETHODIMP nsViewManager::SetViewVisibility(nsIView *aView, nsViewVisibility aVisible)
1201 : {
1202 0 : nsView* view = static_cast<nsView*>(aView);
1203 0 : NS_ASSERTION(view->GetViewManager() == this, "wrong view manager");
1204 :
1205 0 : if (aVisible != view->GetVisibility()) {
1206 0 : view->SetVisibility(aVisible);
1207 :
1208 0 : if (IsViewInserted(view)) {
1209 0 : if (!view->HasWidget()) {
1210 0 : if (nsViewVisibility_kHide == aVisible) {
1211 0 : nsView* parentView = view->GetParent();
1212 0 : if (parentView) {
1213 : parentView->GetViewManager()->
1214 0 : InvalidateView(parentView, view->GetBoundsInParentUnits());
1215 : }
1216 : }
1217 : else {
1218 0 : InvalidateView(view);
1219 : }
1220 : }
1221 : }
1222 : }
1223 0 : return NS_OK;
1224 : }
1225 :
1226 0 : bool nsViewManager::IsViewInserted(nsView *aView)
1227 : {
1228 0 : if (mRootView == aView) {
1229 0 : return true;
1230 0 : } else if (aView->GetParent() == nsnull) {
1231 0 : return false;
1232 : } else {
1233 0 : nsView* view = aView->GetParent()->GetFirstChild();
1234 0 : while (view != nsnull) {
1235 0 : if (view == aView) {
1236 0 : return true;
1237 : }
1238 0 : view = view->GetNextSibling();
1239 : }
1240 0 : return false;
1241 : }
1242 : }
1243 :
1244 0 : NS_IMETHODIMP nsViewManager::SetViewZIndex(nsIView *aView, bool aAutoZIndex, PRInt32 aZIndex, bool aTopMost)
1245 : {
1246 0 : nsView* view = static_cast<nsView*>(aView);
1247 0 : nsresult rv = NS_OK;
1248 :
1249 0 : NS_ASSERTION((view != nsnull), "no view");
1250 :
1251 : // don't allow the root view's z-index to be changed. It should always be zero.
1252 : // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1253 0 : if (aView == mRootView) {
1254 0 : return rv;
1255 : }
1256 :
1257 0 : bool oldTopMost = view->IsTopMost();
1258 0 : bool oldIsAuto = view->GetZIndexIsAuto();
1259 :
1260 0 : if (aAutoZIndex) {
1261 0 : aZIndex = 0;
1262 : }
1263 :
1264 0 : PRInt32 oldidx = view->GetZIndex();
1265 0 : view->SetZIndex(aAutoZIndex, aZIndex, aTopMost);
1266 :
1267 0 : if (oldidx != aZIndex || oldTopMost != aTopMost ||
1268 : oldIsAuto != aAutoZIndex) {
1269 0 : InvalidateView(view);
1270 : }
1271 :
1272 0 : return rv;
1273 : }
1274 :
1275 0 : NS_IMETHODIMP nsViewManager::GetDeviceContext(nsDeviceContext *&aContext)
1276 : {
1277 0 : aContext = mContext;
1278 0 : NS_IF_ADDREF(aContext);
1279 0 : return NS_OK;
1280 : }
1281 :
1282 : nsIViewManager*
1283 0 : nsViewManager::IncrementDisableRefreshCount()
1284 : {
1285 0 : if (!IsRootVM()) {
1286 0 : return RootViewManager()->IncrementDisableRefreshCount();
1287 : }
1288 :
1289 0 : ++mRefreshDisableCount;
1290 :
1291 0 : return this;
1292 : }
1293 :
1294 : void
1295 0 : nsViewManager::DecrementDisableRefreshCount()
1296 : {
1297 0 : NS_ASSERTION(IsRootVM(), "Should only be called on root");
1298 0 : --mRefreshDisableCount;
1299 0 : NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
1300 0 : }
1301 :
1302 0 : NS_IMETHODIMP nsViewManager::GetRootWidget(nsIWidget **aWidget)
1303 : {
1304 0 : if (!mRootView) {
1305 0 : *aWidget = nsnull;
1306 0 : return NS_OK;
1307 : }
1308 0 : if (mRootView->HasWidget()) {
1309 0 : *aWidget = mRootView->GetWidget();
1310 0 : NS_ADDREF(*aWidget);
1311 0 : return NS_OK;
1312 : }
1313 0 : if (mRootView->GetParent())
1314 0 : return mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
1315 0 : *aWidget = nsnull;
1316 0 : return NS_OK;
1317 : }
1318 :
1319 0 : nsIntRect nsViewManager::ViewToWidget(nsView *aView, const nsRect &aRect) const
1320 : {
1321 0 : NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
1322 :
1323 : // intersect aRect with bounds of aView, to prevent generating any illegal rectangles.
1324 0 : nsRect bounds = aView->GetInvalidationDimensions();
1325 0 : nsRect rect;
1326 0 : rect.IntersectRect(aRect, bounds);
1327 :
1328 : // account for the view's origin not lining up with the widget's
1329 0 : rect += aView->ViewToWidgetOffset();
1330 :
1331 : // finally, convert to device coordinates.
1332 0 : return rect.ToOutsidePixels(AppUnitsPerDevPixel());
1333 : }
1334 :
1335 : NS_IMETHODIMP
1336 0 : nsViewManager::IsPainting(bool& aIsPainting)
1337 : {
1338 0 : aIsPainting = IsPainting();
1339 0 : return NS_OK;
1340 : }
1341 :
1342 : void
1343 0 : nsViewManager::ProcessPendingUpdates()
1344 : {
1345 0 : if (!IsRootVM()) {
1346 0 : RootViewManager()->ProcessPendingUpdates();
1347 0 : return;
1348 : }
1349 :
1350 0 : if (mHasPendingUpdates) {
1351 0 : ProcessPendingUpdatesForView(mRootView, true);
1352 0 : mHasPendingUpdates = false;
1353 : }
1354 : }
1355 :
1356 : void
1357 0 : nsViewManager::UpdateWidgetGeometry()
1358 : {
1359 0 : if (!IsRootVM()) {
1360 0 : RootViewManager()->UpdateWidgetGeometry();
1361 0 : return;
1362 : }
1363 :
1364 0 : if (mHasPendingWidgetGeometryChanges) {
1365 0 : ProcessPendingUpdatesForView(mRootView, false);
1366 0 : mHasPendingWidgetGeometryChanges = false;
1367 : }
1368 : }
1369 :
1370 : void
1371 0 : nsViewManager::CallWillPaintOnObservers(bool aWillSendDidPaint)
1372 : {
1373 0 : NS_PRECONDITION(IsRootVM(), "Must be root VM for this to be called!");
1374 :
1375 : PRInt32 index;
1376 0 : for (index = 0; index < mVMCount; index++) {
1377 0 : nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index);
1378 0 : if (vm->RootViewManager() == this) {
1379 : // One of our kids.
1380 0 : if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
1381 0 : nsCOMPtr<nsIPresShell> shell = vm->GetPresShell();
1382 0 : if (shell) {
1383 0 : shell->WillPaint(aWillSendDidPaint);
1384 : }
1385 : }
1386 : }
1387 : }
1388 0 : }
1389 :
1390 : void
1391 0 : nsViewManager::CallDidPaintOnObserver()
1392 : {
1393 0 : NS_PRECONDITION(IsRootVM(), "Must be root VM for this to be called!");
1394 :
1395 0 : if (mRootView && mRootView->IsEffectivelyVisible()) {
1396 0 : nsCOMPtr<nsIPresShell> shell = GetPresShell();
1397 0 : if (shell) {
1398 0 : shell->DidPaint();
1399 : }
1400 : }
1401 0 : }
1402 :
1403 : NS_IMETHODIMP
1404 0 : nsViewManager::GetLastUserEventTime(PRUint32& aTime)
1405 : {
1406 0 : aTime = gLastUserEventTime;
1407 0 : return NS_OK;
1408 : }
1409 :
1410 : void
1411 0 : nsViewManager::InvalidateHierarchy()
1412 : {
1413 0 : if (mRootView) {
1414 0 : if (!IsRootVM()) {
1415 0 : NS_RELEASE(mRootViewManager);
1416 : }
1417 0 : nsView *parent = mRootView->GetParent();
1418 0 : if (parent) {
1419 0 : mRootViewManager = parent->GetViewManager()->RootViewManager();
1420 0 : NS_ADDREF(mRootViewManager);
1421 0 : NS_ASSERTION(mRootViewManager != this,
1422 : "Root view had a parent, but it has the same view manager");
1423 : } else {
1424 0 : mRootViewManager = this;
1425 : }
1426 : }
1427 0 : }
|