Backed out changesets 73f3ce609c23 and 990a21978a2d (bug 951431) because they were...
[gecko.git] / view / src / nsView.cpp
blobcbdb324b103df66952ab83bcdd9c25a46e601e71
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/Likely.h"
12 #include "mozilla/Poison.h"
13 #include "nsIWidget.h"
14 #include "nsViewManager.h"
15 #include "nsIFrame.h"
16 #include "nsPresArena.h"
17 #include "nsXULPopupManager.h"
18 #include "nsIWidgetListener.h"
20 using namespace mozilla;
22 nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility)
24 MOZ_COUNT_CTOR(nsView);
26 mVis = aVisibility;
27 // Views should be transparent by default. Not being transparent is
28 // a promise that the view will paint all its pixels opaquely. Views
29 // should make this promise explicitly by calling
30 // SetViewContentTransparency.
31 mVFlags = 0;
32 mViewManager = aViewManager;
33 mDirtyRegion = nullptr;
34 mWidgetIsTopLevel = false;
37 void nsView::DropMouseGrabbing()
39 nsIPresShell* presShell = mViewManager->GetPresShell();
40 if (presShell)
41 presShell->ClearMouseCaptureOnView(this);
44 nsView::~nsView()
46 MOZ_COUNT_DTOR(nsView);
48 while (GetFirstChild())
50 nsView* child = GetFirstChild();
51 if (child->GetViewManager() == mViewManager) {
52 child->Destroy();
53 } else {
54 // just unhook it. Someone else will want to destroy this.
55 RemoveChild(child);
59 if (mViewManager)
61 DropMouseGrabbing();
63 nsView *rootView = mViewManager->GetRootView();
65 if (rootView)
67 // Root views can have parents!
68 if (mParent)
70 mViewManager->RemoveChild(this);
73 if (rootView == this)
75 // Inform the view manager that the root view has gone away...
76 mViewManager->SetRootView(nullptr);
79 else if (mParent)
81 mParent->RemoveChild(this);
84 mViewManager = nullptr;
86 else if (mParent)
88 mParent->RemoveChild(this);
91 // Destroy and release the widget
92 DestroyWidget();
94 delete mDirtyRegion;
97 class DestroyWidgetRunnable : public nsRunnable {
98 public:
99 NS_DECL_NSIRUNNABLE
101 explicit DestroyWidgetRunnable(nsIWidget* aWidget) : mWidget(aWidget) {}
103 private:
104 nsCOMPtr<nsIWidget> mWidget;
107 NS_IMETHODIMP DestroyWidgetRunnable::Run()
109 mWidget->Destroy();
110 mWidget = nullptr;
111 return NS_OK;
115 void nsView::DestroyWidget()
117 if (mWindow)
119 // If we are not attached to a base window, we're going to tear down our
120 // widget here. However, if we're attached to somebody elses widget, we
121 // want to leave the widget alone: don't reset the client data or call
122 // Destroy. Just clear our event view ptr and free our reference to it.
123 if (mWidgetIsTopLevel) {
124 mWindow->SetAttachedWidgetListener(nullptr);
126 else {
127 mWindow->SetWidgetListener(nullptr);
129 nsCOMPtr<nsIRunnable> widgetDestroyer =
130 new DestroyWidgetRunnable(mWindow);
132 NS_DispatchToMainThread(widgetDestroyer);
135 NS_RELEASE(mWindow);
139 nsView* nsView::GetViewFor(nsIWidget* aWidget)
141 NS_PRECONDITION(nullptr != aWidget, "null widget ptr");
143 nsIWidgetListener* listener = aWidget->GetWidgetListener();
144 if (listener) {
145 nsView* view = listener->GetView();
146 if (view)
147 return view;
150 listener = aWidget->GetAttachedWidgetListener();
151 return listener ? listener->GetView() : nullptr;
154 void nsView::Destroy()
156 this->~nsView();
157 mozWritePoison(this, sizeof(*this));
158 nsView::operator delete(this);
161 void nsView::SetPosition(nscoord aX, nscoord aY)
163 mDimBounds.x += aX - mPosX;
164 mDimBounds.y += aY - mPosY;
165 mPosX = aX;
166 mPosY = aY;
168 NS_ASSERTION(GetParent() || (aX == 0 && aY == 0),
169 "Don't try to move the root widget to something non-zero");
171 ResetWidgetBounds(true, false);
174 void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync)
176 if (mWindow) {
177 if (!aForceSync) {
178 // Don't change widget geometry synchronously, since that can
179 // cause synchronous painting.
180 mViewManager->PostPendingUpdate();
181 } else {
182 DoResetWidgetBounds(false, true);
184 return;
187 if (aRecurse) {
188 // reposition any widgets under this view
189 for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) {
190 v->ResetWidgetBounds(true, aForceSync);
195 bool nsView::IsEffectivelyVisible()
197 for (nsView* v = this; v; v = v->mParent) {
198 if (v->GetVisibility() == nsViewVisibility_kHide)
199 return false;
201 return true;
204 nsIntRect nsView::CalcWidgetBounds(nsWindowType aType)
206 int32_t p2a = mViewManager->AppUnitsPerDevPixel();
208 nsRect viewBounds(mDimBounds);
210 nsView* parent = GetParent();
211 nsIWidget* parentWidget = nullptr;
212 if (parent) {
213 nsPoint offset;
214 parentWidget = parent->GetNearestWidget(&offset, p2a);
215 // make viewBounds be relative to the parent widget, in appunits
216 viewBounds += offset;
218 if (parentWidget && aType == eWindowType_popup &&
219 IsEffectivelyVisible()) {
220 // put offset into screen coordinates. (based on client area origin)
221 nsIntPoint screenPoint = parentWidget->WidgetToScreenOffset();
222 viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a),
223 NSIntPixelsToAppUnits(screenPoint.y, p2a));
227 // Compute widget bounds in device pixels
228 nsIntRect newBounds = viewBounds.ToNearestPixels(p2a);
230 #ifdef XP_MACOSX
231 // cocoa rounds widget coordinates to the nearest global "display pixel"
232 // integer value. So we avoid fractional display pixel values by rounding
233 // to the nearest value that won't yield a fractional display pixel.
234 nsIWidget* widget = parentWidget ? parentWidget : mWindow;
235 uint32_t round;
236 if (aType == eWindowType_popup && widget &&
237 ((round = widget->RoundsWidgetCoordinatesTo()) > 1)) {
238 nsIntSize pixelRoundedSize = newBounds.Size();
239 // round the top left and bottom right to the nearest round pixel
240 newBounds.x = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.x, p2a) / round) * round;
241 newBounds.y = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.y, p2a) / round) * round;
242 newBounds.width =
243 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.XMost(), p2a) / round) * round - newBounds.x;
244 newBounds.height =
245 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.YMost(), p2a) / round) * round - newBounds.y;
246 // but if that makes the widget larger then our frame may not paint the
247 // extra pixels, so reduce the size to the nearest round value
248 if (newBounds.width > pixelRoundedSize.width) {
249 newBounds.width -= round;
251 if (newBounds.height > pixelRoundedSize.height) {
252 newBounds.height -= round;
255 #endif
257 // Compute where the top-left of our widget ended up relative to the parent
258 // widget, in appunits.
259 nsPoint roundedOffset(NSIntPixelsToAppUnits(newBounds.x, p2a),
260 NSIntPixelsToAppUnits(newBounds.y, p2a));
262 // mViewToWidgetOffset is added to coordinates relative to the view origin
263 // to get coordinates relative to the widget.
264 // The view origin, relative to the parent widget, is at
265 // (mPosX,mPosY) - mDimBounds.TopLeft() + viewBounds.TopLeft().
266 // Our widget, relative to the parent widget, is roundedOffset.
267 mViewToWidgetOffset = nsPoint(mPosX, mPosY)
268 - mDimBounds.TopLeft() + viewBounds.TopLeft() - roundedOffset;
270 return newBounds;
273 void nsView::DoResetWidgetBounds(bool aMoveOnly,
274 bool aInvalidateChangedSize) {
275 // The geometry of a root view's widget is controlled externally,
276 // NOT by sizing or positioning the view
277 if (mViewManager->GetRootView() == this) {
278 return;
281 NS_PRECONDITION(mWindow, "Why was this called??");
283 // Hold this ref to make sure it stays alive.
284 nsCOMPtr<nsIWidget> widget = mWindow;
286 // Stash a copy of these and use them so we can handle this being deleted (say
287 // from sync painting/flushing from Show/Move/Resize on the widget).
288 nsIntRect newBounds;
289 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
291 nsWindowType type;
292 widget->GetWindowType(type);
294 nsIntRect curBounds;
295 widget->GetClientBounds(curBounds);
296 bool invisiblePopup = type == eWindowType_popup &&
297 ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) ||
298 mVis == nsViewVisibility_kHide);
300 if (invisiblePopup) {
301 // We're going to hit the early exit below, avoid calling CalcWidgetBounds.
302 } else {
303 newBounds = CalcWidgetBounds(type);
306 bool curVisibility = widget->IsVisible();
307 bool newVisibility = IsEffectivelyVisible();
308 if (curVisibility && !newVisibility) {
309 widget->Show(false);
312 if (invisiblePopup) {
313 // Don't manipulate empty or hidden popup widgets. For example there's no
314 // point moving hidden comboboxes around, or doing X server roundtrips
315 // to compute their true screen position. This could mean that WidgetToScreen
316 // operations on these widgets don't return up-to-date values, but popup
317 // positions aren't reliable anyway because of correction to be on or off-screen.
318 return;
321 bool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
322 bool changedSize = curBounds.Size() != newBounds.Size();
324 // Child views are never attached to top level widgets, this is safe.
326 // Coordinates are converted to display pixels for window Move/Resize APIs,
327 // because of the potential for device-pixel coordinate spaces for mixed
328 // hidpi/lodpi screens to overlap each other and result in bad placement
329 // (bug 814434).
330 double invScale;
332 // Bug 861270: for correct widget manipulation at arbitrary scale factors,
333 // prefer to base scaling on widget->GetDefaultScale(). But only do this if
334 // it matches the view manager's device context scale after allowing for the
335 // quantization to app units, because of OS X multiscreen issues (where the
336 // only two scales are 1.0 or 2.0, and so the quantization doesn't actually
337 // cause problems anyhow).
338 // In the case of a mismatch, fall back to scaling based on the dev context's
339 // unscaledAppUnitsPerDevPixel value. On platforms where the device-pixel
340 // scale is uniform across all displays (currently all except OS X), we'll
341 // always use the precise value from mWindow->GetDefaultScale here.
342 CSSToLayoutDeviceScale scale = widget->GetDefaultScale();
343 if (NSToIntRound(60.0 / scale.scale) == dx->UnscaledAppUnitsPerDevPixel()) {
344 invScale = 1.0 / scale.scale;
345 } else {
346 invScale = dx->UnscaledAppUnitsPerDevPixel() / 60.0;
349 if (changedPos) {
350 if (changedSize && !aMoveOnly) {
351 widget->ResizeClient(newBounds.x * invScale,
352 newBounds.y * invScale,
353 newBounds.width * invScale,
354 newBounds.height * invScale,
355 aInvalidateChangedSize);
356 } else {
357 widget->MoveClient(newBounds.x * invScale,
358 newBounds.y * invScale);
360 } else {
361 if (changedSize && !aMoveOnly) {
362 widget->ResizeClient(newBounds.width * invScale,
363 newBounds.height * invScale,
364 aInvalidateChangedSize);
365 } // else do nothing!
368 if (!curVisibility && newVisibility) {
369 widget->Show(true);
373 void nsView::SetDimensions(const nsRect& aRect, bool aPaint, bool aResizeWidget)
375 nsRect dims = aRect;
376 dims.MoveBy(mPosX, mPosY);
378 // Don't use nsRect's operator== here, since it returns true when
379 // both rects are empty even if they have different widths and we
380 // have cases where that sort of thing matters to us.
381 if (mDimBounds.TopLeft() == dims.TopLeft() &&
382 mDimBounds.Size() == dims.Size()) {
383 return;
386 mDimBounds = dims;
388 if (aResizeWidget) {
389 ResetWidgetBounds(false, false);
393 void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible)
395 if (!aEffectivelyVisible)
397 DropMouseGrabbing();
400 SetForcedRepaint(true);
402 if (nullptr != mWindow)
404 ResetWidgetBounds(false, false);
407 for (nsView* child = mFirstChild; child; child = child->mNextSibling) {
408 if (child->mVis == nsViewVisibility_kHide) {
409 // It was effectively hidden and still is
410 continue;
412 // Our child is visible if we are
413 child->NotifyEffectiveVisibilityChanged(aEffectivelyVisible);
417 void nsView::SetVisibility(nsViewVisibility aVisibility)
419 mVis = aVisibility;
420 NotifyEffectiveVisibilityChanged(IsEffectivelyVisible());
423 void nsView::SetFloating(bool aFloatingView)
425 if (aFloatingView)
426 mVFlags |= NS_VIEW_FLAG_FLOATING;
427 else
428 mVFlags &= ~NS_VIEW_FLAG_FLOATING;
431 void nsView::InvalidateHierarchy(nsViewManager *aViewManagerParent)
433 if (mViewManager->GetRootView() == this)
434 mViewManager->InvalidateHierarchy();
436 for (nsView *child = mFirstChild; child; child = child->GetNextSibling())
437 child->InvalidateHierarchy(aViewManagerParent);
440 void nsView::InsertChild(nsView *aChild, nsView *aSibling)
442 NS_PRECONDITION(nullptr != aChild, "null ptr");
444 if (nullptr != aChild)
446 if (nullptr != aSibling)
448 #ifdef DEBUG
449 NS_ASSERTION(aSibling->GetParent() == this, "tried to insert view with invalid sibling");
450 #endif
451 //insert after sibling
452 aChild->SetNextSibling(aSibling->GetNextSibling());
453 aSibling->SetNextSibling(aChild);
455 else
457 aChild->SetNextSibling(mFirstChild);
458 mFirstChild = aChild;
460 aChild->SetParent(this);
462 // If we just inserted a root view, then update the RootViewManager
463 // on all view managers in the new subtree.
465 nsViewManager *vm = aChild->GetViewManager();
466 if (vm->GetRootView() == aChild)
468 aChild->InvalidateHierarchy(nullptr); // don't care about releasing grabs
473 void nsView::RemoveChild(nsView *child)
475 NS_PRECONDITION(nullptr != child, "null ptr");
477 if (nullptr != child)
479 nsView* prevKid = nullptr;
480 nsView* kid = mFirstChild;
481 DebugOnly<bool> found = false;
482 while (nullptr != kid) {
483 if (kid == child) {
484 if (nullptr != prevKid) {
485 prevKid->SetNextSibling(kid->GetNextSibling());
486 } else {
487 mFirstChild = kid->GetNextSibling();
489 child->SetParent(nullptr);
490 found = true;
491 break;
493 prevKid = kid;
494 kid = kid->GetNextSibling();
496 NS_ASSERTION(found, "tried to remove non child");
498 // If we just removed a root view, then update the RootViewManager
499 // on all view managers in the removed subtree.
501 nsViewManager *vm = child->GetViewManager();
502 if (vm->GetRootView() == child)
504 child->InvalidateHierarchy(GetViewManager());
509 // Native widgets ultimately just can't deal with the awesome power of
510 // CSS2 z-index. However, we set the z-index on the widget anyway
511 // because in many simple common cases the widgets do end up in the
512 // right order. We set each widget's z-index to the z-index of the
513 // nearest ancestor that has non-auto z-index.
514 static void UpdateNativeWidgetZIndexes(nsView* aView, int32_t aZIndex)
516 if (aView->HasWidget()) {
517 nsIWidget* widget = aView->GetWidget();
518 int32_t curZ;
519 widget->GetZIndex(&curZ);
520 if (curZ != aZIndex) {
521 widget->SetZIndex(aZIndex);
523 } else {
524 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
525 if (v->GetZIndexIsAuto()) {
526 UpdateNativeWidgetZIndexes(v, aZIndex);
532 static int32_t FindNonAutoZIndex(nsView* aView)
534 while (aView) {
535 if (!aView->GetZIndexIsAuto()) {
536 return aView->GetZIndex();
538 aView = aView->GetParent();
540 return 0;
543 struct DefaultWidgetInitData : public nsWidgetInitData {
544 DefaultWidgetInitData() : nsWidgetInitData()
546 mWindowType = eWindowType_child;
547 clipChildren = true;
548 clipSiblings = true;
552 nsresult nsView::CreateWidget(nsWidgetInitData *aWidgetInitData,
553 bool aEnableDragDrop,
554 bool aResetVisibility)
556 AssertNoWindow();
557 NS_ABORT_IF_FALSE(!aWidgetInitData ||
558 aWidgetInitData->mWindowType != eWindowType_popup,
559 "Use CreateWidgetForPopup");
561 DefaultWidgetInitData defaultInitData;
562 bool initDataPassedIn = !!aWidgetInitData;
563 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
564 defaultInitData.mListenForResizes =
565 (!initDataPassedIn && GetParent() &&
566 GetParent()->GetViewManager() != mViewManager);
568 nsIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
570 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
572 nsIWidget* parentWidget =
573 GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr;
574 if (!parentWidget) {
575 NS_ERROR("nsView::CreateWidget without suitable parent widget??");
576 return NS_ERROR_FAILURE;
579 // XXX: using aForceUseIWidgetParent=true to preserve previous
580 // semantics. It's not clear that it's actually needed.
581 mWindow = parentWidget->CreateChild(trect, dx, aWidgetInitData,
582 true).get();
583 if (!mWindow) {
584 return NS_ERROR_FAILURE;
587 InitializeWindow(aEnableDragDrop, aResetVisibility);
589 return NS_OK;
592 nsresult nsView::CreateWidgetForParent(nsIWidget* aParentWidget,
593 nsWidgetInitData *aWidgetInitData,
594 bool aEnableDragDrop,
595 bool aResetVisibility)
597 AssertNoWindow();
598 NS_ABORT_IF_FALSE(!aWidgetInitData ||
599 aWidgetInitData->mWindowType != eWindowType_popup,
600 "Use CreateWidgetForPopup");
601 NS_ABORT_IF_FALSE(aParentWidget, "Parent widget required");
603 DefaultWidgetInitData defaultInitData;
604 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
606 nsIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
608 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
610 mWindow =
611 aParentWidget->CreateChild(trect, dx, aWidgetInitData).get();
612 if (!mWindow) {
613 return NS_ERROR_FAILURE;
616 InitializeWindow(aEnableDragDrop, aResetVisibility);
618 return NS_OK;
621 nsresult nsView::CreateWidgetForPopup(nsWidgetInitData *aWidgetInitData,
622 nsIWidget* aParentWidget,
623 bool aEnableDragDrop,
624 bool aResetVisibility)
626 AssertNoWindow();
627 NS_ABORT_IF_FALSE(aWidgetInitData, "Widget init data required");
628 NS_ABORT_IF_FALSE(aWidgetInitData->mWindowType == eWindowType_popup,
629 "Use one of the other CreateWidget methods");
631 nsIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
633 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
635 // XXX/cjones: having these two separate creation cases seems ... um
636 // ... unnecessary, but it's the way the old code did it. Please
637 // unify them by first finding a suitable parent nsIWidget, then
638 // getting rid of aForceUseIWidgetParent.
639 if (aParentWidget) {
640 // XXX: using aForceUseIWidgetParent=true to preserve previous
641 // semantics. It's not clear that it's actually needed.
642 mWindow = aParentWidget->CreateChild(trect, dx, aWidgetInitData,
643 true).get();
645 else {
646 nsIWidget* nearestParent = GetParent() ? GetParent()->GetNearestWidget(nullptr)
647 : nullptr;
648 if (!nearestParent) {
649 // Without a parent, we can't make a popup. This can happen
650 // when printing
651 return NS_ERROR_FAILURE;
654 mWindow =
655 nearestParent->CreateChild(trect, dx, aWidgetInitData).get();
657 if (!mWindow) {
658 return NS_ERROR_FAILURE;
661 InitializeWindow(aEnableDragDrop, aResetVisibility);
663 return NS_OK;
666 void
667 nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility)
669 NS_ABORT_IF_FALSE(mWindow, "Must have a window to initialize");
671 mWindow->SetWidgetListener(this);
673 if (aEnableDragDrop) {
674 mWindow->EnableDragDrop(true);
677 // propagate the z-index to the widget.
678 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
680 //make sure visibility state is accurate
682 if (aResetVisibility) {
683 SetVisibility(GetVisibility());
687 // Attach to a top level widget and start receiving mirrored events.
688 nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget)
690 NS_PRECONDITION(nullptr != aWidget, "null widget ptr");
691 /// XXXjimm This is a temporary workaround to an issue w/document
692 // viewer (bug 513162).
693 nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener();
694 if (listener) {
695 nsView *oldView = listener->GetView();
696 if (oldView) {
697 oldView->DetachFromTopLevelWidget();
701 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
703 // Note, the previous device context will be released. Detaching
704 // will not restore the old one.
705 nsresult rv = aWidget->AttachViewToTopLevel(!nsIWidget::UsePuppetWidgets(), dx);
706 if (NS_FAILED(rv))
707 return rv;
709 mWindow = aWidget;
710 NS_ADDREF(mWindow);
712 mWindow->SetAttachedWidgetListener(this);
713 mWindow->EnableDragDrop(true);
714 mWidgetIsTopLevel = true;
716 // Refresh the view bounds
717 nsWindowType type;
718 mWindow->GetWindowType(type);
719 CalcWidgetBounds(type);
721 return NS_OK;
724 // Detach this view from an attached widget.
725 nsresult nsView::DetachFromTopLevelWidget()
727 NS_PRECONDITION(mWidgetIsTopLevel, "Not attached currently!");
728 NS_PRECONDITION(mWindow, "null mWindow for DetachFromTopLevelWidget!");
730 mWindow->SetAttachedWidgetListener(nullptr);
731 NS_RELEASE(mWindow);
733 mWidgetIsTopLevel = false;
735 return NS_OK;
738 void nsView::SetZIndex(bool aAuto, int32_t aZIndex)
740 bool oldIsAuto = GetZIndexIsAuto();
741 mVFlags = (mVFlags & ~NS_VIEW_FLAG_AUTO_ZINDEX) | (aAuto ? NS_VIEW_FLAG_AUTO_ZINDEX : 0);
742 mZIndex = aZIndex;
744 if (HasWidget() || !oldIsAuto || !aAuto) {
745 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
749 void nsView::AssertNoWindow()
751 // XXX: it would be nice to make this a strong assert
752 if (MOZ_UNLIKELY(mWindow)) {
753 NS_ERROR("We already have a window for this view? BAD");
754 mWindow->SetWidgetListener(nullptr);
755 mWindow->Destroy();
756 NS_RELEASE(mWindow);
761 // internal window creation functions
763 void nsView::AttachWidgetEventHandler(nsIWidget* aWidget)
765 #ifdef DEBUG
766 NS_ASSERTION(!aWidget->GetWidgetListener(), "Already have a widget listener");
767 #endif
769 aWidget->SetWidgetListener(this);
772 void nsView::DetachWidgetEventHandler(nsIWidget* aWidget)
774 NS_ASSERTION(!aWidget->GetWidgetListener() ||
775 aWidget->GetWidgetListener()->GetView() == this, "Wrong view");
776 aWidget->SetWidgetListener(nullptr);
779 #ifdef DEBUG
780 void nsView::List(FILE* out, int32_t aIndent) const
782 int32_t i;
783 for (i = aIndent; --i >= 0; ) fputs(" ", out);
784 fprintf(out, "%p ", (void*)this);
785 if (nullptr != mWindow) {
786 nscoord p2a = mViewManager->AppUnitsPerDevPixel();
787 nsIntRect rect;
788 mWindow->GetClientBounds(rect);
789 nsRect windowBounds = rect.ToAppUnits(p2a);
790 mWindow->GetBounds(rect);
791 nsRect nonclientBounds = rect.ToAppUnits(p2a);
792 nsrefcnt widgetRefCnt = mWindow->AddRef() - 1;
793 mWindow->Release();
794 int32_t Z;
795 mWindow->GetZIndex(&Z);
796 fprintf(out, "(widget=%p[%d] z=%d pos={%d,%d,%d,%d}) ",
797 (void*)mWindow, widgetRefCnt, Z,
798 nonclientBounds.x, nonclientBounds.y,
799 windowBounds.width, windowBounds.height);
801 nsRect brect = GetBounds();
802 fprintf(out, "{%d,%d,%d,%d}",
803 brect.x, brect.y, brect.width, brect.height);
804 fprintf(out, " z=%d vis=%d frame=%p <\n",
805 mZIndex, mVis, static_cast<void*>(mFrame));
806 for (nsView* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
807 NS_ASSERTION(kid->GetParent() == this, "incorrect parent");
808 kid->List(out, aIndent + 1);
810 for (i = aIndent; --i >= 0; ) fputs(" ", out);
811 fputs(">\n", out);
813 #endif // DEBUG
815 nsPoint nsView::GetOffsetTo(const nsView* aOther) const
817 return GetOffsetTo(aOther, GetViewManager()->AppUnitsPerDevPixel());
820 nsPoint nsView::GetOffsetTo(const nsView* aOther, const int32_t aAPD) const
822 NS_ABORT_IF_FALSE(GetParent() || !aOther || aOther->GetParent() ||
823 this == aOther, "caller of (outer) GetOffsetTo must not "
824 "pass unrelated views");
825 // We accumulate the final result in offset
826 nsPoint offset(0, 0);
827 // The offset currently accumulated at the current APD
828 nsPoint docOffset(0, 0);
829 const nsView* v = this;
830 nsViewManager* currVM = v->GetViewManager();
831 int32_t currAPD = currVM->AppUnitsPerDevPixel();
832 const nsView* root = nullptr;
833 for ( ; v != aOther && v; root = v, v = v->GetParent()) {
834 nsViewManager* newVM = v->GetViewManager();
835 if (newVM != currVM) {
836 int32_t newAPD = newVM->AppUnitsPerDevPixel();
837 if (newAPD != currAPD) {
838 offset += docOffset.ConvertAppUnits(currAPD, aAPD);
839 docOffset.x = docOffset.y = 0;
840 currAPD = newAPD;
842 currVM = newVM;
844 docOffset += v->GetPosition();
846 offset += docOffset.ConvertAppUnits(currAPD, aAPD);
848 if (v != aOther) {
849 // Looks like aOther wasn't an ancestor of |this|. So now we have
850 // the root-VM-relative position of |this| in |offset|. Get the
851 // root-VM-relative position of aOther and subtract it.
852 nsPoint negOffset = aOther->GetOffsetTo(root, aAPD);
853 offset -= negOffset;
856 return offset;
859 nsPoint nsView::GetOffsetToWidget(nsIWidget* aWidget) const
861 nsPoint pt;
862 // Get the view for widget
863 nsView* widgetView = GetViewFor(aWidget);
864 if (!widgetView) {
865 return pt;
868 // Get the offset to the widget view in the widget view's APD
869 // We get the offset in the widget view's APD first and then convert to our
870 // APD afterwards so that we can include the widget view's ViewToWidgetOffset
871 // in the sum in its native APD, and then convert the whole thing to our APD
872 // so that we don't have to convert the APD of the relatively small
873 // ViewToWidgetOffset by itself with a potentially large relative rounding
874 // error.
875 pt = -widgetView->GetOffsetTo(this);
876 // Add in the offset to the widget.
877 pt += widgetView->ViewToWidgetOffset();
879 // Convert to our appunits.
880 int32_t widgetAPD = widgetView->GetViewManager()->AppUnitsPerDevPixel();
881 int32_t ourAPD = GetViewManager()->AppUnitsPerDevPixel();
882 pt = pt.ConvertAppUnits(widgetAPD, ourAPD);
883 return pt;
886 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset) const
888 return GetNearestWidget(aOffset, GetViewManager()->AppUnitsPerDevPixel());
891 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset, const int32_t aAPD) const
893 // aOffset is based on the view's position, which ignores any chrome on
894 // attached parent widgets.
896 // We accumulate the final result in pt
897 nsPoint pt(0, 0);
898 // The offset currently accumulated at the current APD
899 nsPoint docPt(0,0);
900 const nsView* v = this;
901 nsViewManager* currVM = v->GetViewManager();
902 int32_t currAPD = currVM->AppUnitsPerDevPixel();
903 for ( ; v && !v->HasWidget(); v = v->GetParent()) {
904 nsViewManager* newVM = v->GetViewManager();
905 if (newVM != currVM) {
906 int32_t newAPD = newVM->AppUnitsPerDevPixel();
907 if (newAPD != currAPD) {
908 pt += docPt.ConvertAppUnits(currAPD, aAPD);
909 docPt.x = docPt.y = 0;
910 currAPD = newAPD;
912 currVM = newVM;
914 docPt += v->GetPosition();
916 if (!v) {
917 if (aOffset) {
918 pt += docPt.ConvertAppUnits(currAPD, aAPD);
919 *aOffset = pt;
921 return nullptr;
924 // pt is now the offset from v's origin to this view's origin.
925 // We add the ViewToWidgetOffset to get the offset to the widget.
926 if (aOffset) {
927 docPt += v->ViewToWidgetOffset();
928 pt += docPt.ConvertAppUnits(currAPD, aAPD);
929 *aOffset = pt;
931 return v->GetWidget();
934 bool nsView::IsRoot() const
936 NS_ASSERTION(mViewManager != nullptr," View manager is null in nsView::IsRoot()");
937 return mViewManager->GetRootView() == this;
940 nsRect
941 nsView::GetBoundsInParentUnits() const
943 nsView* parent = GetParent();
944 nsViewManager* VM = GetViewManager();
945 if (this != VM->GetRootView() || !parent) {
946 return mDimBounds;
948 int32_t ourAPD = VM->AppUnitsPerDevPixel();
949 int32_t parentAPD = parent->GetViewManager()->AppUnitsPerDevPixel();
950 return mDimBounds.ConvertAppUnitsRoundOut(ourAPD, parentAPD);
953 nsPoint
954 nsView::ConvertFromParentCoords(nsPoint aPt) const
956 const nsView* parent = GetParent();
957 if (parent) {
958 aPt = aPt.ConvertAppUnits(parent->GetViewManager()->AppUnitsPerDevPixel(),
959 GetViewManager()->AppUnitsPerDevPixel());
961 aPt -= GetPosition();
962 return aPt;
965 static bool
966 IsPopupWidget(nsIWidget* aWidget)
968 nsWindowType type;
969 aWidget->GetWindowType(type);
970 return (type == eWindowType_popup);
973 nsIPresShell*
974 nsView::GetPresShell()
976 return GetViewManager()->GetPresShell();
979 bool
980 nsView::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y)
982 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
983 if (pm && IsPopupWidget(aWidget)) {
984 pm->PopupMoved(mFrame, nsIntPoint(x, y));
985 return true;
988 return false;
991 bool
992 nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight)
994 // The root view may not be set if this is the resize associated with
995 // window creation
996 SetForcedRepaint(true);
997 if (this == mViewManager->GetRootView()) {
998 nsRefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext();
999 // ensure DPI is up-to-date, in case of window being opened and sized
1000 // on a non-default-dpi display (bug 829963)
1001 devContext->CheckDPIChange();
1002 int32_t p2a = devContext->AppUnitsPerDevPixel();
1003 mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(aWidth, p2a),
1004 NSIntPixelsToAppUnits(aHeight, p2a));
1005 return true;
1007 else if (IsPopupWidget(aWidget)) {
1008 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1009 if (pm) {
1010 pm->PopupResized(mFrame, nsIntSize(aWidth, aHeight));
1011 return true;
1015 return false;
1018 bool
1019 nsView::RequestWindowClose(nsIWidget* aWidget)
1021 if (mFrame && IsPopupWidget(aWidget) &&
1022 mFrame->GetType() == nsGkAtoms::menuPopupFrame) {
1023 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1024 if (pm) {
1025 pm->HidePopup(mFrame->GetContent(), false, true, false);
1026 return true;
1030 return false;
1033 void
1034 nsView::WillPaintWindow(nsIWidget* aWidget)
1036 nsRefPtr<nsViewManager> vm = mViewManager;
1037 vm->WillPaintWindow(aWidget);
1040 bool
1041 nsView::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion)
1043 NS_ASSERTION(this == nsView::GetViewFor(aWidget), "wrong view for widget?");
1045 nsRefPtr<nsViewManager> vm = mViewManager;
1046 bool result = vm->PaintWindow(aWidget, aRegion);
1047 return result;
1050 void
1051 nsView::DidPaintWindow()
1053 nsRefPtr<nsViewManager> vm = mViewManager;
1054 vm->DidPaintWindow();
1057 void
1058 nsView::RequestRepaint()
1060 nsIPresShell* presShell = mViewManager->GetPresShell();
1061 if (presShell) {
1062 presShell->ScheduleViewManagerFlush();
1066 nsEventStatus
1067 nsView::HandleEvent(WidgetGUIEvent* aEvent,
1068 bool aUseAttachedEvents)
1070 NS_PRECONDITION(nullptr != aEvent->widget, "null widget ptr");
1072 nsEventStatus result = nsEventStatus_eIgnore;
1073 nsView* view;
1074 if (aUseAttachedEvents) {
1075 nsIWidgetListener* listener = aEvent->widget->GetAttachedWidgetListener();
1076 view = listener ? listener->GetView() : nullptr;
1078 else {
1079 view = GetViewFor(aEvent->widget);
1082 if (view) {
1083 nsRefPtr<nsViewManager> vm = view->GetViewManager();
1084 vm->DispatchEvent(aEvent, view, &result);
1087 return result;