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