Bug 1601859 - Vendor cubeb-pulse-rs. r=kinetik
[gecko.git] / view / nsView.cpp
blob29fb909e5679a2276fc9a0109544c9b30dcd71e3
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/. */
6 #include "nsView.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/BasicEvents.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/IntegerPrintfMacros.h"
12 #include "mozilla/Likely.h"
13 #include "mozilla/Poison.h"
14 #include "mozilla/PresShell.h"
15 #include "mozilla/StaticPrefs_layout.h"
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/BrowserParent.h"
18 #include "nsIWidget.h"
19 #include "nsViewManager.h"
20 #include "nsIFrame.h"
21 #include "nsPresArena.h"
22 #include "nsXULPopupManager.h"
23 #include "nsIWidgetListener.h"
24 #include "nsContentUtils.h" // for nsAutoScriptBlocker
25 #include "mozilla/TimelineConsumers.h"
26 #include "mozilla/CompositeTimelineMarker.h"
28 using namespace mozilla;
30 nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility)
31 : mViewManager(aViewManager),
32 mParent(nullptr),
33 mNextSibling(nullptr),
34 mFirstChild(nullptr),
35 mFrame(nullptr),
36 mDirtyRegion(nullptr),
37 mZIndex(0),
38 mVis(aVisibility),
39 mPosX(0),
40 mPosY(0),
41 mVFlags(0),
42 mWidgetIsTopLevel(false),
43 mForcedRepaint(false),
44 mNeedsWindowPropertiesSync(false) {
45 MOZ_COUNT_CTOR(nsView);
47 // Views should be transparent by default. Not being transparent is
48 // a promise that the view will paint all its pixels opaquely. Views
49 // should make this promise explicitly by calling
50 // SetViewContentTransparency.
53 void nsView::DropMouseGrabbing() {
54 if (mViewManager->GetPresShell()) {
55 PresShell::ClearMouseCaptureOnView(this);
59 nsView::~nsView() {
60 MOZ_COUNT_DTOR(nsView);
62 while (GetFirstChild()) {
63 nsView* child = GetFirstChild();
64 if (child->GetViewManager() == mViewManager) {
65 child->Destroy();
66 } else {
67 // just unhook it. Someone else will want to destroy this.
68 RemoveChild(child);
72 if (mViewManager) {
73 DropMouseGrabbing();
75 nsView* rootView = mViewManager->GetRootView();
77 if (rootView) {
78 // Root views can have parents!
79 if (mParent) {
80 mViewManager->RemoveChild(this);
83 if (rootView == this) {
84 // Inform the view manager that the root view has gone away...
85 mViewManager->SetRootView(nullptr);
87 } else if (mParent) {
88 mParent->RemoveChild(this);
91 mViewManager = nullptr;
92 } else if (mParent) {
93 mParent->RemoveChild(this);
96 if (mPreviousWindow) {
97 mPreviousWindow->SetPreviouslyAttachedWidgetListener(nullptr);
100 // Destroy and release the widget
101 DestroyWidget();
103 MOZ_RELEASE_ASSERT(!mFrame);
105 delete mDirtyRegion;
108 class DestroyWidgetRunnable : public Runnable {
109 public:
110 NS_DECL_NSIRUNNABLE
112 explicit DestroyWidgetRunnable(nsIWidget* aWidget)
113 : mozilla::Runnable("DestroyWidgetRunnable"), mWidget(aWidget) {}
115 private:
116 nsCOMPtr<nsIWidget> mWidget;
119 NS_IMETHODIMP DestroyWidgetRunnable::Run() {
120 mWidget->Destroy();
121 mWidget = nullptr;
122 return NS_OK;
125 void nsView::DestroyWidget() {
126 if (mWindow) {
127 // If we are not attached to a base window, we're going to tear down our
128 // widget here. However, if we're attached to somebody elses widget, we
129 // want to leave the widget alone: don't reset the client data or call
130 // Destroy. Just clear our event view ptr and free our reference to it.
131 if (mWidgetIsTopLevel) {
132 mWindow->SetAttachedWidgetListener(nullptr);
133 } else {
134 mWindow->SetWidgetListener(nullptr);
136 nsCOMPtr<nsIRunnable> widgetDestroyer =
137 new DestroyWidgetRunnable(mWindow);
139 // Don't leak if we happen to arrive here after the main thread
140 // has disappeared.
141 nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
142 if (mainThread) {
143 mainThread->Dispatch(widgetDestroyer.forget(), NS_DISPATCH_NORMAL);
147 mWindow = nullptr;
151 nsView* nsView::GetViewFor(nsIWidget* aWidget) {
152 MOZ_ASSERT(nullptr != aWidget, "null widget ptr");
154 nsIWidgetListener* listener = aWidget->GetWidgetListener();
155 if (listener) {
156 nsView* view = listener->GetView();
157 if (view) return view;
160 listener = aWidget->GetAttachedWidgetListener();
161 return listener ? listener->GetView() : nullptr;
164 void nsView::Destroy() {
165 this->~nsView();
166 mozWritePoison(this, sizeof(*this));
167 nsView::operator delete(this);
170 void nsView::SetPosition(nscoord aX, nscoord aY) {
171 mDimBounds.MoveBy(aX - mPosX, aY - mPosY);
172 mPosX = aX;
173 mPosY = aY;
175 NS_ASSERTION(GetParent() || (aX == 0 && aY == 0),
176 "Don't try to move the root widget to something non-zero");
178 ResetWidgetBounds(true, false);
181 void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync) {
182 if (mWindow) {
183 if (!aForceSync) {
184 // Don't change widget geometry synchronously, since that can
185 // cause synchronous painting.
186 mViewManager->PostPendingUpdate();
187 } else {
188 DoResetWidgetBounds(false, true);
190 return;
193 if (aRecurse) {
194 // reposition any widgets under this view
195 for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) {
196 v->ResetWidgetBounds(true, aForceSync);
201 bool nsView::IsEffectivelyVisible() {
202 for (nsView* v = this; v; v = v->mParent) {
203 if (v->GetVisibility() == nsViewVisibility_kHide) return false;
205 return true;
208 LayoutDeviceIntRect nsView::CalcWidgetBounds(nsWindowType aType) {
209 int32_t p2a = mViewManager->AppUnitsPerDevPixel();
211 nsRect viewBounds(mDimBounds);
213 nsView* parent = GetParent();
214 nsIWidget* parentWidget = nullptr;
215 if (parent) {
216 nsPoint offset;
217 parentWidget = parent->GetNearestWidget(&offset, p2a);
218 // make viewBounds be relative to the parent widget, in appunits
219 viewBounds += offset;
221 if (parentWidget && aType == eWindowType_popup && IsEffectivelyVisible()) {
222 // put offset into screen coordinates. (based on client area origin)
223 LayoutDeviceIntPoint screenPoint = parentWidget->WidgetToScreenOffset();
224 viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a),
225 NSIntPixelsToAppUnits(screenPoint.y, p2a));
229 // Compute widget bounds in device pixels
230 LayoutDeviceIntRect newBounds =
231 LayoutDeviceIntRect::FromUnknownRect(viewBounds.ToNearestPixels(p2a));
233 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
234 // cocoa and GTK round widget coordinates to the nearest global "display
235 // pixel" integer value. So we avoid fractional display pixel values by
236 // rounding to the nearest value that won't yield a fractional display pixel.
237 nsIWidget* widget = parentWidget ? parentWidget : mWindow.get();
238 uint32_t round;
239 if (aType == eWindowType_popup && widget &&
240 ((round = widget->RoundsWidgetCoordinatesTo()) > 1)) {
241 LayoutDeviceIntSize pixelRoundedSize = newBounds.Size();
242 // round the top left and bottom right to the nearest round pixel
243 newBounds.x =
244 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.x, p2a) / round) *
245 round;
246 newBounds.y =
247 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.y, p2a) / round) *
248 round;
249 newBounds.width =
250 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.XMost(), p2a) /
251 round) *
252 round -
253 newBounds.x;
254 newBounds.height =
255 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.YMost(), p2a) /
256 round) *
257 round -
258 newBounds.y;
259 // but if that makes the widget larger then our frame may not paint the
260 // extra pixels, so reduce the size to the nearest round value
261 if (newBounds.width > pixelRoundedSize.width) {
262 newBounds.width -= round;
264 if (newBounds.height > pixelRoundedSize.height) {
265 newBounds.height -= round;
268 #endif
270 // Compute where the top-left of our widget ended up relative to the parent
271 // widget, in appunits.
272 nsPoint roundedOffset(NSIntPixelsToAppUnits(newBounds.X(), p2a),
273 NSIntPixelsToAppUnits(newBounds.Y(), p2a));
275 // mViewToWidgetOffset is added to coordinates relative to the view origin
276 // to get coordinates relative to the widget.
277 // The view origin, relative to the parent widget, is at
278 // (mPosX,mPosY) - mDimBounds.TopLeft() + viewBounds.TopLeft().
279 // Our widget, relative to the parent widget, is roundedOffset.
280 mViewToWidgetOffset = nsPoint(mPosX, mPosY) - mDimBounds.TopLeft() +
281 viewBounds.TopLeft() - roundedOffset;
283 return newBounds;
286 void nsView::DoResetWidgetBounds(bool aMoveOnly, bool aInvalidateChangedSize) {
287 // The geometry of a root view's widget is controlled externally,
288 // NOT by sizing or positioning the view
289 if (mViewManager->GetRootView() == this) {
290 return;
293 MOZ_ASSERT(mWindow, "Why was this called??");
295 // Hold this ref to make sure it stays alive.
296 nsCOMPtr<nsIWidget> widget = mWindow;
298 // Stash a copy of these and use them so we can handle this being deleted (say
299 // from sync painting/flushing from Show/Move/Resize on the widget).
300 LayoutDeviceIntRect newBounds;
302 nsWindowType type = widget->WindowType();
304 LayoutDeviceIntRect curBounds = widget->GetClientBounds();
305 bool invisiblePopup = type == eWindowType_popup &&
306 ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) ||
307 mVis == nsViewVisibility_kHide);
309 if (invisiblePopup) {
310 // We're going to hit the early exit below, avoid calling CalcWidgetBounds.
311 } else {
312 newBounds = CalcWidgetBounds(type);
313 invisiblePopup = newBounds.IsEmpty();
316 bool curVisibility = widget->IsVisible();
317 bool newVisibility = !invisiblePopup && IsEffectivelyVisible();
318 if (curVisibility && !newVisibility) {
319 widget->Show(false);
322 if (invisiblePopup) {
323 // Don't manipulate empty or hidden popup widgets. For example there's no
324 // point moving hidden comboboxes around, or doing X server roundtrips
325 // to compute their true screen position. This could mean that
326 // WidgetToScreen operations on these widgets don't return up-to-date
327 // values, but popup positions aren't reliable anyway because of correction
328 // to be on or off-screen.
329 return;
332 bool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
333 bool changedSize = curBounds.Size() != newBounds.Size();
335 // Child views are never attached to top level widgets, this is safe.
337 // Coordinates are converted to desktop pixels for window Move/Resize APIs,
338 // because of the potential for device-pixel coordinate spaces for mixed
339 // hidpi/lodpi screens to overlap each other and result in bad placement
340 // (bug 814434).
342 DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScaleByScreen();
344 DesktopRect deskRect = newBounds / scale;
345 if (changedPos) {
346 if (changedSize && !aMoveOnly) {
347 widget->ResizeClient(deskRect.X(), deskRect.Y(), deskRect.Width(),
348 deskRect.Height(), aInvalidateChangedSize);
349 } else {
350 widget->MoveClient(deskRect.X(), deskRect.Y());
352 } else {
353 if (changedSize && !aMoveOnly) {
354 widget->ResizeClient(deskRect.Width(), deskRect.Height(),
355 aInvalidateChangedSize);
356 } // else do nothing!
359 if (!curVisibility && newVisibility) {
360 widget->Show(true);
364 void nsView::SetDimensions(const nsRect& aRect, bool aPaint,
365 bool aResizeWidget) {
366 nsRect dims = aRect;
367 dims.MoveBy(mPosX, mPosY);
369 // Don't use nsRect's operator== here, since it returns true when
370 // both rects are empty even if they have different widths and we
371 // have cases where that sort of thing matters to us.
372 if (mDimBounds.TopLeft() == dims.TopLeft() &&
373 mDimBounds.Size() == dims.Size()) {
374 return;
377 mDimBounds = dims;
379 if (aResizeWidget) {
380 ResetWidgetBounds(false, false);
384 void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible) {
385 if (!aEffectivelyVisible) {
386 DropMouseGrabbing();
389 SetForcedRepaint(true);
391 if (nullptr != mWindow) {
392 ResetWidgetBounds(false, false);
395 for (nsView* child = mFirstChild; child; child = child->mNextSibling) {
396 if (child->mVis == nsViewVisibility_kHide) {
397 // It was effectively hidden and still is
398 continue;
400 // Our child is visible if we are
401 child->NotifyEffectiveVisibilityChanged(aEffectivelyVisible);
405 void nsView::SetVisibility(nsViewVisibility aVisibility) {
406 mVis = aVisibility;
407 NotifyEffectiveVisibilityChanged(IsEffectivelyVisible());
410 void nsView::SetFloating(bool aFloatingView) {
411 if (aFloatingView)
412 mVFlags |= NS_VIEW_FLAG_FLOATING;
413 else
414 mVFlags &= ~NS_VIEW_FLAG_FLOATING;
417 void nsView::InvalidateHierarchy() {
418 if (mViewManager->GetRootView() == this) mViewManager->InvalidateHierarchy();
420 for (nsView* child = mFirstChild; child; child = child->GetNextSibling())
421 child->InvalidateHierarchy();
424 void nsView::InsertChild(nsView* aChild, nsView* aSibling) {
425 MOZ_ASSERT(nullptr != aChild, "null ptr");
427 if (nullptr != aChild) {
428 if (nullptr != aSibling) {
429 #ifdef DEBUG
430 NS_ASSERTION(aSibling->GetParent() == this,
431 "tried to insert view with invalid sibling");
432 #endif
433 // insert after sibling
434 aChild->SetNextSibling(aSibling->GetNextSibling());
435 aSibling->SetNextSibling(aChild);
436 } else {
437 aChild->SetNextSibling(mFirstChild);
438 mFirstChild = aChild;
440 aChild->SetParent(this);
442 // If we just inserted a root view, then update the RootViewManager
443 // on all view managers in the new subtree.
445 nsViewManager* vm = aChild->GetViewManager();
446 if (vm->GetRootView() == aChild) {
447 aChild->InvalidateHierarchy();
452 void nsView::RemoveChild(nsView* child) {
453 MOZ_ASSERT(nullptr != child, "null ptr");
455 if (nullptr != child) {
456 nsView* prevKid = nullptr;
457 nsView* kid = mFirstChild;
458 DebugOnly<bool> found = false;
459 while (nullptr != kid) {
460 if (kid == child) {
461 if (nullptr != prevKid) {
462 prevKid->SetNextSibling(kid->GetNextSibling());
463 } else {
464 mFirstChild = kid->GetNextSibling();
466 child->SetParent(nullptr);
467 found = true;
468 break;
470 prevKid = kid;
471 kid = kid->GetNextSibling();
473 NS_ASSERTION(found, "tried to remove non child");
475 // If we just removed a root view, then update the RootViewManager
476 // on all view managers in the removed subtree.
478 nsViewManager* vm = child->GetViewManager();
479 if (vm->GetRootView() == child) {
480 child->InvalidateHierarchy();
485 // Native widgets ultimately just can't deal with the awesome power of
486 // CSS2 z-index. However, we set the z-index on the widget anyway
487 // because in many simple common cases the widgets do end up in the
488 // right order. We set each widget's z-index to the z-index of the
489 // nearest ancestor that has non-auto z-index.
490 static void UpdateNativeWidgetZIndexes(nsView* aView, int32_t aZIndex) {
491 if (aView->HasWidget()) {
492 nsIWidget* widget = aView->GetWidget();
493 if (widget->GetZIndex() != aZIndex) {
494 widget->SetZIndex(aZIndex);
496 } else {
497 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
498 if (v->GetZIndexIsAuto()) {
499 UpdateNativeWidgetZIndexes(v, aZIndex);
505 static int32_t FindNonAutoZIndex(nsView* aView) {
506 while (aView) {
507 if (!aView->GetZIndexIsAuto()) {
508 return aView->GetZIndex();
510 aView = aView->GetParent();
512 return 0;
515 struct DefaultWidgetInitData : public nsWidgetInitData {
516 DefaultWidgetInitData() : nsWidgetInitData() {
517 mWindowType = eWindowType_child;
518 clipChildren = true;
519 clipSiblings = true;
523 nsresult nsView::CreateWidget(nsWidgetInitData* aWidgetInitData,
524 bool aEnableDragDrop, bool aResetVisibility) {
525 AssertNoWindow();
526 MOZ_ASSERT(
527 !aWidgetInitData || aWidgetInitData->mWindowType != eWindowType_popup,
528 "Use CreateWidgetForPopup");
530 DefaultWidgetInitData defaultInitData;
531 bool initDataPassedIn = !!aWidgetInitData;
532 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
533 defaultInitData.mListenForResizes =
534 (!initDataPassedIn && GetParent() &&
535 GetParent()->GetViewManager() != mViewManager);
537 LayoutDeviceIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
539 nsIWidget* parentWidget =
540 GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr;
541 if (!parentWidget) {
542 NS_ERROR("nsView::CreateWidget without suitable parent widget??");
543 return NS_ERROR_FAILURE;
546 // XXX: using aForceUseIWidgetParent=true to preserve previous
547 // semantics. It's not clear that it's actually needed.
548 mWindow = parentWidget->CreateChild(trect, aWidgetInitData, true);
549 if (!mWindow) {
550 return NS_ERROR_FAILURE;
553 InitializeWindow(aEnableDragDrop, aResetVisibility);
555 return NS_OK;
558 nsresult nsView::CreateWidgetForParent(nsIWidget* aParentWidget,
559 nsWidgetInitData* aWidgetInitData,
560 bool aEnableDragDrop,
561 bool aResetVisibility) {
562 AssertNoWindow();
563 MOZ_ASSERT(
564 !aWidgetInitData || aWidgetInitData->mWindowType != eWindowType_popup,
565 "Use CreateWidgetForPopup");
566 MOZ_ASSERT(aParentWidget, "Parent widget required");
568 DefaultWidgetInitData defaultInitData;
569 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
571 LayoutDeviceIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
573 mWindow = aParentWidget->CreateChild(trect, aWidgetInitData);
574 if (!mWindow) {
575 return NS_ERROR_FAILURE;
578 InitializeWindow(aEnableDragDrop, aResetVisibility);
580 return NS_OK;
583 nsresult nsView::CreateWidgetForPopup(nsWidgetInitData* aWidgetInitData,
584 nsIWidget* aParentWidget,
585 bool aEnableDragDrop,
586 bool aResetVisibility) {
587 AssertNoWindow();
588 MOZ_ASSERT(aWidgetInitData, "Widget init data required");
589 MOZ_ASSERT(aWidgetInitData->mWindowType == eWindowType_popup,
590 "Use one of the other CreateWidget methods");
592 LayoutDeviceIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
594 // XXX/cjones: having these two separate creation cases seems ... um
595 // ... unnecessary, but it's the way the old code did it. Please
596 // unify them by first finding a suitable parent nsIWidget, then
597 // getting rid of aForceUseIWidgetParent.
598 if (aParentWidget) {
599 // XXX: using aForceUseIWidgetParent=true to preserve previous
600 // semantics. It's not clear that it's actually needed.
601 mWindow = aParentWidget->CreateChild(trect, aWidgetInitData, true);
602 } else {
603 nsIWidget* nearestParent =
604 GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr;
605 if (!nearestParent) {
606 // Without a parent, we can't make a popup. This can happen
607 // when printing
608 return NS_ERROR_FAILURE;
611 mWindow = nearestParent->CreateChild(trect, aWidgetInitData);
613 if (!mWindow) {
614 return NS_ERROR_FAILURE;
617 InitializeWindow(aEnableDragDrop, aResetVisibility);
619 return NS_OK;
622 void nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility) {
623 MOZ_ASSERT(mWindow, "Must have a window to initialize");
625 mWindow->SetWidgetListener(this);
627 if (aEnableDragDrop) {
628 mWindow->EnableDragDrop(true);
631 // propagate the z-index to the widget.
632 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
634 // make sure visibility state is accurate
636 if (aResetVisibility) {
637 SetVisibility(GetVisibility());
641 void nsView::SetNeedsWindowPropertiesSync() {
642 mNeedsWindowPropertiesSync = true;
643 if (mViewManager) {
644 mViewManager->PostPendingUpdate();
648 // Attach to a top level widget and start receiving mirrored events.
649 nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget) {
650 MOZ_ASSERT(nullptr != aWidget, "null widget ptr");
652 /// XXXjimm This is a temporary workaround to an issue w/document
653 // viewer (bug 513162).
654 nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener();
655 if (listener) {
656 nsView* oldView = listener->GetView();
657 if (oldView) {
658 oldView->DetachFromTopLevelWidget();
662 // Note, the previous device context will be released. Detaching
663 // will not restore the old one.
664 aWidget->AttachViewToTopLevel(!nsIWidget::UsePuppetWidgets());
666 mWindow = aWidget;
668 mWindow->SetAttachedWidgetListener(this);
669 if (mWindow->WindowType() != eWindowType_invisible) {
670 nsresult rv = mWindow->AsyncEnableDragDrop(true);
671 NS_ENSURE_SUCCESS(rv, rv);
673 mWidgetIsTopLevel = true;
675 // Refresh the view bounds
676 CalcWidgetBounds(mWindow->WindowType());
678 return NS_OK;
681 // Detach this view from an attached widget.
682 nsresult nsView::DetachFromTopLevelWidget() {
683 MOZ_ASSERT(mWidgetIsTopLevel, "Not attached currently!");
684 MOZ_ASSERT(mWindow, "null mWindow for DetachFromTopLevelWidget!");
686 mWindow->SetAttachedWidgetListener(nullptr);
687 nsIWidgetListener* listener = mWindow->GetPreviouslyAttachedWidgetListener();
689 if (listener && listener->GetView()) {
690 // Ensure the listener doesn't think it's being used anymore
691 listener->GetView()->SetPreviousWidget(nullptr);
694 // If the new view's frame is paint suppressed then the window
695 // will want to use us instead until that's done
696 mWindow->SetPreviouslyAttachedWidgetListener(this);
698 mPreviousWindow = mWindow;
699 mWindow = nullptr;
701 mWidgetIsTopLevel = false;
703 return NS_OK;
706 void nsView::SetZIndex(bool aAuto, int32_t aZIndex) {
707 bool oldIsAuto = GetZIndexIsAuto();
708 mVFlags = (mVFlags & ~NS_VIEW_FLAG_AUTO_ZINDEX) |
709 (aAuto ? NS_VIEW_FLAG_AUTO_ZINDEX : 0);
710 mZIndex = aZIndex;
712 if (HasWidget() || !oldIsAuto || !aAuto) {
713 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
717 void nsView::AssertNoWindow() {
718 // XXX: it would be nice to make this a strong assert
719 if (MOZ_UNLIKELY(mWindow)) {
720 NS_ERROR("We already have a window for this view? BAD");
721 mWindow->SetWidgetListener(nullptr);
722 mWindow->Destroy();
723 mWindow = nullptr;
728 // internal window creation functions
730 void nsView::AttachWidgetEventHandler(nsIWidget* aWidget) {
731 #ifdef DEBUG
732 NS_ASSERTION(!aWidget->GetWidgetListener(), "Already have a widget listener");
733 #endif
735 aWidget->SetWidgetListener(this);
738 void nsView::DetachWidgetEventHandler(nsIWidget* aWidget) {
739 NS_ASSERTION(!aWidget->GetWidgetListener() ||
740 aWidget->GetWidgetListener()->GetView() == this,
741 "Wrong view");
742 aWidget->SetWidgetListener(nullptr);
745 #ifdef DEBUG
746 void nsView::List(FILE* out, int32_t aIndent) const {
747 int32_t i;
748 for (i = aIndent; --i >= 0;) fputs(" ", out);
749 fprintf(out, "%p ", (void*)this);
750 if (nullptr != mWindow) {
751 nscoord p2a = mViewManager->AppUnitsPerDevPixel();
752 LayoutDeviceIntRect rect = mWindow->GetClientBounds();
753 nsRect windowBounds = LayoutDeviceIntRect::ToAppUnits(rect, p2a);
754 rect = mWindow->GetBounds();
755 nsRect nonclientBounds = LayoutDeviceIntRect::ToAppUnits(rect, p2a);
756 nsrefcnt widgetRefCnt = mWindow.get()->AddRef() - 1;
757 mWindow.get()->Release();
758 int32_t Z = mWindow->GetZIndex();
759 fprintf(out, "(widget=%p[%" PRIuPTR "] z=%d pos={%d,%d,%d,%d}) ",
760 (void*)mWindow, widgetRefCnt, Z, nonclientBounds.X(),
761 nonclientBounds.Y(), windowBounds.Width(), windowBounds.Height());
763 nsRect brect = GetBounds();
764 fprintf(out, "{%d,%d,%d,%d} @ %d,%d", brect.X(), brect.Y(), brect.Width(),
765 brect.Height(), mPosX, mPosY);
766 fprintf(out, " flags=%x z=%d vis=%d frame=%p <\n", mVFlags, mZIndex, mVis,
767 static_cast<void*>(mFrame));
768 for (nsView* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
769 NS_ASSERTION(kid->GetParent() == this, "incorrect parent");
770 kid->List(out, aIndent + 1);
772 for (i = aIndent; --i >= 0;) fputs(" ", out);
773 fputs(">\n", out);
775 #endif // DEBUG
777 nsPoint nsView::GetOffsetTo(const nsView* aOther) const {
778 return GetOffsetTo(aOther, GetViewManager()->AppUnitsPerDevPixel());
781 nsPoint nsView::GetOffsetTo(const nsView* aOther, const int32_t aAPD) const {
782 MOZ_ASSERT(GetParent() || !aOther || aOther->GetParent() || this == aOther,
783 "caller of (outer) GetOffsetTo must not pass unrelated views");
784 // We accumulate the final result in offset
785 nsPoint offset(0, 0);
786 // The offset currently accumulated at the current APD
787 nsPoint docOffset(0, 0);
788 const nsView* v = this;
789 nsViewManager* currVM = v->GetViewManager();
790 int32_t currAPD = currVM->AppUnitsPerDevPixel();
791 const nsView* root = nullptr;
792 for (; v != aOther && v; root = v, v = v->GetParent()) {
793 nsViewManager* newVM = v->GetViewManager();
794 if (newVM != currVM) {
795 int32_t newAPD = newVM->AppUnitsPerDevPixel();
796 if (newAPD != currAPD) {
797 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
798 docOffset.x = docOffset.y = 0;
799 currAPD = newAPD;
801 currVM = newVM;
803 docOffset += v->GetPosition();
805 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
807 if (v != aOther) {
808 // Looks like aOther wasn't an ancestor of |this|. So now we have
809 // the root-VM-relative position of |this| in |offset|. Get the
810 // root-VM-relative position of aOther and subtract it.
811 nsPoint negOffset = aOther->GetOffsetTo(root, aAPD);
812 offset -= negOffset;
815 return offset;
818 nsPoint nsView::GetOffsetToWidget(nsIWidget* aWidget) const {
819 nsPoint pt;
820 // Get the view for widget
821 nsView* widgetView = GetViewFor(aWidget);
822 if (!widgetView) {
823 return pt;
826 // Get the offset to the widget view in the widget view's APD
827 // We get the offset in the widget view's APD first and then convert to our
828 // APD afterwards so that we can include the widget view's ViewToWidgetOffset
829 // in the sum in its native APD, and then convert the whole thing to our APD
830 // so that we don't have to convert the APD of the relatively small
831 // ViewToWidgetOffset by itself with a potentially large relative rounding
832 // error.
833 pt = -widgetView->GetOffsetTo(this);
834 // Add in the offset to the widget.
835 pt += widgetView->ViewToWidgetOffset();
837 // Convert to our appunits.
838 int32_t widgetAPD = widgetView->GetViewManager()->AppUnitsPerDevPixel();
839 int32_t ourAPD = GetViewManager()->AppUnitsPerDevPixel();
840 pt = pt.ScaleToOtherAppUnits(widgetAPD, ourAPD);
841 return pt;
844 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset) const {
845 return GetNearestWidget(aOffset, GetViewManager()->AppUnitsPerDevPixel());
848 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset,
849 const int32_t aAPD) const {
850 // aOffset is based on the view's position, which ignores any chrome on
851 // attached parent widgets.
853 // We accumulate the final result in pt
854 nsPoint pt(0, 0);
855 // The offset currently accumulated at the current APD
856 nsPoint docPt(0, 0);
857 const nsView* v = this;
858 nsViewManager* currVM = v->GetViewManager();
859 int32_t currAPD = currVM->AppUnitsPerDevPixel();
860 for (; v && !v->HasWidget(); v = v->GetParent()) {
861 nsViewManager* newVM = v->GetViewManager();
862 if (newVM != currVM) {
863 int32_t newAPD = newVM->AppUnitsPerDevPixel();
864 if (newAPD != currAPD) {
865 pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
866 docPt.x = docPt.y = 0;
867 currAPD = newAPD;
869 currVM = newVM;
871 docPt += v->GetPosition();
873 if (!v) {
874 if (aOffset) {
875 pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
876 *aOffset = pt;
878 return nullptr;
881 // pt is now the offset from v's origin to this view's origin.
882 // We add the ViewToWidgetOffset to get the offset to the widget.
883 if (aOffset) {
884 docPt += v->ViewToWidgetOffset();
885 pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
886 *aOffset = pt;
888 return v->GetWidget();
891 bool nsView::IsRoot() const {
892 NS_ASSERTION(mViewManager != nullptr,
893 " View manager is null in nsView::IsRoot()");
894 return mViewManager->GetRootView() == this;
897 nsRect nsView::GetBoundsInParentUnits() const {
898 nsView* parent = GetParent();
899 nsViewManager* VM = GetViewManager();
900 if (this != VM->GetRootView() || !parent) {
901 return mDimBounds;
903 int32_t ourAPD = VM->AppUnitsPerDevPixel();
904 int32_t parentAPD = parent->GetViewManager()->AppUnitsPerDevPixel();
905 return mDimBounds.ScaleToOtherAppUnitsRoundOut(ourAPD, parentAPD);
908 nsPoint nsView::ConvertFromParentCoords(nsPoint aPt) const {
909 const nsView* parent = GetParent();
910 if (parent) {
911 aPt = aPt.ScaleToOtherAppUnits(
912 parent->GetViewManager()->AppUnitsPerDevPixel(),
913 GetViewManager()->AppUnitsPerDevPixel());
915 aPt -= GetPosition();
916 return aPt;
919 static bool IsPopupWidget(nsIWidget* aWidget) {
920 return (aWidget->WindowType() == eWindowType_popup);
923 PresShell* nsView::GetPresShell() { return GetViewManager()->GetPresShell(); }
925 bool nsView::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) {
926 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
927 if (pm && IsPopupWidget(aWidget)) {
928 pm->PopupMoved(mFrame, nsIntPoint(x, y));
929 return true;
932 return false;
935 bool nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth,
936 int32_t aHeight) {
937 // The root view may not be set if this is the resize associated with
938 // window creation
939 SetForcedRepaint(true);
940 if (this == mViewManager->GetRootView()) {
941 RefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext();
942 // ensure DPI is up-to-date, in case of window being opened and sized
943 // on a non-default-dpi display (bug 829963)
944 devContext->CheckDPIChange();
945 int32_t p2a = devContext->AppUnitsPerDevPixel();
946 mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(aWidth, p2a),
947 NSIntPixelsToAppUnits(aHeight, p2a));
949 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
950 if (pm) {
951 PresShell* presShell = mViewManager->GetPresShell();
952 if (presShell && presShell->GetDocument()) {
953 pm->AdjustPopupsOnWindowChange(presShell);
957 return true;
959 if (IsPopupWidget(aWidget)) {
960 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
961 if (pm) {
962 pm->PopupResized(mFrame, LayoutDeviceIntSize(aWidth, aHeight));
963 return true;
967 return false;
970 #if defined(MOZ_WIDGET_ANDROID)
971 static bool NotifyDynamicToolbarMaxHeightChanged(
972 dom::BrowserParent* aBrowserParent, void* aArg) {
973 ScreenIntCoord* height = static_cast<ScreenIntCoord*>(aArg);
974 aBrowserParent->DynamicToolbarMaxHeightChanged(*height);
975 return false;
978 void nsView::DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight) {
979 MOZ_ASSERT(XRE_IsParentProcess(),
980 "Should be only called for the browser parent process");
981 MOZ_ASSERT(this == mViewManager->GetRootView(),
982 "Should be called for the root view");
984 PresShell* presShell = mViewManager->GetPresShell();
985 if (!presShell) {
986 return;
989 dom::Document* document = presShell->GetDocument();
990 if (!document) {
991 return;
994 nsPIDOMWindowOuter* window = document->GetWindow();
995 if (!window) {
996 return;
999 nsContentUtils::CallOnAllRemoteChildren(
1000 window, NotifyDynamicToolbarMaxHeightChanged, &aHeight);
1003 static bool NotifyDynamicToolbarOffsetChanged(
1004 dom::BrowserParent* aBrowserParent, void* aArg) {
1005 // Skip background tabs.
1006 if (!aBrowserParent->GetDocShellIsActive()) {
1007 return false;
1010 ScreenIntCoord* offset = static_cast<ScreenIntCoord*>(aArg);
1011 aBrowserParent->DynamicToolbarOffsetChanged(*offset);
1012 return true;
1015 void nsView::DynamicToolbarOffsetChanged(ScreenIntCoord aOffset) {
1016 MOZ_ASSERT(XRE_IsParentProcess(),
1017 "Should be only called for the browser parent process");
1018 MOZ_ASSERT(this == mViewManager->GetRootView(),
1019 "Should be called for the root view");
1021 PresShell* presShell = mViewManager->GetPresShell();
1022 if (!presShell) {
1023 return;
1026 dom::Document* document = presShell->GetDocument();
1027 if (!document) {
1028 return;
1031 nsPIDOMWindowOuter* window = document->GetWindow();
1032 if (!window) {
1033 return;
1036 nsContentUtils::CallOnAllRemoteChildren(
1037 window, NotifyDynamicToolbarOffsetChanged, &aOffset);
1039 #endif
1041 bool nsView::RequestWindowClose(nsIWidget* aWidget) {
1042 if (mFrame && IsPopupWidget(aWidget) && mFrame->IsMenuPopupFrame()) {
1043 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1044 if (pm) {
1045 pm->HidePopup(mFrame->GetContent(), false, true, false, false);
1046 return true;
1050 return false;
1053 void nsView::WillPaintWindow(nsIWidget* aWidget) {
1054 RefPtr<nsViewManager> vm = mViewManager;
1055 vm->WillPaintWindow(aWidget);
1058 bool nsView::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion) {
1059 NS_ASSERTION(this == nsView::GetViewFor(aWidget), "wrong view for widget?");
1061 RefPtr<nsViewManager> vm = mViewManager;
1062 bool result = vm->PaintWindow(aWidget, aRegion);
1063 return result;
1066 void nsView::DidPaintWindow() {
1067 RefPtr<nsViewManager> vm = mViewManager;
1068 vm->DidPaintWindow();
1071 void nsView::DidCompositeWindow(mozilla::layers::TransactionId aTransactionId,
1072 const TimeStamp& aCompositeStart,
1073 const TimeStamp& aCompositeEnd) {
1074 PresShell* presShell = mViewManager->GetPresShell();
1075 if (!presShell) {
1076 return;
1079 nsAutoScriptBlocker scriptBlocker;
1081 nsPresContext* context = presShell->GetPresContext();
1082 nsRootPresContext* rootContext = context->GetRootPresContext();
1083 if (rootContext) {
1084 rootContext->NotifyDidPaintForSubtree(aTransactionId, aCompositeEnd);
1087 // If the two timestamps are identical, this was likely a fake composite
1088 // event which wouldn't be terribly useful to display.
1089 if (aCompositeStart == aCompositeEnd) {
1090 return;
1093 nsIDocShell* docShell = context->GetDocShell();
1094 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1096 if (timelines && timelines->HasConsumer(docShell)) {
1097 timelines->AddMarkerForDocShell(
1098 docShell, MakeUnique<CompositeTimelineMarker>(
1099 aCompositeStart, MarkerTracingType::START));
1100 timelines->AddMarkerForDocShell(
1101 docShell, MakeUnique<CompositeTimelineMarker>(aCompositeEnd,
1102 MarkerTracingType::END));
1106 void nsView::RequestRepaint() {
1107 PresShell* presShell = mViewManager->GetPresShell();
1108 if (presShell) {
1109 presShell->ScheduleViewManagerFlush();
1113 bool nsView::ShouldNotBeVisible() {
1114 if (mFrame && mFrame->IsMenuPopupFrame()) {
1115 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1116 return !pm || !pm->IsPopupOpen(mFrame->GetContent());
1119 return false;
1122 nsEventStatus nsView::HandleEvent(WidgetGUIEvent* aEvent,
1123 bool aUseAttachedEvents) {
1124 MOZ_ASSERT(nullptr != aEvent->mWidget, "null widget ptr");
1126 nsEventStatus result = nsEventStatus_eIgnore;
1127 nsView* view;
1128 if (aUseAttachedEvents) {
1129 nsIWidgetListener* listener = aEvent->mWidget->GetAttachedWidgetListener();
1130 view = listener ? listener->GetView() : nullptr;
1131 } else {
1132 view = GetViewFor(aEvent->mWidget);
1135 if (view) {
1136 RefPtr<nsViewManager> vm = view->GetViewManager();
1137 vm->DispatchEvent(aEvent, view, &result);
1140 return result;
1143 bool nsView::IsPrimaryFramePaintSuppressed() {
1144 return StaticPrefs::layout_show_previous_page() && mFrame &&
1145 mFrame->PresShell()->IsPaintingSuppressed();