|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1) |
|
7 #include "plarena.h" |
|
8 |
|
9 #include "nsAutoPtr.h" |
|
10 #include "nsViewManager.h" |
|
11 #include "nsGfxCIID.h" |
|
12 #include "nsView.h" |
|
13 #include "nsCOMPtr.h" |
|
14 #include "mozilla/MouseEvents.h" |
|
15 #include "nsRegion.h" |
|
16 #include "nsCOMArray.h" |
|
17 #include "nsIPluginWidget.h" |
|
18 #include "nsXULPopupManager.h" |
|
19 #include "nsIPresShell.h" |
|
20 #include "nsPresContext.h" |
|
21 #include "mozilla/StartupTimeline.h" |
|
22 #include "GeckoProfiler.h" |
|
23 #include "nsRefreshDriver.h" |
|
24 #include "mozilla/Preferences.h" |
|
25 #include "nsContentUtils.h" // for nsAutoScriptBlocker |
|
26 #include "nsLayoutUtils.h" |
|
27 #include "Layers.h" |
|
28 #include "gfxPlatform.h" |
|
29 #include "gfxPrefs.h" |
|
30 #include "nsIDocument.h" |
|
31 |
|
32 /** |
|
33 XXX TODO XXX |
|
34 |
|
35 DeCOMify newly private methods |
|
36 Optimize view storage |
|
37 */ |
|
38 |
|
39 /** |
|
40 A note about platform assumptions: |
|
41 |
|
42 We assume that a widget is z-ordered on top of its parent. |
|
43 |
|
44 We do NOT assume anything about the relative z-ordering of sibling widgets. Even though |
|
45 we ask for a specific z-order, we don't assume that widget z-ordering actually works. |
|
46 */ |
|
47 |
|
48 using namespace mozilla; |
|
49 using namespace mozilla::layers; |
|
50 |
|
51 #define NSCOORD_NONE INT32_MIN |
|
52 |
|
53 #undef DEBUG_MOUSE_LOCATION |
|
54 |
|
55 int32_t nsViewManager::mVMCount = 0; |
|
56 |
|
57 // Weakly held references to all of the view managers |
|
58 nsVoidArray* nsViewManager::gViewManagers = nullptr; |
|
59 uint32_t nsViewManager::gLastUserEventTime = 0; |
|
60 |
|
61 nsViewManager::nsViewManager() |
|
62 : mDelayedResize(NSCOORD_NONE, NSCOORD_NONE) |
|
63 { |
|
64 mRootViewManager = this; |
|
65 if (gViewManagers == nullptr) { |
|
66 NS_ASSERTION(mVMCount == 0, "View Manager count is incorrect"); |
|
67 // Create an array to hold a list of view managers |
|
68 gViewManagers = new nsVoidArray; |
|
69 } |
|
70 |
|
71 gViewManagers->AppendElement(this); |
|
72 |
|
73 ++mVMCount; |
|
74 |
|
75 // NOTE: we use a zeroing operator new, so all data members are |
|
76 // assumed to be cleared here. |
|
77 mHasPendingWidgetGeometryChanges = false; |
|
78 mRecursiveRefreshPending = false; |
|
79 } |
|
80 |
|
81 nsViewManager::~nsViewManager() |
|
82 { |
|
83 if (mRootView) { |
|
84 // Destroy any remaining views |
|
85 mRootView->Destroy(); |
|
86 mRootView = nullptr; |
|
87 } |
|
88 |
|
89 if (!IsRootVM()) { |
|
90 // We have a strong ref to mRootViewManager |
|
91 NS_RELEASE(mRootViewManager); |
|
92 } |
|
93 |
|
94 NS_ASSERTION((mVMCount > 0), "underflow of viewmanagers"); |
|
95 --mVMCount; |
|
96 |
|
97 #ifdef DEBUG |
|
98 bool removed = |
|
99 #endif |
|
100 gViewManagers->RemoveElement(this); |
|
101 NS_ASSERTION(removed, "Viewmanager instance not was not in the global list of viewmanagers"); |
|
102 |
|
103 if (0 == mVMCount) { |
|
104 // There aren't any more view managers so |
|
105 // release the global array of view managers |
|
106 |
|
107 NS_ASSERTION(gViewManagers != nullptr, "About to delete null gViewManagers"); |
|
108 delete gViewManagers; |
|
109 gViewManagers = nullptr; |
|
110 } |
|
111 |
|
112 mPresShell = nullptr; |
|
113 } |
|
114 |
|
115 // We don't hold a reference to the presentation context because it |
|
116 // holds a reference to us. |
|
117 nsresult |
|
118 nsViewManager::Init(nsDeviceContext* aContext) |
|
119 { |
|
120 NS_PRECONDITION(nullptr != aContext, "null ptr"); |
|
121 |
|
122 if (nullptr == aContext) { |
|
123 return NS_ERROR_NULL_POINTER; |
|
124 } |
|
125 if (nullptr != mContext) { |
|
126 return NS_ERROR_ALREADY_INITIALIZED; |
|
127 } |
|
128 mContext = aContext; |
|
129 |
|
130 return NS_OK; |
|
131 } |
|
132 |
|
133 nsView* |
|
134 nsViewManager::CreateView(const nsRect& aBounds, |
|
135 nsView* aParent, |
|
136 nsViewVisibility aVisibilityFlag) |
|
137 { |
|
138 nsView *v = new nsView(this, aVisibilityFlag); |
|
139 v->SetParent(aParent); |
|
140 v->SetPosition(aBounds.x, aBounds.y); |
|
141 nsRect dim(0, 0, aBounds.width, aBounds.height); |
|
142 v->SetDimensions(dim, false); |
|
143 return v; |
|
144 } |
|
145 |
|
146 void |
|
147 nsViewManager::SetRootView(nsView *aView) |
|
148 { |
|
149 NS_PRECONDITION(!aView || aView->GetViewManager() == this, |
|
150 "Unexpected viewmanager on root view"); |
|
151 |
|
152 // Do NOT destroy the current root view. It's the caller's responsibility |
|
153 // to destroy it |
|
154 mRootView = aView; |
|
155 |
|
156 if (mRootView) { |
|
157 nsView* parent = mRootView->GetParent(); |
|
158 if (parent) { |
|
159 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so |
|
160 // no need to set mRootViewManager ourselves here. |
|
161 parent->InsertChild(mRootView, nullptr); |
|
162 } else { |
|
163 InvalidateHierarchy(); |
|
164 } |
|
165 |
|
166 mRootView->SetZIndex(false, 0); |
|
167 } |
|
168 // Else don't touch mRootViewManager |
|
169 } |
|
170 |
|
171 void |
|
172 nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight) |
|
173 { |
|
174 if (nullptr != mRootView) { |
|
175 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) { |
|
176 nsRect dim = mRootView->GetDimensions(); |
|
177 *aWidth = dim.width; |
|
178 *aHeight = dim.height; |
|
179 } else { |
|
180 *aWidth = mDelayedResize.width; |
|
181 *aHeight = mDelayedResize.height; |
|
182 } |
|
183 } |
|
184 else |
|
185 { |
|
186 *aWidth = 0; |
|
187 *aHeight = 0; |
|
188 } |
|
189 } |
|
190 |
|
191 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight) |
|
192 { |
|
193 nsRect oldDim = mRootView->GetDimensions(); |
|
194 nsRect newDim(0, 0, aWidth, aHeight); |
|
195 // We care about resizes even when one dimension is already zero. |
|
196 if (!oldDim.IsEqualEdges(newDim)) { |
|
197 // Don't resize the widget. It is already being set elsewhere. |
|
198 mRootView->SetDimensions(newDim, true, false); |
|
199 if (mPresShell) |
|
200 mPresShell->ResizeReflow(aWidth, aHeight); |
|
201 } |
|
202 } |
|
203 |
|
204 void |
|
205 nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight) |
|
206 { |
|
207 if (mRootView) { |
|
208 if (mRootView->IsEffectivelyVisible() && mPresShell && mPresShell->IsVisible()) { |
|
209 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) && |
|
210 mDelayedResize != nsSize(aWidth, aHeight)) { |
|
211 // We have a delayed resize; that now obsolete size may already have |
|
212 // been flushed to the PresContext so we need to update the PresContext |
|
213 // with the new size because if the new size is exactly the same as the |
|
214 // root view's current size then DoSetWindowDimensions will not |
|
215 // request a resize reflow (which would correct it). See bug 617076. |
|
216 mDelayedResize = nsSize(aWidth, aHeight); |
|
217 FlushDelayedResize(false); |
|
218 } |
|
219 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE); |
|
220 DoSetWindowDimensions(aWidth, aHeight); |
|
221 } else { |
|
222 mDelayedResize.SizeTo(aWidth, aHeight); |
|
223 if (mPresShell && mPresShell->GetDocument()) { |
|
224 mPresShell->GetDocument()->SetNeedStyleFlush(); |
|
225 } |
|
226 } |
|
227 } |
|
228 } |
|
229 |
|
230 void |
|
231 nsViewManager::FlushDelayedResize(bool aDoReflow) |
|
232 { |
|
233 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) { |
|
234 if (aDoReflow) { |
|
235 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height); |
|
236 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE); |
|
237 } else if (mPresShell) { |
|
238 nsPresContext* presContext = mPresShell->GetPresContext(); |
|
239 if (presContext) { |
|
240 presContext->SetVisibleArea(nsRect(nsPoint(0, 0), mDelayedResize)); |
|
241 } |
|
242 } |
|
243 } |
|
244 } |
|
245 |
|
246 // Convert aIn from being relative to and in appunits of aFromView, to being |
|
247 // relative to and in appunits of aToView. |
|
248 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn, |
|
249 nsView* aFromView, |
|
250 nsView* aToView) |
|
251 { |
|
252 nsRegion out = aIn; |
|
253 out.MoveBy(aFromView->GetOffsetTo(aToView)); |
|
254 out = out.ConvertAppUnitsRoundOut( |
|
255 aFromView->GetViewManager()->AppUnitsPerDevPixel(), |
|
256 aToView->GetViewManager()->AppUnitsPerDevPixel()); |
|
257 return out; |
|
258 } |
|
259 |
|
260 nsView* nsViewManager::GetDisplayRootFor(nsView* aView) |
|
261 { |
|
262 nsView *displayRoot = aView; |
|
263 for (;;) { |
|
264 nsView *displayParent = displayRoot->GetParent(); |
|
265 if (!displayParent) |
|
266 return displayRoot; |
|
267 |
|
268 if (displayRoot->GetFloating() && !displayParent->GetFloating()) |
|
269 return displayRoot; |
|
270 |
|
271 // If we have a combobox dropdown popup within a panel popup, both the view |
|
272 // for the dropdown popup and its parent will be floating, so we need to |
|
273 // distinguish this situation. We do this by looking for a widget. Any view |
|
274 // with a widget is a display root, except for plugins. |
|
275 nsIWidget* widget = displayRoot->GetWidget(); |
|
276 if (widget && widget->WindowType() == eWindowType_popup) { |
|
277 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(), |
|
278 "this should only happen with floating views that have floating parents"); |
|
279 return displayRoot; |
|
280 } |
|
281 |
|
282 displayRoot = displayParent; |
|
283 } |
|
284 } |
|
285 |
|
286 /** |
|
287 aRegion is given in device coordinates!! |
|
288 aContext may be null, in which case layers should be used for |
|
289 rendering. |
|
290 */ |
|
291 void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion) |
|
292 { |
|
293 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); |
|
294 |
|
295 if (mPresShell && mPresShell->IsNeverPainting()) { |
|
296 return; |
|
297 } |
|
298 |
|
299 // damageRegion is the damaged area, in twips, relative to the view origin |
|
300 nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel()); |
|
301 // move region from widget coordinates into view coordinates |
|
302 damageRegion.MoveBy(-aView->ViewToWidgetOffset()); |
|
303 |
|
304 if (damageRegion.IsEmpty()) { |
|
305 #ifdef DEBUG_roc |
|
306 nsRect viewRect = aView->GetDimensions(); |
|
307 nsRect damageRect = damageRegion.GetBounds(); |
|
308 printf_stderr("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n", |
|
309 damageRect.x, damageRect.y, damageRect.width, damageRect.height, |
|
310 viewRect.x, viewRect.y, viewRect.width, viewRect.height); |
|
311 #endif |
|
312 return; |
|
313 } |
|
314 |
|
315 nsIWidget *widget = aView->GetWidget(); |
|
316 if (!widget) { |
|
317 return; |
|
318 } |
|
319 |
|
320 NS_ASSERTION(!IsPainting(), "recursive painting not permitted"); |
|
321 if (IsPainting()) { |
|
322 RootViewManager()->mRecursiveRefreshPending = true; |
|
323 return; |
|
324 } |
|
325 |
|
326 { |
|
327 nsAutoScriptBlocker scriptBlocker; |
|
328 SetPainting(true); |
|
329 |
|
330 NS_ASSERTION(GetDisplayRootFor(aView) == aView, |
|
331 "Widgets that we paint must all be display roots"); |
|
332 |
|
333 if (mPresShell) { |
|
334 #ifdef MOZ_DUMP_PAINTING |
|
335 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
336 printf_stderr("--COMPOSITE-- %p\n", mPresShell); |
|
337 } |
|
338 #endif |
|
339 uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE; |
|
340 LayerManager *manager = widget->GetLayerManager(); |
|
341 if (!manager->NeedsWidgetInvalidation()) { |
|
342 manager->FlushRendering(); |
|
343 } else { |
|
344 mPresShell->Paint(aView, damageRegion, |
|
345 paintFlags); |
|
346 } |
|
347 #ifdef MOZ_DUMP_PAINTING |
|
348 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
349 printf_stderr("--ENDCOMPOSITE--\n"); |
|
350 } |
|
351 #endif |
|
352 mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT); |
|
353 } |
|
354 |
|
355 SetPainting(false); |
|
356 } |
|
357 |
|
358 if (RootViewManager()->mRecursiveRefreshPending) { |
|
359 RootViewManager()->mRecursiveRefreshPending = false; |
|
360 InvalidateAllViews(); |
|
361 } |
|
362 } |
|
363 |
|
364 void |
|
365 nsViewManager::ProcessPendingUpdatesForView(nsView* aView, |
|
366 bool aFlushDirtyRegion) |
|
367 { |
|
368 NS_ASSERTION(IsRootVM(), "Updates will be missed"); |
|
369 if (!aView) { |
|
370 return; |
|
371 } |
|
372 |
|
373 nsCOMPtr<nsIPresShell> rootShell(mPresShell); |
|
374 nsTArray<nsCOMPtr<nsIWidget> > widgets; |
|
375 aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets); |
|
376 for (uint32_t i = 0; i < widgets.Length(); ++i) { |
|
377 nsView* view = nsView::GetViewFor(widgets[i]); |
|
378 if (view) { |
|
379 view->ResetWidgetBounds(false, true); |
|
380 } |
|
381 } |
|
382 if (rootShell->GetViewManager() != this) { |
|
383 return; // 'this' might have been destroyed |
|
384 } |
|
385 if (aFlushDirtyRegion) { |
|
386 nsAutoScriptBlocker scriptBlocker; |
|
387 SetPainting(true); |
|
388 for (uint32_t i = 0; i < widgets.Length(); ++i) { |
|
389 nsIWidget* widget = widgets[i]; |
|
390 nsView* view = nsView::GetViewFor(widget); |
|
391 if (view) { |
|
392 view->GetViewManager()->ProcessPendingUpdatesPaint(widget); |
|
393 } |
|
394 } |
|
395 SetPainting(false); |
|
396 } |
|
397 } |
|
398 |
|
399 void |
|
400 nsViewManager::ProcessPendingUpdatesRecurse(nsView* aView, |
|
401 nsTArray<nsCOMPtr<nsIWidget> >& aWidgets) |
|
402 { |
|
403 if (mPresShell && mPresShell->IsNeverPainting()) { |
|
404 return; |
|
405 } |
|
406 |
|
407 for (nsView* childView = aView->GetFirstChild(); childView; |
|
408 childView = childView->GetNextSibling()) { |
|
409 childView->GetViewManager()-> |
|
410 ProcessPendingUpdatesRecurse(childView, aWidgets); |
|
411 } |
|
412 |
|
413 nsIWidget* widget = aView->GetWidget(); |
|
414 if (widget) { |
|
415 aWidgets.AppendElement(widget); |
|
416 } else { |
|
417 FlushDirtyRegionToWidget(aView); |
|
418 } |
|
419 } |
|
420 |
|
421 void |
|
422 nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget) |
|
423 { |
|
424 if (aWidget->NeedsPaint()) { |
|
425 // If an ancestor widget was hidden and then shown, we could |
|
426 // have a delayed resize to handle. |
|
427 for (nsViewManager *vm = this; vm; |
|
428 vm = vm->mRootView->GetParent() |
|
429 ? vm->mRootView->GetParent()->GetViewManager() |
|
430 : nullptr) { |
|
431 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) && |
|
432 vm->mRootView->IsEffectivelyVisible() && |
|
433 vm->mPresShell && vm->mPresShell->IsVisible()) { |
|
434 vm->FlushDelayedResize(true); |
|
435 } |
|
436 } |
|
437 nsView* view = nsView::GetViewFor(aWidget); |
|
438 if (!view) { |
|
439 NS_ERROR("FlushDelayedResize destroyed the nsView?"); |
|
440 return; |
|
441 } |
|
442 |
|
443 if (mPresShell) { |
|
444 #ifdef MOZ_DUMP_PAINTING |
|
445 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
446 printf_stderr("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", |
|
447 mPresShell, view, aWidget); |
|
448 } |
|
449 #endif |
|
450 |
|
451 mPresShell->Paint(view, nsRegion(), nsIPresShell::PAINT_LAYERS); |
|
452 view->SetForcedRepaint(false); |
|
453 |
|
454 #ifdef MOZ_DUMP_PAINTING |
|
455 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
456 printf_stderr("---- PAINT END ----\n"); |
|
457 } |
|
458 #endif |
|
459 } |
|
460 } |
|
461 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget)); |
|
462 } |
|
463 |
|
464 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView) |
|
465 { |
|
466 NS_ASSERTION(aView->GetViewManager() == this, |
|
467 "FlushDirtyRegionToWidget called on view we don't own"); |
|
468 |
|
469 if (!aView->HasNonEmptyDirtyRegion()) |
|
470 return; |
|
471 |
|
472 nsRegion* dirtyRegion = aView->GetDirtyRegion(); |
|
473 nsView* nearestViewWithWidget = aView; |
|
474 while (!nearestViewWithWidget->HasWidget() && |
|
475 nearestViewWithWidget->GetParent()) { |
|
476 nearestViewWithWidget = nearestViewWithWidget->GetParent(); |
|
477 } |
|
478 nsRegion r = |
|
479 ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget); |
|
480 |
|
481 // If we draw the frame counter we need to make sure we invalidate the area |
|
482 // for it to make it on screen |
|
483 if (gfxPrefs::DrawFrameCounter()) { |
|
484 nsRect counterBounds = gfxPlatform::FrameCounterBounds().ToAppUnits(AppUnitsPerDevPixel()); |
|
485 r = r.Or(r, counterBounds); |
|
486 } |
|
487 |
|
488 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager(); |
|
489 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r); |
|
490 dirtyRegion->SetEmpty(); |
|
491 } |
|
492 |
|
493 void |
|
494 nsViewManager::InvalidateView(nsView *aView) |
|
495 { |
|
496 // Mark the entire view as damaged |
|
497 InvalidateView(aView, aView->GetDimensions()); |
|
498 } |
|
499 |
|
500 static void |
|
501 AddDirtyRegion(nsView *aView, const nsRegion &aDamagedRegion) |
|
502 { |
|
503 nsRegion* dirtyRegion = aView->GetDirtyRegion(); |
|
504 if (!dirtyRegion) |
|
505 return; |
|
506 |
|
507 dirtyRegion->Or(*dirtyRegion, aDamagedRegion); |
|
508 dirtyRegion->SimplifyOutward(8); |
|
509 } |
|
510 |
|
511 void |
|
512 nsViewManager::PostPendingUpdate() |
|
513 { |
|
514 nsViewManager* rootVM = RootViewManager(); |
|
515 rootVM->mHasPendingWidgetGeometryChanges = true; |
|
516 if (rootVM->mPresShell) { |
|
517 rootVM->mPresShell->ScheduleViewManagerFlush(); |
|
518 } |
|
519 } |
|
520 |
|
521 /** |
|
522 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in |
|
523 * every widget child of aWidgetView, plus aWidgetView's own widget |
|
524 */ |
|
525 void |
|
526 nsViewManager::InvalidateWidgetArea(nsView *aWidgetView, |
|
527 const nsRegion &aDamagedRegion) |
|
528 { |
|
529 NS_ASSERTION(aWidgetView->GetViewManager() == this, |
|
530 "InvalidateWidgetArea called on view we don't own"); |
|
531 nsIWidget* widget = aWidgetView->GetWidget(); |
|
532 |
|
533 #if 0 |
|
534 nsRect dbgBounds = aDamagedRegion.GetBounds(); |
|
535 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n", |
|
536 aWidgetView, aWidgetView->IsAttachedToTopLevel(), |
|
537 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height); |
|
538 #endif |
|
539 |
|
540 // If the widget is hidden, it don't cover nothing |
|
541 if (widget && !widget->IsVisible()) { |
|
542 return; |
|
543 } |
|
544 |
|
545 if (!widget) { |
|
546 // The root view or a scrolling view might not have a widget |
|
547 // (for example, during printing). We get here when we scroll |
|
548 // during printing to show selected options in a listbox, for example. |
|
549 return; |
|
550 } |
|
551 |
|
552 // Update all child widgets with the damage. In the process, |
|
553 // accumulate the union of all the child widget areas, or at least |
|
554 // some subset of that. |
|
555 nsRegion children; |
|
556 if (widget->GetTransparencyMode() != eTransparencyTransparent) { |
|
557 for (nsIWidget* childWidget = widget->GetFirstChild(); |
|
558 childWidget; |
|
559 childWidget = childWidget->GetNextSibling()) { |
|
560 nsView* view = nsView::GetViewFor(childWidget); |
|
561 NS_ASSERTION(view != aWidgetView, "will recur infinitely"); |
|
562 nsWindowType type = childWidget->WindowType(); |
|
563 if (view && childWidget->IsVisible() && type != eWindowType_popup) { |
|
564 NS_ASSERTION(type == eWindowType_plugin, |
|
565 "Only plugin or popup widgets can be children!"); |
|
566 |
|
567 // We do not need to invalidate in plugin widgets, but we should |
|
568 // exclude them from the invalidation region IF we're not on |
|
569 // Mac. On Mac we need to draw under plugin widgets, because |
|
570 // plugin widgets are basically invisible |
|
571 #ifndef XP_MACOSX |
|
572 // GetBounds should compensate for chrome on a toplevel widget |
|
573 nsIntRect bounds; |
|
574 childWidget->GetBounds(bounds); |
|
575 |
|
576 nsTArray<nsIntRect> clipRects; |
|
577 childWidget->GetWindowClipRegion(&clipRects); |
|
578 for (uint32_t i = 0; i < clipRects.Length(); ++i) { |
|
579 nsRect rr = (clipRects[i] + bounds.TopLeft()). |
|
580 ToAppUnits(AppUnitsPerDevPixel()); |
|
581 children.Or(children, rr - aWidgetView->ViewToWidgetOffset()); |
|
582 children.SimplifyInward(20); |
|
583 } |
|
584 #endif |
|
585 } |
|
586 } |
|
587 } |
|
588 |
|
589 nsRegion leftOver; |
|
590 leftOver.Sub(aDamagedRegion, children); |
|
591 |
|
592 if (!leftOver.IsEmpty()) { |
|
593 const nsRect* r; |
|
594 for (nsRegionRectIterator iter(leftOver); (r = iter.Next());) { |
|
595 nsIntRect bounds = ViewToWidget(aWidgetView, *r); |
|
596 widget->Invalidate(bounds); |
|
597 } |
|
598 } |
|
599 } |
|
600 |
|
601 static bool |
|
602 ShouldIgnoreInvalidation(nsViewManager* aVM) |
|
603 { |
|
604 while (aVM) { |
|
605 nsIPresShell* shell = aVM->GetPresShell(); |
|
606 if (!shell || shell->ShouldIgnoreInvalidation()) { |
|
607 return true; |
|
608 } |
|
609 nsView* view = aVM->GetRootView()->GetParent(); |
|
610 aVM = view ? view->GetViewManager() : nullptr; |
|
611 } |
|
612 return false; |
|
613 } |
|
614 |
|
615 void |
|
616 nsViewManager::InvalidateView(nsView *aView, const nsRect &aRect) |
|
617 { |
|
618 // If painting is suppressed in the presshell or an ancestor drop all |
|
619 // invalidates, it will invalidate everything when it unsuppresses. |
|
620 if (ShouldIgnoreInvalidation(this)) { |
|
621 return; |
|
622 } |
|
623 |
|
624 InvalidateViewNoSuppression(aView, aRect); |
|
625 } |
|
626 |
|
627 void |
|
628 nsViewManager::InvalidateViewNoSuppression(nsView *aView, |
|
629 const nsRect &aRect) |
|
630 { |
|
631 NS_PRECONDITION(nullptr != aView, "null view"); |
|
632 |
|
633 NS_ASSERTION(aView->GetViewManager() == this, |
|
634 "InvalidateViewNoSuppression called on view we don't own"); |
|
635 |
|
636 nsRect damagedRect(aRect); |
|
637 if (damagedRect.IsEmpty()) { |
|
638 return; |
|
639 } |
|
640 |
|
641 nsView* displayRoot = GetDisplayRootFor(aView); |
|
642 nsViewManager* displayRootVM = displayRoot->GetViewManager(); |
|
643 // Propagate the update to the displayRoot, since iframes, for example, |
|
644 // can overlap each other and be translucent. So we have to possibly |
|
645 // invalidate our rect in each of the widgets we have lying about. |
|
646 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot)); |
|
647 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel(); |
|
648 int32_t APD = AppUnitsPerDevPixel(); |
|
649 damagedRect = damagedRect.ConvertAppUnitsRoundOut(APD, rootAPD); |
|
650 |
|
651 // accumulate this rectangle in the view's dirty region, so we can |
|
652 // process it later. |
|
653 AddDirtyRegion(displayRoot, nsRegion(damagedRect)); |
|
654 } |
|
655 |
|
656 void |
|
657 nsViewManager::InvalidateAllViews() |
|
658 { |
|
659 if (RootViewManager() != this) { |
|
660 return RootViewManager()->InvalidateAllViews(); |
|
661 } |
|
662 |
|
663 InvalidateViews(mRootView); |
|
664 } |
|
665 |
|
666 void nsViewManager::InvalidateViews(nsView *aView) |
|
667 { |
|
668 // Invalidate this view. |
|
669 InvalidateView(aView); |
|
670 |
|
671 // Invalidate all children as well. |
|
672 nsView* childView = aView->GetFirstChild(); |
|
673 while (nullptr != childView) { |
|
674 childView->GetViewManager()->InvalidateViews(childView); |
|
675 childView = childView->GetNextSibling(); |
|
676 } |
|
677 } |
|
678 |
|
679 void nsViewManager::WillPaintWindow(nsIWidget* aWidget) |
|
680 { |
|
681 if (aWidget) { |
|
682 nsView* view = nsView::GetViewFor(aWidget); |
|
683 LayerManager *manager = aWidget->GetLayerManager(); |
|
684 if (view && |
|
685 (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) { |
|
686 ProcessPendingUpdates(); |
|
687 // Re-get the view pointer here since the ProcessPendingUpdates might have |
|
688 // destroyed it during CallWillPaintOnObservers. |
|
689 view = nsView::GetViewFor(aWidget); |
|
690 if (view) { |
|
691 view->SetForcedRepaint(false); |
|
692 } |
|
693 } |
|
694 } |
|
695 |
|
696 nsCOMPtr<nsIPresShell> shell = mPresShell; |
|
697 if (shell) { |
|
698 shell->WillPaintWindow(); |
|
699 } |
|
700 } |
|
701 |
|
702 bool nsViewManager::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) |
|
703 { |
|
704 if (!aWidget || !mContext) |
|
705 return false; |
|
706 |
|
707 NS_ASSERTION(IsPaintingAllowed(), |
|
708 "shouldn't be receiving paint events while painting is disallowed!"); |
|
709 |
|
710 // Get the view pointer here since NS_WILL_PAINT might have |
|
711 // destroyed it during CallWillPaintOnObservers (bug 378273). |
|
712 nsView* view = nsView::GetViewFor(aWidget); |
|
713 if (view && !aRegion.IsEmpty()) { |
|
714 Refresh(view, aRegion); |
|
715 } |
|
716 |
|
717 return true; |
|
718 } |
|
719 |
|
720 void nsViewManager::DidPaintWindow() |
|
721 { |
|
722 nsCOMPtr<nsIPresShell> shell = mPresShell; |
|
723 if (shell) { |
|
724 shell->DidPaintWindow(); |
|
725 } |
|
726 } |
|
727 |
|
728 void |
|
729 nsViewManager::DispatchEvent(WidgetGUIEvent *aEvent, |
|
730 nsView* aView, |
|
731 nsEventStatus* aStatus) |
|
732 { |
|
733 PROFILER_LABEL("event", "nsViewManager::DispatchEvent"); |
|
734 |
|
735 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
|
736 if ((mouseEvent && |
|
737 // Ignore mouse events that we synthesize. |
|
738 mouseEvent->reason == WidgetMouseEvent::eReal && |
|
739 // Ignore mouse exit and enter (we'll get moves if the user |
|
740 // is really moving the mouse) since we get them when we |
|
741 // create and destroy widgets. |
|
742 mouseEvent->message != NS_MOUSE_EXIT && |
|
743 mouseEvent->message != NS_MOUSE_ENTER) || |
|
744 aEvent->HasKeyEventMessage() || |
|
745 aEvent->HasIMEEventMessage() || |
|
746 aEvent->message == NS_PLUGIN_INPUT_EVENT) { |
|
747 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow()); |
|
748 } |
|
749 |
|
750 // Find the view whose coordinates system we're in. |
|
751 nsView* view = aView; |
|
752 bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates(); |
|
753 if (dispatchUsingCoordinates) { |
|
754 // Will dispatch using coordinates. Pretty bogus but it's consistent |
|
755 // with what presshell does. |
|
756 view = GetDisplayRootFor(view); |
|
757 } |
|
758 |
|
759 // If the view has no frame, look for a view that does. |
|
760 nsIFrame* frame = view->GetFrame(); |
|
761 if (!frame && |
|
762 (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() || |
|
763 aEvent->IsIMERelatedEvent() || |
|
764 aEvent->IsNonRetargetedNativeEventDelivererForPlugin() || |
|
765 aEvent->HasPluginActivationEventMessage() || |
|
766 aEvent->message == NS_PLUGIN_RESOLUTION_CHANGED)) { |
|
767 while (view && !view->GetFrame()) { |
|
768 view = view->GetParent(); |
|
769 } |
|
770 |
|
771 if (view) { |
|
772 frame = view->GetFrame(); |
|
773 } |
|
774 } |
|
775 |
|
776 if (nullptr != frame) { |
|
777 // Hold a refcount to the presshell. The continued existence of the |
|
778 // presshell will delay deletion of this view hierarchy should the event |
|
779 // want to cause its destruction in, say, some JavaScript event handler. |
|
780 nsCOMPtr<nsIPresShell> shell = view->GetViewManager()->GetPresShell(); |
|
781 if (shell) { |
|
782 shell->HandleEvent(frame, aEvent, false, aStatus); |
|
783 return; |
|
784 } |
|
785 } |
|
786 |
|
787 *aStatus = nsEventStatus_eIgnore; |
|
788 } |
|
789 |
|
790 // Recursively reparent widgets if necessary |
|
791 |
|
792 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget *aNewWidget) |
|
793 { |
|
794 NS_PRECONDITION(aNewWidget, ""); |
|
795 |
|
796 if (aView->HasWidget()) { |
|
797 // Check to see if the parent widget is the |
|
798 // same as the new parent. If not then reparent |
|
799 // the widget, otherwise there is nothing more |
|
800 // to do for the view and its descendants |
|
801 nsIWidget* widget = aView->GetWidget(); |
|
802 nsIWidget* parentWidget = widget->GetParent(); |
|
803 if (parentWidget) { |
|
804 // Child widget |
|
805 if (parentWidget != aNewWidget) { |
|
806 #ifdef DEBUG |
|
807 nsresult rv = |
|
808 #endif |
|
809 widget->SetParent(aNewWidget); |
|
810 NS_ASSERTION(NS_SUCCEEDED(rv), "SetParent failed!"); |
|
811 } |
|
812 } else { |
|
813 // Toplevel widget (popup, dialog, etc) |
|
814 widget->ReparentNativeWidget(aNewWidget); |
|
815 } |
|
816 return; |
|
817 } |
|
818 |
|
819 // Need to check each of the views children to see |
|
820 // if they have a widget and reparent it. |
|
821 |
|
822 for (nsView *kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) { |
|
823 ReparentChildWidgets(kid, aNewWidget); |
|
824 } |
|
825 } |
|
826 |
|
827 // Reparent a view and its descendant views widgets if necessary |
|
828 |
|
829 void nsViewManager::ReparentWidgets(nsView* aView, nsView *aParent) |
|
830 { |
|
831 NS_PRECONDITION(aParent, "Must have a parent"); |
|
832 NS_PRECONDITION(aView, "Must have a view"); |
|
833 |
|
834 // Quickly determine whether the view has pre-existing children or a |
|
835 // widget. In most cases the view will not have any pre-existing |
|
836 // children when this is called. Only in the case |
|
837 // where a view has been reparented by removing it from |
|
838 // a reinserting it into a new location in the view hierarchy do we |
|
839 // have to consider reparenting the existing widgets for the view and |
|
840 // it's descendants. |
|
841 if (aView->HasWidget() || aView->GetFirstChild()) { |
|
842 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr); |
|
843 if (parentWidget) { |
|
844 ReparentChildWidgets(aView, parentWidget); |
|
845 return; |
|
846 } |
|
847 NS_WARNING("Can not find a widget for the parent view"); |
|
848 } |
|
849 } |
|
850 |
|
851 void |
|
852 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, nsView *aSibling, |
|
853 bool aAfter) |
|
854 { |
|
855 NS_PRECONDITION(nullptr != aParent, "null ptr"); |
|
856 NS_PRECONDITION(nullptr != aChild, "null ptr"); |
|
857 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent, |
|
858 "tried to insert view with invalid sibling"); |
|
859 NS_ASSERTION(!IsViewInserted(aChild), "tried to insert an already-inserted view"); |
|
860 |
|
861 if ((nullptr != aParent) && (nullptr != aChild)) |
|
862 { |
|
863 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document |
|
864 // order, otherwise after 'kid' (i.e. before 'kid' in document order). |
|
865 |
|
866 if (nullptr == aSibling) { |
|
867 if (aAfter) { |
|
868 // insert at end of document order, i.e., before first view |
|
869 // this is the common case, by far |
|
870 aParent->InsertChild(aChild, nullptr); |
|
871 ReparentWidgets(aChild, aParent); |
|
872 } else { |
|
873 // insert at beginning of document order, i.e., after last view |
|
874 nsView *kid = aParent->GetFirstChild(); |
|
875 nsView *prev = nullptr; |
|
876 while (kid) { |
|
877 prev = kid; |
|
878 kid = kid->GetNextSibling(); |
|
879 } |
|
880 // prev is last view or null if there are no children |
|
881 aParent->InsertChild(aChild, prev); |
|
882 ReparentWidgets(aChild, aParent); |
|
883 } |
|
884 } else { |
|
885 nsView *kid = aParent->GetFirstChild(); |
|
886 nsView *prev = nullptr; |
|
887 while (kid && aSibling != kid) { |
|
888 //get the next sibling view |
|
889 prev = kid; |
|
890 kid = kid->GetNextSibling(); |
|
891 } |
|
892 NS_ASSERTION(kid != nullptr, |
|
893 "couldn't find sibling in child list"); |
|
894 if (aAfter) { |
|
895 // insert after 'kid' in document order, i.e. before in view order |
|
896 aParent->InsertChild(aChild, prev); |
|
897 ReparentWidgets(aChild, aParent); |
|
898 } else { |
|
899 // insert before 'kid' in document order, i.e. after in view order |
|
900 aParent->InsertChild(aChild, kid); |
|
901 ReparentWidgets(aChild, aParent); |
|
902 } |
|
903 } |
|
904 |
|
905 // if the parent view is marked as "floating", make the newly added view float as well. |
|
906 if (aParent->GetFloating()) |
|
907 aChild->SetFloating(true); |
|
908 } |
|
909 } |
|
910 |
|
911 void |
|
912 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, int32_t aZIndex) |
|
913 { |
|
914 // no-one really calls this with anything other than aZIndex == 0 on a fresh view |
|
915 // XXX this method should simply be eliminated and its callers redirected to the real method |
|
916 SetViewZIndex(aChild, false, aZIndex); |
|
917 InsertChild(aParent, aChild, nullptr, true); |
|
918 } |
|
919 |
|
920 void |
|
921 nsViewManager::RemoveChild(nsView *aChild) |
|
922 { |
|
923 NS_ASSERTION(aChild, "aChild must not be null"); |
|
924 |
|
925 nsView* parent = aChild->GetParent(); |
|
926 |
|
927 if (nullptr != parent) { |
|
928 NS_ASSERTION(aChild->GetViewManager() == this || |
|
929 parent->GetViewManager() == this, "wrong view manager"); |
|
930 parent->RemoveChild(aChild); |
|
931 } |
|
932 } |
|
933 |
|
934 void |
|
935 nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY) |
|
936 { |
|
937 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); |
|
938 aView->SetPosition(aX, aY); |
|
939 } |
|
940 |
|
941 void |
|
942 nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly) |
|
943 { |
|
944 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); |
|
945 |
|
946 nsRect oldDimensions = aView->GetDimensions(); |
|
947 if (!oldDimensions.IsEqualEdges(aRect)) { |
|
948 aView->SetDimensions(aRect, true); |
|
949 } |
|
950 |
|
951 // Note that if layout resizes the view and the view has a custom clip |
|
952 // region set, then we expect layout to update the clip region too. Thus |
|
953 // in the case where mClipRect has been optimized away to just be a null |
|
954 // pointer, and this resize is implicitly changing the clip rect, it's OK |
|
955 // because layout will change it back again if necessary. |
|
956 } |
|
957 |
|
958 void |
|
959 nsViewManager::SetViewFloating(nsView *aView, bool aFloating) |
|
960 { |
|
961 NS_ASSERTION(!(nullptr == aView), "no view"); |
|
962 |
|
963 aView->SetFloating(aFloating); |
|
964 } |
|
965 |
|
966 void |
|
967 nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible) |
|
968 { |
|
969 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); |
|
970 |
|
971 if (aVisible != aView->GetVisibility()) { |
|
972 aView->SetVisibility(aVisible); |
|
973 } |
|
974 } |
|
975 |
|
976 bool nsViewManager::IsViewInserted(nsView *aView) |
|
977 { |
|
978 if (mRootView == aView) { |
|
979 return true; |
|
980 } else if (aView->GetParent() == nullptr) { |
|
981 return false; |
|
982 } else { |
|
983 nsView* view = aView->GetParent()->GetFirstChild(); |
|
984 while (view != nullptr) { |
|
985 if (view == aView) { |
|
986 return true; |
|
987 } |
|
988 view = view->GetNextSibling(); |
|
989 } |
|
990 return false; |
|
991 } |
|
992 } |
|
993 |
|
994 void |
|
995 nsViewManager::SetViewZIndex(nsView *aView, bool aAutoZIndex, int32_t aZIndex) |
|
996 { |
|
997 NS_ASSERTION((aView != nullptr), "no view"); |
|
998 |
|
999 // don't allow the root view's z-index to be changed. It should always be zero. |
|
1000 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences |
|
1001 if (aView == mRootView) { |
|
1002 return; |
|
1003 } |
|
1004 |
|
1005 if (aAutoZIndex) { |
|
1006 aZIndex = 0; |
|
1007 } |
|
1008 |
|
1009 aView->SetZIndex(aAutoZIndex, aZIndex); |
|
1010 } |
|
1011 |
|
1012 nsViewManager* |
|
1013 nsViewManager::IncrementDisableRefreshCount() |
|
1014 { |
|
1015 if (!IsRootVM()) { |
|
1016 return RootViewManager()->IncrementDisableRefreshCount(); |
|
1017 } |
|
1018 |
|
1019 ++mRefreshDisableCount; |
|
1020 |
|
1021 return this; |
|
1022 } |
|
1023 |
|
1024 void |
|
1025 nsViewManager::DecrementDisableRefreshCount() |
|
1026 { |
|
1027 NS_ASSERTION(IsRootVM(), "Should only be called on root"); |
|
1028 --mRefreshDisableCount; |
|
1029 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!"); |
|
1030 } |
|
1031 |
|
1032 void |
|
1033 nsViewManager::GetRootWidget(nsIWidget **aWidget) |
|
1034 { |
|
1035 if (!mRootView) { |
|
1036 *aWidget = nullptr; |
|
1037 return; |
|
1038 } |
|
1039 if (mRootView->HasWidget()) { |
|
1040 *aWidget = mRootView->GetWidget(); |
|
1041 NS_ADDREF(*aWidget); |
|
1042 return; |
|
1043 } |
|
1044 if (mRootView->GetParent()) { |
|
1045 mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget); |
|
1046 return; |
|
1047 } |
|
1048 *aWidget = nullptr; |
|
1049 } |
|
1050 |
|
1051 nsIntRect nsViewManager::ViewToWidget(nsView *aView, const nsRect &aRect) const |
|
1052 { |
|
1053 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); |
|
1054 |
|
1055 // account for the view's origin not lining up with the widget's |
|
1056 nsRect rect = aRect + aView->ViewToWidgetOffset(); |
|
1057 |
|
1058 // finally, convert to device coordinates. |
|
1059 return rect.ToOutsidePixels(AppUnitsPerDevPixel()); |
|
1060 } |
|
1061 |
|
1062 void |
|
1063 nsViewManager::IsPainting(bool& aIsPainting) |
|
1064 { |
|
1065 aIsPainting = IsPainting(); |
|
1066 } |
|
1067 |
|
1068 void |
|
1069 nsViewManager::ProcessPendingUpdates() |
|
1070 { |
|
1071 if (!IsRootVM()) { |
|
1072 RootViewManager()->ProcessPendingUpdates(); |
|
1073 return; |
|
1074 } |
|
1075 |
|
1076 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush(); |
|
1077 |
|
1078 // Flush things like reflows by calling WillPaint on observer presShells. |
|
1079 if (mPresShell) { |
|
1080 CallWillPaintOnObservers(); |
|
1081 } |
|
1082 ProcessPendingUpdatesForView(mRootView, true); |
|
1083 } |
|
1084 |
|
1085 void |
|
1086 nsViewManager::UpdateWidgetGeometry() |
|
1087 { |
|
1088 if (!IsRootVM()) { |
|
1089 RootViewManager()->UpdateWidgetGeometry(); |
|
1090 return; |
|
1091 } |
|
1092 |
|
1093 if (mHasPendingWidgetGeometryChanges) { |
|
1094 mHasPendingWidgetGeometryChanges = false; |
|
1095 ProcessPendingUpdatesForView(mRootView, false); |
|
1096 } |
|
1097 } |
|
1098 |
|
1099 void |
|
1100 nsViewManager::CallWillPaintOnObservers() |
|
1101 { |
|
1102 NS_PRECONDITION(IsRootVM(), "Must be root VM for this to be called!"); |
|
1103 |
|
1104 int32_t index; |
|
1105 for (index = 0; index < mVMCount; index++) { |
|
1106 nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index); |
|
1107 if (vm->RootViewManager() == this) { |
|
1108 // One of our kids. |
|
1109 if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) { |
|
1110 nsCOMPtr<nsIPresShell> shell = vm->GetPresShell(); |
|
1111 if (shell) { |
|
1112 shell->WillPaint(); |
|
1113 } |
|
1114 } |
|
1115 } |
|
1116 } |
|
1117 } |
|
1118 |
|
1119 void |
|
1120 nsViewManager::GetLastUserEventTime(uint32_t& aTime) |
|
1121 { |
|
1122 aTime = gLastUserEventTime; |
|
1123 } |
|
1124 |
|
1125 void |
|
1126 nsViewManager::InvalidateHierarchy() |
|
1127 { |
|
1128 if (mRootView) { |
|
1129 if (!IsRootVM()) { |
|
1130 NS_RELEASE(mRootViewManager); |
|
1131 } |
|
1132 nsView *parent = mRootView->GetParent(); |
|
1133 if (parent) { |
|
1134 mRootViewManager = parent->GetViewManager()->RootViewManager(); |
|
1135 NS_ADDREF(mRootViewManager); |
|
1136 NS_ASSERTION(mRootViewManager != this, |
|
1137 "Root view had a parent, but it has the same view manager"); |
|
1138 } else { |
|
1139 mRootViewManager = this; |
|
1140 } |
|
1141 } |
|
1142 } |