Bug 1890277: part 1) Add CSP parser tests for `require-trusted-types-for`. r=tschuster
[gecko.git] / view / nsView.cpp
blobaebc23688357e6361a6176b65397cabaa6b12a9b
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 "mozilla/widget/Screen.h"
19 #include "nsIWidget.h"
20 #include "nsViewManager.h"
21 #include "nsIFrame.h"
22 #include "nsPresArena.h"
23 #include "nsXULPopupManager.h"
24 #include "nsIScreen.h"
25 #include "nsIWidgetListener.h"
26 #include "nsContentUtils.h" // for nsAutoScriptBlocker
27 #include "nsDocShell.h"
28 #include "nsLayoutUtils.h"
29 #include "mozilla/StartupTimeline.h"
31 using namespace mozilla;
32 using namespace mozilla::widget;
34 nsView::nsView(nsViewManager* aViewManager, ViewVisibility aVisibility)
35 : mViewManager(aViewManager),
36 mParent(nullptr),
37 mNextSibling(nullptr),
38 mFirstChild(nullptr),
39 mFrame(nullptr),
40 mZIndex(0),
41 mVis(aVisibility),
42 mPosX(0),
43 mPosY(0),
44 mVFlags(0),
45 mWidgetIsTopLevel(false),
46 mForcedRepaint(false),
47 mNeedsWindowPropertiesSync(false) {
48 MOZ_COUNT_CTOR(nsView);
50 // Views should be transparent by default. Not being transparent is
51 // a promise that the view will paint all its pixels opaquely. Views
52 // should make this promise explicitly by calling
53 // SetViewContentTransparency.
56 void nsView::DropMouseGrabbing() {
57 if (mViewManager->GetPresShell()) {
58 PresShell::ClearMouseCaptureOnView(this);
62 nsView::~nsView() {
63 MOZ_COUNT_DTOR(nsView);
65 while (GetFirstChild()) {
66 nsView* child = GetFirstChild();
67 if (child->GetViewManager() == mViewManager) {
68 child->Destroy();
69 } else {
70 // just unhook it. Someone else will want to destroy this.
71 RemoveChild(child);
75 if (mViewManager) {
76 DropMouseGrabbing();
78 nsView* rootView = mViewManager->GetRootView();
80 if (rootView) {
81 // Root views can have parents!
82 if (mParent) {
83 mViewManager->RemoveChild(this);
86 if (rootView == this) {
87 // Inform the view manager that the root view has gone away...
88 mViewManager->SetRootView(nullptr);
90 } else if (mParent) {
91 mParent->RemoveChild(this);
94 mViewManager = nullptr;
95 } else if (mParent) {
96 mParent->RemoveChild(this);
99 if (mPreviousWindow) {
100 mPreviousWindow->SetPreviouslyAttachedWidgetListener(nullptr);
103 // Destroy and release the widget
104 DestroyWidget();
106 MOZ_RELEASE_ASSERT(!mFrame);
109 class DestroyWidgetRunnable : public Runnable {
110 public:
111 NS_DECL_NSIRUNNABLE
113 explicit DestroyWidgetRunnable(nsIWidget* aWidget)
114 : mozilla::Runnable("DestroyWidgetRunnable"), mWidget(aWidget) {}
116 private:
117 nsCOMPtr<nsIWidget> mWidget;
120 NS_IMETHODIMP DestroyWidgetRunnable::Run() {
121 mWidget->Destroy();
122 mWidget = nullptr;
123 return NS_OK;
126 void nsView::DestroyWidget() {
127 if (mWindow) {
128 // If we are not attached to a base window, we're going to tear down our
129 // widget here. However, if we're attached to somebody elses widget, we
130 // want to leave the widget alone: don't reset the client data or call
131 // Destroy. Just clear our event view ptr and free our reference to it.
132 if (mWidgetIsTopLevel) {
133 mWindow->SetAttachedWidgetListener(nullptr);
134 } else {
135 mWindow->SetWidgetListener(nullptr);
137 nsCOMPtr<nsIRunnable> widgetDestroyer =
138 new DestroyWidgetRunnable(mWindow);
140 // Don't leak if we happen to arrive here after the main thread
141 // has disappeared.
142 nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
143 if (mainThread) {
144 mainThread->Dispatch(widgetDestroyer.forget(), NS_DISPATCH_NORMAL);
148 mWindow = nullptr;
152 nsView* nsView::GetViewFor(const nsIWidget* aWidget) {
153 MOZ_ASSERT(aWidget, "null widget ptr");
155 nsIWidgetListener* listener = aWidget->GetWidgetListener();
156 if (listener) {
157 if (nsView* view = listener->GetView()) {
158 return view;
162 listener = aWidget->GetAttachedWidgetListener();
163 return listener ? listener->GetView() : nullptr;
166 void nsView::Destroy() {
167 this->~nsView();
168 mozWritePoison(this, sizeof(*this));
169 nsView::operator delete(this);
172 void nsView::SetPosition(nscoord aX, nscoord aY) {
173 mDimBounds.MoveBy(aX - mPosX, aY - mPosY);
174 mPosX = aX;
175 mPosY = aY;
177 NS_ASSERTION(GetParent() || (aX == 0 && aY == 0),
178 "Don't try to move the root widget to something non-zero");
180 ResetWidgetBounds(true, false);
183 void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync) {
184 if (mWindow) {
185 if (!aForceSync) {
186 // Don't change widget geometry synchronously, since that can
187 // cause synchronous painting.
188 mViewManager->PostPendingUpdate();
189 } else {
190 DoResetWidgetBounds(false, true);
192 return;
195 if (aRecurse) {
196 // reposition any widgets under this view
197 for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) {
198 v->ResetWidgetBounds(true, aForceSync);
203 bool nsView::IsEffectivelyVisible() {
204 for (nsView* v = this; v; v = v->mParent) {
205 if (v->GetVisibility() == ViewVisibility::Hide) return false;
207 return true;
210 // Cocoa and GTK round widget coordinates to the nearest global "display pixel"
211 // integer value. So we avoid fractional display pixel values by rounding to
212 // the nearest value that won't yield a fractional display pixel.
213 static LayoutDeviceIntRect MaybeRoundToDisplayPixels(
214 const LayoutDeviceIntRect& aRect, TransparencyMode aTransparency,
215 int32_t aRound) {
216 if (aRound == 1) {
217 return aRect;
220 // If the widget doesn't support transparency, we prefer truncating to
221 // ceiling, so that we don't have extra pixels not painted by our frame.
222 auto size = aTransparency == TransparencyMode::Opaque
223 ? aRect.Size().TruncatedToMultiple(aRound)
224 : aRect.Size().CeiledToMultiple(aRound);
225 Unused << NS_WARN_IF(aTransparency == TransparencyMode::Opaque &&
226 size != aRect.Size());
227 return {aRect.TopLeft().RoundedToMultiple(aRound), size};
230 LayoutDeviceIntRect nsView::CalcWidgetBounds(WindowType aType,
231 TransparencyMode aTransparency) {
232 int32_t p2a = mViewManager->AppUnitsPerDevPixel();
234 nsRect viewBounds(mDimBounds);
236 nsView* parent = GetParent();
237 nsIWidget* parentWidget = nullptr;
238 if (parent) {
239 nsPoint offset;
240 parentWidget = parent->GetNearestWidget(&offset, p2a);
241 // make viewBounds be relative to the parent widget, in appunits
242 viewBounds += offset;
244 if (parentWidget && aType == WindowType::Popup && IsEffectivelyVisible()) {
245 // put offset into screen coordinates. (based on client area origin)
246 LayoutDeviceIntPoint screenPoint = parentWidget->WidgetToScreenOffset();
247 viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a),
248 NSIntPixelsToAppUnits(screenPoint.y, p2a));
252 // Compute widget bounds in device pixels
253 const LayoutDeviceIntRect newBounds = [&] {
254 // TODO(emilio): We should probably use outside pixels for transparent
255 // windows (not just popups) as well.
256 if (aType != WindowType::Popup) {
257 return LayoutDeviceIntRect::FromUnknownRect(
258 viewBounds.ToNearestPixels(p2a));
260 // We use outside pixels for transparent windows if possible, so that we
261 // don't truncate the contents. For opaque popups, we use nearest pixels
262 // which prevents having pixels not drawn by the frame.
263 const bool opaque = aTransparency == TransparencyMode::Opaque;
264 const auto idealBounds = LayoutDeviceIntRect::FromUnknownRect(
265 opaque ? viewBounds.ToNearestPixels(p2a)
266 : viewBounds.ToOutsidePixels(p2a));
268 nsIWidget* widget = parentWidget ? parentWidget : mWindow.get();
269 if (!widget) {
270 return idealBounds;
272 const int32_t round = widget->RoundsWidgetCoordinatesTo();
273 return MaybeRoundToDisplayPixels(idealBounds, aTransparency, round);
274 }();
276 // Compute where the top-left of our widget ended up relative to the parent
277 // widget, in appunits.
278 nsPoint roundedOffset(NSIntPixelsToAppUnits(newBounds.X(), p2a),
279 NSIntPixelsToAppUnits(newBounds.Y(), p2a));
281 // mViewToWidgetOffset is added to coordinates relative to the view origin
282 // to get coordinates relative to the widget.
283 // The view origin, relative to the parent widget, is at
284 // (mPosX,mPosY) - mDimBounds.TopLeft() + viewBounds.TopLeft().
285 // Our widget, relative to the parent widget, is roundedOffset.
286 mViewToWidgetOffset = nsPoint(mPosX, mPosY) - mDimBounds.TopLeft() +
287 viewBounds.TopLeft() - roundedOffset;
289 return newBounds;
292 LayoutDeviceIntRect nsView::RecalcWidgetBounds() {
293 MOZ_ASSERT(mWindow);
294 return CalcWidgetBounds(mWindow->GetWindowType(),
295 mWindow->GetTransparencyMode());
298 void nsView::DoResetWidgetBounds(bool aMoveOnly, bool aInvalidateChangedSize) {
299 // The geometry of a root view's widget is controlled externally,
300 // NOT by sizing or positioning the view
301 if (mViewManager->GetRootView() == this) {
302 return;
305 MOZ_ASSERT(mWindow, "Why was this called??");
307 // Hold this ref to make sure it stays alive.
308 nsCOMPtr<nsIWidget> widget = mWindow;
310 // Stash a copy of these and use them so we can handle this being deleted (say
311 // from sync painting/flushing from Show/Move/Resize on the widget).
312 LayoutDeviceIntRect newBounds;
314 WindowType type = widget->GetWindowType();
316 LayoutDeviceIntRect curBounds = widget->GetClientBounds();
317 bool invisiblePopup = type == WindowType::Popup &&
318 ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) ||
319 mVis == ViewVisibility::Hide);
321 if (invisiblePopup) {
322 // We're going to hit the early exit below, avoid calling CalcWidgetBounds.
323 } else {
324 newBounds = CalcWidgetBounds(type, widget->GetTransparencyMode());
325 invisiblePopup = newBounds.IsEmpty();
328 bool curVisibility = widget->IsVisible();
329 bool newVisibility = !invisiblePopup && IsEffectivelyVisible();
330 if (curVisibility && !newVisibility) {
331 widget->Show(false);
334 if (invisiblePopup) {
335 // Don't manipulate empty or hidden popup widgets. For example there's no
336 // point moving hidden comboboxes around, or doing X server roundtrips
337 // to compute their true screen position. This could mean that
338 // WidgetToScreen operations on these widgets don't return up-to-date
339 // values, but popup positions aren't reliable anyway because of correction
340 // to be on or off-screen.
341 return;
344 // Apply the widget size constraints to newBounds.
345 widget->ConstrainSize(&newBounds.width, &newBounds.height);
347 bool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
348 bool changedSize = curBounds.Size() != newBounds.Size();
350 // Child views are never attached to top level widgets, this is safe.
352 // Coordinates are converted to desktop pixels for window Move/Resize APIs,
353 // because of the potential for device-pixel coordinate spaces for mixed
354 // hidpi/lodpi screens to overlap each other and result in bad placement
355 // (bug 814434).
357 DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScaleByScreen();
359 DesktopRect deskRect = newBounds / scale;
360 if (changedPos) {
361 if (changedSize && !aMoveOnly) {
362 widget->ResizeClient(deskRect, aInvalidateChangedSize);
363 } else {
364 widget->MoveClient(deskRect.TopLeft());
366 } else {
367 if (changedSize && !aMoveOnly) {
368 widget->ResizeClient(deskRect.Size(), aInvalidateChangedSize);
369 } // else do nothing!
372 if (!curVisibility && newVisibility) {
373 widget->Show(true);
377 void nsView::SetDimensions(const nsRect& aRect, bool aPaint,
378 bool aResizeWidget) {
379 nsRect dims = aRect;
380 dims.MoveBy(mPosX, mPosY);
382 // Don't use nsRect's operator== here, since it returns true when
383 // both rects are empty even if they have different widths and we
384 // have cases where that sort of thing matters to us.
385 if (mDimBounds.TopLeft() == dims.TopLeft() &&
386 mDimBounds.Size() == dims.Size()) {
387 return;
390 mDimBounds = dims;
392 if (aResizeWidget) {
393 ResetWidgetBounds(false, false);
397 void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible) {
398 if (!aEffectivelyVisible) {
399 DropMouseGrabbing();
402 SetForcedRepaint(true);
404 if (nullptr != mWindow) {
405 ResetWidgetBounds(false, false);
408 for (nsView* child = mFirstChild; child; child = child->mNextSibling) {
409 if (child->mVis == ViewVisibility::Hide) {
410 // It was effectively hidden and still is
411 continue;
413 // Our child is visible if we are
414 child->NotifyEffectiveVisibilityChanged(aEffectivelyVisible);
418 void nsView::SetVisibility(ViewVisibility aVisibility) {
419 mVis = aVisibility;
420 NotifyEffectiveVisibilityChanged(IsEffectivelyVisible());
423 void nsView::SetFloating(bool aFloatingView) {
424 if (aFloatingView)
425 mVFlags |= NS_VIEW_FLAG_FLOATING;
426 else
427 mVFlags &= ~NS_VIEW_FLAG_FLOATING;
430 void nsView::InvalidateHierarchy() {
431 if (mViewManager->GetRootView() == this) mViewManager->InvalidateHierarchy();
433 for (nsView* child = mFirstChild; child; child = child->GetNextSibling())
434 child->InvalidateHierarchy();
437 void nsView::InsertChild(nsView* aChild, nsView* aSibling) {
438 MOZ_ASSERT(nullptr != aChild, "null ptr");
440 if (nullptr != aChild) {
441 if (nullptr != aSibling) {
442 #ifdef DEBUG
443 NS_ASSERTION(aSibling->GetParent() == this,
444 "tried to insert view with invalid sibling");
445 #endif
446 // insert after sibling
447 aChild->SetNextSibling(aSibling->GetNextSibling());
448 aSibling->SetNextSibling(aChild);
449 } else {
450 aChild->SetNextSibling(mFirstChild);
451 mFirstChild = aChild;
453 aChild->SetParent(this);
455 // If we just inserted a root view, then update the RootViewManager
456 // on all view managers in the new subtree.
458 nsViewManager* vm = aChild->GetViewManager();
459 if (vm->GetRootView() == aChild) {
460 aChild->InvalidateHierarchy();
465 void nsView::RemoveChild(nsView* child) {
466 MOZ_ASSERT(nullptr != child, "null ptr");
468 if (nullptr != child) {
469 nsView* prevKid = nullptr;
470 nsView* kid = mFirstChild;
471 DebugOnly<bool> found = false;
472 while (nullptr != kid) {
473 if (kid == child) {
474 if (nullptr != prevKid) {
475 prevKid->SetNextSibling(kid->GetNextSibling());
476 } else {
477 mFirstChild = kid->GetNextSibling();
479 child->SetParent(nullptr);
480 found = true;
481 break;
483 prevKid = kid;
484 kid = kid->GetNextSibling();
486 NS_ASSERTION(found, "tried to remove non child");
488 // If we just removed a root view, then update the RootViewManager
489 // on all view managers in the removed subtree.
491 nsViewManager* vm = child->GetViewManager();
492 if (vm->GetRootView() == child) {
493 child->InvalidateHierarchy();
498 // Native widgets ultimately just can't deal with the awesome power of
499 // CSS2 z-index. However, we set the z-index on the widget anyway
500 // because in many simple common cases the widgets do end up in the
501 // right order. We set each widget's z-index to the z-index of the
502 // nearest ancestor that has non-auto z-index.
503 static void UpdateNativeWidgetZIndexes(nsView* aView, int32_t aZIndex) {
504 if (aView->HasWidget()) {
505 nsIWidget* widget = aView->GetWidget();
506 if (widget->GetZIndex() != aZIndex) {
507 widget->SetZIndex(aZIndex);
509 } else {
510 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
511 if (v->GetZIndexIsAuto()) {
512 UpdateNativeWidgetZIndexes(v, aZIndex);
518 static int32_t FindNonAutoZIndex(nsView* aView) {
519 while (aView) {
520 if (!aView->GetZIndexIsAuto()) {
521 return aView->GetZIndex();
523 aView = aView->GetParent();
525 return 0;
528 struct DefaultWidgetInitData : public widget::InitData {
529 DefaultWidgetInitData() : widget::InitData() {
530 mWindowType = WindowType::Child;
531 mClipChildren = true;
532 mClipSiblings = true;
536 nsresult nsView::CreateWidget(widget::InitData* aWidgetInitData,
537 bool aEnableDragDrop, bool aResetVisibility) {
538 AssertNoWindow();
539 MOZ_ASSERT(
540 !aWidgetInitData || aWidgetInitData->mWindowType != WindowType::Popup,
541 "Use CreateWidgetForPopup");
543 DefaultWidgetInitData defaultInitData;
544 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
545 LayoutDeviceIntRect trect = CalcWidgetBounds(
546 aWidgetInitData->mWindowType, aWidgetInitData->mTransparencyMode);
548 nsIWidget* parentWidget =
549 GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr;
550 if (!parentWidget) {
551 NS_ERROR("nsView::CreateWidget without suitable parent widget??");
552 return NS_ERROR_FAILURE;
555 // XXX: using aForceUseIWidgetParent=true to preserve previous
556 // semantics. It's not clear that it's actually needed.
557 mWindow = parentWidget->CreateChild(trect, aWidgetInitData, true);
558 if (!mWindow) {
559 return NS_ERROR_FAILURE;
562 InitializeWindow(aEnableDragDrop, aResetVisibility);
564 return NS_OK;
567 nsresult nsView::CreateWidgetForParent(nsIWidget* aParentWidget,
568 widget::InitData* aWidgetInitData,
569 bool aEnableDragDrop,
570 bool aResetVisibility) {
571 AssertNoWindow();
572 MOZ_ASSERT(
573 !aWidgetInitData || aWidgetInitData->mWindowType != WindowType::Popup,
574 "Use CreateWidgetForPopup");
575 MOZ_ASSERT(aParentWidget, "Parent widget required");
577 DefaultWidgetInitData defaultInitData;
578 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
580 LayoutDeviceIntRect trect = CalcWidgetBounds(
581 aWidgetInitData->mWindowType, aWidgetInitData->mTransparencyMode);
583 mWindow = aParentWidget->CreateChild(trect, aWidgetInitData);
584 if (!mWindow) {
585 return NS_ERROR_FAILURE;
588 InitializeWindow(aEnableDragDrop, aResetVisibility);
590 return NS_OK;
593 nsresult nsView::CreateWidgetForPopup(widget::InitData* aWidgetInitData,
594 nsIWidget* aParentWidget) {
595 AssertNoWindow();
596 MOZ_ASSERT(aWidgetInitData, "Widget init data required");
597 MOZ_ASSERT(aWidgetInitData->mWindowType == WindowType::Popup,
598 "Use one of the other CreateWidget methods");
600 LayoutDeviceIntRect trect = CalcWidgetBounds(
601 aWidgetInitData->mWindowType, aWidgetInitData->mTransparencyMode);
603 // XXX/cjones: having these two separate creation cases seems ... um
604 // ... unnecessary, but it's the way the old code did it. Please
605 // unify them by first finding a suitable parent nsIWidget, then
606 // getting rid of aForceUseIWidgetParent.
607 if (aParentWidget) {
608 // XXX: using aForceUseIWidgetParent=true to preserve previous
609 // semantics. It's not clear that it's actually needed.
610 mWindow = aParentWidget->CreateChild(trect, aWidgetInitData, true);
611 } else {
612 nsIWidget* nearestParent =
613 GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr;
614 if (!nearestParent) {
615 // Without a parent, we can't make a popup. This can happen
616 // when printing
617 return NS_ERROR_FAILURE;
620 mWindow = nearestParent->CreateChild(trect, aWidgetInitData);
622 if (!mWindow) {
623 return NS_ERROR_FAILURE;
626 InitializeWindow(/* aEnableDragDrop = */ true, /* aResetVisibility = */ true);
628 return NS_OK;
631 void nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility) {
632 MOZ_ASSERT(mWindow, "Must have a window to initialize");
634 mWindow->SetWidgetListener(this);
636 if (aEnableDragDrop) {
637 mWindow->EnableDragDrop(true);
640 // propagate the z-index to the widget.
641 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
643 // make sure visibility state is accurate
645 if (aResetVisibility) {
646 SetVisibility(GetVisibility());
650 void nsView::SetNeedsWindowPropertiesSync() {
651 mNeedsWindowPropertiesSync = true;
652 if (mViewManager) {
653 mViewManager->PostPendingUpdate();
657 // Attach to a top level widget and start receiving mirrored events.
658 nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget) {
659 MOZ_ASSERT(nullptr != aWidget, "null widget ptr");
661 /// XXXjimm This is a temporary workaround to an issue w/document
662 // viewer (bug 513162).
663 nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener();
664 if (listener) {
665 nsView* oldView = listener->GetView();
666 if (oldView) {
667 oldView->DetachFromTopLevelWidget();
671 // Note, the previous device context will be released. Detaching
672 // will not restore the old one.
673 aWidget->AttachViewToTopLevel(!nsIWidget::UsePuppetWidgets());
675 mWindow = aWidget;
677 mWindow->SetAttachedWidgetListener(this);
678 if (mWindow->GetWindowType() != WindowType::Invisible) {
679 nsresult rv = mWindow->AsyncEnableDragDrop(true);
680 NS_ENSURE_SUCCESS(rv, rv);
682 mWidgetIsTopLevel = true;
684 // Refresh the view bounds
685 RecalcWidgetBounds();
686 return NS_OK;
689 // Detach this view from an attached widget.
690 nsresult nsView::DetachFromTopLevelWidget() {
691 MOZ_ASSERT(mWidgetIsTopLevel, "Not attached currently!");
692 MOZ_ASSERT(mWindow, "null mWindow for DetachFromTopLevelWidget!");
694 mWindow->SetAttachedWidgetListener(nullptr);
695 nsIWidgetListener* listener = mWindow->GetPreviouslyAttachedWidgetListener();
697 if (listener && listener->GetView()) {
698 // Ensure the listener doesn't think it's being used anymore
699 listener->GetView()->SetPreviousWidget(nullptr);
702 // If the new view's frame is paint suppressed then the window
703 // will want to use us instead until that's done
704 mWindow->SetPreviouslyAttachedWidgetListener(this);
706 mPreviousWindow = mWindow;
707 mWindow = nullptr;
709 mWidgetIsTopLevel = false;
711 return NS_OK;
714 void nsView::SetZIndex(bool aAuto, int32_t aZIndex) {
715 bool oldIsAuto = GetZIndexIsAuto();
716 mVFlags = (mVFlags & ~NS_VIEW_FLAG_AUTO_ZINDEX) |
717 (aAuto ? NS_VIEW_FLAG_AUTO_ZINDEX : 0);
718 mZIndex = aZIndex;
720 if (HasWidget() || !oldIsAuto || !aAuto) {
721 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
725 void nsView::AssertNoWindow() {
726 // XXX: it would be nice to make this a strong assert
727 if (MOZ_UNLIKELY(mWindow)) {
728 NS_ERROR("We already have a window for this view? BAD");
729 mWindow->SetWidgetListener(nullptr);
730 mWindow->Destroy();
731 mWindow = nullptr;
736 // internal window creation functions
738 void nsView::AttachWidgetEventHandler(nsIWidget* aWidget) {
739 #ifdef DEBUG
740 NS_ASSERTION(!aWidget->GetWidgetListener(), "Already have a widget listener");
741 #endif
743 aWidget->SetWidgetListener(this);
746 void nsView::DetachWidgetEventHandler(nsIWidget* aWidget) {
747 NS_ASSERTION(!aWidget->GetWidgetListener() ||
748 aWidget->GetWidgetListener()->GetView() == this,
749 "Wrong view");
750 aWidget->SetWidgetListener(nullptr);
753 #ifdef DEBUG
754 void nsView::List(FILE* out, int32_t aIndent) const {
755 int32_t i;
756 for (i = aIndent; --i >= 0;) fputs(" ", out);
757 fprintf(out, "%p ", (void*)this);
758 if (nullptr != mWindow) {
759 nscoord p2a = mViewManager->AppUnitsPerDevPixel();
760 LayoutDeviceIntRect rect = mWindow->GetClientBounds();
761 nsRect windowBounds = LayoutDeviceIntRect::ToAppUnits(rect, p2a);
762 rect = mWindow->GetBounds();
763 nsRect nonclientBounds = LayoutDeviceIntRect::ToAppUnits(rect, p2a);
764 nsrefcnt widgetRefCnt = mWindow.get()->AddRef() - 1;
765 mWindow.get()->Release();
766 int32_t Z = mWindow->GetZIndex();
767 fprintf(out, "(widget=%p[%" PRIuPTR "] z=%d pos={%d,%d,%d,%d}) ",
768 (void*)mWindow, widgetRefCnt, Z, nonclientBounds.X(),
769 nonclientBounds.Y(), windowBounds.Width(), windowBounds.Height());
771 nsRect brect = GetBounds();
772 fprintf(out, "{%d,%d,%d,%d} @ %d,%d", brect.X(), brect.Y(), brect.Width(),
773 brect.Height(), mPosX, mPosY);
774 fprintf(out, " flags=%x z=%d vis=%d frame=%p <\n", mVFlags, mZIndex,
775 int(mVis), mFrame);
776 for (nsView* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
777 NS_ASSERTION(kid->GetParent() == this, "incorrect parent");
778 kid->List(out, aIndent + 1);
780 for (i = aIndent; --i >= 0;) fputs(" ", out);
781 fputs(">\n", out);
783 #endif // DEBUG
785 nsPoint nsView::GetOffsetTo(const nsView* aOther) const {
786 return GetOffsetTo(aOther, GetViewManager()->AppUnitsPerDevPixel());
789 nsPoint nsView::GetOffsetTo(const nsView* aOther, const int32_t aAPD) const {
790 MOZ_ASSERT(GetParent() || !aOther || aOther->GetParent() || this == aOther,
791 "caller of (outer) GetOffsetTo must not pass unrelated views");
792 // We accumulate the final result in offset
793 nsPoint offset(0, 0);
794 // The offset currently accumulated at the current APD
795 nsPoint docOffset(0, 0);
796 const nsView* v = this;
797 nsViewManager* currVM = v->GetViewManager();
798 int32_t currAPD = currVM->AppUnitsPerDevPixel();
799 const nsView* root = nullptr;
800 for (; v != aOther && v; root = v, v = v->GetParent()) {
801 nsViewManager* newVM = v->GetViewManager();
802 if (newVM != currVM) {
803 int32_t newAPD = newVM->AppUnitsPerDevPixel();
804 if (newAPD != currAPD) {
805 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
806 docOffset.x = docOffset.y = 0;
807 currAPD = newAPD;
809 currVM = newVM;
811 docOffset += v->GetPosition();
813 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
815 if (v != aOther) {
816 // Looks like aOther wasn't an ancestor of |this|. So now we have
817 // the root-VM-relative position of |this| in |offset|. Get the
818 // root-VM-relative position of aOther and subtract it.
819 nsPoint negOffset = aOther->GetOffsetTo(root, aAPD);
820 offset -= negOffset;
823 return offset;
826 nsPoint nsView::GetOffsetToWidget(nsIWidget* aWidget) const {
827 nsPoint pt;
828 // Get the view for widget
829 nsView* widgetView = GetViewFor(aWidget);
830 if (!widgetView) {
831 return pt;
834 // Get the offset to the widget view in the widget view's APD
835 // We get the offset in the widget view's APD first and then convert to our
836 // APD afterwards so that we can include the widget view's ViewToWidgetOffset
837 // in the sum in its native APD, and then convert the whole thing to our APD
838 // so that we don't have to convert the APD of the relatively small
839 // ViewToWidgetOffset by itself with a potentially large relative rounding
840 // error.
841 pt = -widgetView->GetOffsetTo(this);
842 // Add in the offset to the widget.
843 pt += widgetView->ViewToWidgetOffset();
845 // Convert to our appunits.
846 int32_t widgetAPD = widgetView->GetViewManager()->AppUnitsPerDevPixel();
847 int32_t ourAPD = GetViewManager()->AppUnitsPerDevPixel();
848 pt = pt.ScaleToOtherAppUnits(widgetAPD, ourAPD);
849 return pt;
852 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset) const {
853 return GetNearestWidget(aOffset, GetViewManager()->AppUnitsPerDevPixel());
856 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset,
857 const int32_t aAPD) const {
858 // aOffset is based on the view's position, which ignores any chrome on
859 // attached parent widgets.
861 // We accumulate the final result in pt
862 nsPoint pt(0, 0);
863 // The offset currently accumulated at the current APD
864 nsPoint docPt(0, 0);
865 const nsView* v = this;
866 nsViewManager* currVM = v->GetViewManager();
867 int32_t currAPD = currVM->AppUnitsPerDevPixel();
868 for (; v && !v->HasWidget(); v = v->GetParent()) {
869 nsViewManager* newVM = v->GetViewManager();
870 if (newVM != currVM) {
871 int32_t newAPD = newVM->AppUnitsPerDevPixel();
872 if (newAPD != currAPD) {
873 pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
874 docPt.x = docPt.y = 0;
875 currAPD = newAPD;
877 currVM = newVM;
879 docPt += v->GetPosition();
881 if (!v) {
882 if (aOffset) {
883 pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
884 *aOffset = pt;
886 return nullptr;
889 // pt is now the offset from v's origin to this view's origin.
890 // We add the ViewToWidgetOffset to get the offset to the widget.
891 if (aOffset) {
892 docPt += v->ViewToWidgetOffset();
893 pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
894 *aOffset = pt;
896 return v->GetWidget();
899 bool nsView::IsRoot() const {
900 NS_ASSERTION(mViewManager != nullptr,
901 " View manager is null in nsView::IsRoot()");
902 return mViewManager->GetRootView() == this;
905 nsRect nsView::GetBoundsInParentUnits() const {
906 nsView* parent = GetParent();
907 nsViewManager* VM = GetViewManager();
908 if (this != VM->GetRootView() || !parent) {
909 return mDimBounds;
911 int32_t ourAPD = VM->AppUnitsPerDevPixel();
912 int32_t parentAPD = parent->GetViewManager()->AppUnitsPerDevPixel();
913 return mDimBounds.ScaleToOtherAppUnitsRoundOut(ourAPD, parentAPD);
916 nsPoint nsView::ConvertFromParentCoords(nsPoint aPt) const {
917 const nsView* parent = GetParent();
918 if (parent) {
919 aPt = aPt.ScaleToOtherAppUnits(
920 parent->GetViewManager()->AppUnitsPerDevPixel(),
921 GetViewManager()->AppUnitsPerDevPixel());
923 aPt -= GetPosition();
924 return aPt;
927 static bool IsPopupWidget(nsIWidget* aWidget) {
928 return aWidget->GetWindowType() == WindowType::Popup;
931 PresShell* nsView::GetPresShell() { return GetViewManager()->GetPresShell(); }
933 bool nsView::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y,
934 ByMoveToRect aByMoveToRect) {
935 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
936 if (pm && IsPopupWidget(aWidget)) {
937 pm->PopupMoved(mFrame, LayoutDeviceIntPoint(x, y),
938 aByMoveToRect == ByMoveToRect::Yes);
939 return true;
942 return false;
945 bool nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth,
946 int32_t aHeight) {
947 // The root view may not be set if this is the resize associated with
948 // window creation
949 SetForcedRepaint(true);
950 if (this == mViewManager->GetRootView()) {
951 RefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext();
952 // ensure DPI is up-to-date, in case of window being opened and sized
953 // on a non-default-dpi display (bug 829963)
954 devContext->CheckDPIChange();
955 int32_t p2a = devContext->AppUnitsPerDevPixel();
956 if (auto* frame = GetFrame()) {
957 // Usually the resize would deal with this, but there are some cases (like
958 // web-extension popups) where frames might already be correctly sized etc
959 // due to a call to e.g. nsDocumentViewer::GetContentSize or so.
960 frame->InvalidateFrame();
963 mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(aWidth, p2a),
964 NSIntPixelsToAppUnits(aHeight, p2a));
966 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
967 PresShell* presShell = mViewManager->GetPresShell();
968 if (presShell && presShell->GetDocument()) {
969 pm->AdjustPopupsOnWindowChange(presShell);
973 return true;
975 if (IsPopupWidget(aWidget)) {
976 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
977 if (pm) {
978 pm->PopupResized(mFrame, LayoutDeviceIntSize(aWidth, aHeight));
979 return true;
983 return false;
986 #if defined(MOZ_WIDGET_ANDROID)
987 void nsView::DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight) {
988 MOZ_ASSERT(XRE_IsParentProcess(),
989 "Should be only called for the browser parent process");
990 MOZ_ASSERT(this == mViewManager->GetRootView(),
991 "Should be called for the root view");
993 PresShell* presShell = mViewManager->GetPresShell();
994 if (!presShell) {
995 return;
998 dom::Document* document = presShell->GetDocument();
999 if (!document) {
1000 return;
1003 nsPIDOMWindowOuter* window = document->GetWindow();
1004 if (!window) {
1005 return;
1008 nsContentUtils::CallOnAllRemoteChildren(
1009 window, [&aHeight](dom::BrowserParent* aBrowserParent) -> CallState {
1010 aBrowserParent->DynamicToolbarMaxHeightChanged(aHeight);
1011 return CallState::Continue;
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, [&aOffset](dom::BrowserParent* aBrowserParent) -> CallState {
1038 // Skip background tabs.
1039 if (!aBrowserParent->GetDocShellIsActive()) {
1040 return CallState::Continue;
1043 aBrowserParent->DynamicToolbarOffsetChanged(aOffset);
1044 return CallState::Stop;
1047 #endif
1049 bool nsView::RequestWindowClose(nsIWidget* aWidget) {
1050 if (mFrame && IsPopupWidget(aWidget) && mFrame->IsMenuPopupFrame()) {
1051 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
1052 pm->HidePopup(mFrame->GetContent()->AsElement(),
1053 {HidePopupOption::DeselectMenu});
1054 return true;
1058 return false;
1061 void nsView::WillPaintWindow(nsIWidget* aWidget) {
1062 RefPtr<nsViewManager> vm = mViewManager;
1063 vm->WillPaintWindow(aWidget);
1066 bool nsView::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion) {
1067 NS_ASSERTION(this == nsView::GetViewFor(aWidget), "wrong view for widget?");
1069 RefPtr<nsViewManager> vm = mViewManager;
1070 bool result = vm->PaintWindow(aWidget, aRegion);
1071 return result;
1074 void nsView::DidPaintWindow() {
1075 RefPtr<nsViewManager> vm = mViewManager;
1076 vm->DidPaintWindow();
1079 void nsView::DidCompositeWindow(mozilla::layers::TransactionId aTransactionId,
1080 const TimeStamp& aCompositeStart,
1081 const TimeStamp& aCompositeEnd) {
1082 PresShell* presShell = mViewManager->GetPresShell();
1083 if (!presShell) {
1084 return;
1087 nsAutoScriptBlocker scriptBlocker;
1089 nsPresContext* context = presShell->GetPresContext();
1090 nsRootPresContext* rootContext = context->GetRootPresContext();
1091 if (rootContext) {
1092 rootContext->NotifyDidPaintForSubtree(aTransactionId, aCompositeEnd);
1095 mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT2,
1096 aCompositeEnd);
1098 // If the two timestamps are identical, this was likely a fake composite
1099 // event which wouldn't be terribly useful to display.
1100 if (aCompositeStart == aCompositeEnd) {
1101 return;
1105 void nsView::RequestRepaint() {
1106 PresShell* presShell = mViewManager->GetPresShell();
1107 if (presShell) {
1108 presShell->ScheduleViewManagerFlush();
1112 bool nsView::ShouldNotBeVisible() {
1113 if (mFrame && mFrame->IsMenuPopupFrame()) {
1114 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1115 return !pm || !pm->IsPopupOpen(mFrame->GetContent()->AsElement());
1118 return false;
1121 nsEventStatus nsView::HandleEvent(WidgetGUIEvent* aEvent,
1122 bool aUseAttachedEvents) {
1123 MOZ_ASSERT(nullptr != aEvent->mWidget, "null widget ptr");
1125 nsEventStatus result = nsEventStatus_eIgnore;
1126 nsView* view;
1127 if (aUseAttachedEvents) {
1128 nsIWidgetListener* listener = aEvent->mWidget->GetAttachedWidgetListener();
1129 view = listener ? listener->GetView() : nullptr;
1130 } else {
1131 view = GetViewFor(aEvent->mWidget);
1134 if (view) {
1135 RefPtr<nsViewManager> vm = view->GetViewManager();
1136 vm->DispatchEvent(aEvent, view, &result);
1139 return result;
1142 void nsView::SafeAreaInsetsChanged(const ScreenIntMargin& aSafeAreaInsets) {
1143 if (!IsRoot()) {
1144 return;
1147 PresShell* presShell = mViewManager->GetPresShell();
1148 if (!presShell) {
1149 return;
1152 ScreenIntMargin windowSafeAreaInsets;
1153 LayoutDeviceIntRect windowRect = mWindow->GetScreenBounds();
1154 nsCOMPtr<nsIScreen> screen = mWindow->GetWidgetScreen();
1155 if (screen) {
1156 windowSafeAreaInsets = nsContentUtils::GetWindowSafeAreaInsets(
1157 screen, aSafeAreaInsets, windowRect);
1160 presShell->GetPresContext()->SetSafeAreaInsets(windowSafeAreaInsets);
1162 // https://github.com/w3c/csswg-drafts/issues/4670
1163 // Actually we don't set this value on sub document. This behaviour is
1164 // same as Blink.
1166 dom::Document* document = presShell->GetDocument();
1167 if (!document) {
1168 return;
1171 nsPIDOMWindowOuter* window = document->GetWindow();
1172 if (!window) {
1173 return;
1176 nsContentUtils::CallOnAllRemoteChildren(
1177 window,
1178 [windowSafeAreaInsets](dom::BrowserParent* aBrowserParent) -> CallState {
1179 Unused << aBrowserParent->SendSafeAreaInsetsChanged(
1180 windowSafeAreaInsets);
1181 return CallState::Continue;
1185 bool nsView::IsPrimaryFramePaintSuppressed() {
1186 return StaticPrefs::layout_show_previous_page() && mFrame &&
1187 mFrame->PresShell()->IsPaintingSuppressed();