Bumping manifests a=b2g-bump
[gecko.git] / view / nsView.cpp
blob1f6a745e045df3fb5bbdf9f24e19145ca3095bd0
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 "nsIWidget.h"
15 #include "nsViewManager.h"
16 #include "nsIFrame.h"
17 #include "nsPresArena.h"
18 #include "nsXULPopupManager.h"
19 #include "nsIWidgetListener.h"
20 #include "nsContentUtils.h" // for nsAutoScriptBlocker
22 using namespace mozilla;
24 nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility)
26 MOZ_COUNT_CTOR(nsView);
28 mVis = aVisibility;
29 // Views should be transparent by default. Not being transparent is
30 // a promise that the view will paint all its pixels opaquely. Views
31 // should make this promise explicitly by calling
32 // SetViewContentTransparency.
33 mVFlags = 0;
34 mViewManager = aViewManager;
35 mDirtyRegion = nullptr;
36 mWidgetIsTopLevel = false;
39 void nsView::DropMouseGrabbing()
41 nsIPresShell* presShell = mViewManager->GetPresShell();
42 if (presShell)
43 presShell->ClearMouseCaptureOnView(this);
46 nsView::~nsView()
48 MOZ_COUNT_DTOR(nsView);
50 while (GetFirstChild())
52 nsView* child = GetFirstChild();
53 if (child->GetViewManager() == mViewManager) {
54 child->Destroy();
55 } else {
56 // just unhook it. Someone else will want to destroy this.
57 RemoveChild(child);
61 if (mViewManager)
63 DropMouseGrabbing();
65 nsView *rootView = mViewManager->GetRootView();
67 if (rootView)
69 // Root views can have parents!
70 if (mParent)
72 mViewManager->RemoveChild(this);
75 if (rootView == this)
77 // Inform the view manager that the root view has gone away...
78 mViewManager->SetRootView(nullptr);
81 else if (mParent)
83 mParent->RemoveChild(this);
86 mViewManager = nullptr;
88 else if (mParent)
90 mParent->RemoveChild(this);
93 // Destroy and release the widget
94 DestroyWidget();
96 delete mDirtyRegion;
99 class DestroyWidgetRunnable : public nsRunnable {
100 public:
101 NS_DECL_NSIRUNNABLE
103 explicit DestroyWidgetRunnable(nsIWidget* aWidget) : mWidget(aWidget) {}
105 private:
106 nsCOMPtr<nsIWidget> mWidget;
109 NS_IMETHODIMP DestroyWidgetRunnable::Run()
111 mWidget->Destroy();
112 mWidget = nullptr;
113 return NS_OK;
117 void nsView::DestroyWidget()
119 if (mWindow)
121 // If we are not attached to a base window, we're going to tear down our
122 // widget here. However, if we're attached to somebody elses widget, we
123 // want to leave the widget alone: don't reset the client data or call
124 // Destroy. Just clear our event view ptr and free our reference to it.
125 if (mWidgetIsTopLevel) {
126 mWindow->SetAttachedWidgetListener(nullptr);
128 else {
129 mWindow->SetWidgetListener(nullptr);
131 nsCOMPtr<nsIRunnable> widgetDestroyer =
132 new DestroyWidgetRunnable(mWindow);
134 NS_DispatchToMainThread(widgetDestroyer);
137 NS_RELEASE(mWindow);
141 nsView* nsView::GetViewFor(nsIWidget* aWidget)
143 NS_PRECONDITION(nullptr != aWidget, "null widget ptr");
145 nsIWidgetListener* listener = aWidget->GetWidgetListener();
146 if (listener) {
147 nsView* view = listener->GetView();
148 if (view)
149 return view;
152 listener = aWidget->GetAttachedWidgetListener();
153 return listener ? listener->GetView() : nullptr;
156 void nsView::Destroy()
158 this->~nsView();
159 mozWritePoison(this, sizeof(*this));
160 nsView::operator delete(this);
163 void nsView::SetPosition(nscoord aX, nscoord aY)
165 mDimBounds.x += aX - mPosX;
166 mDimBounds.y += aY - mPosY;
167 mPosX = aX;
168 mPosY = aY;
170 NS_ASSERTION(GetParent() || (aX == 0 && aY == 0),
171 "Don't try to move the root widget to something non-zero");
173 ResetWidgetBounds(true, false);
176 void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync)
178 if (mWindow) {
179 if (!aForceSync) {
180 // Don't change widget geometry synchronously, since that can
181 // cause synchronous painting.
182 mViewManager->PostPendingUpdate();
183 } else {
184 DoResetWidgetBounds(false, true);
186 return;
189 if (aRecurse) {
190 // reposition any widgets under this view
191 for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) {
192 v->ResetWidgetBounds(true, aForceSync);
197 bool nsView::IsEffectivelyVisible()
199 for (nsView* v = this; v; v = v->mParent) {
200 if (v->GetVisibility() == nsViewVisibility_kHide)
201 return false;
203 return true;
206 nsIntRect nsView::CalcWidgetBounds(nsWindowType aType)
208 int32_t p2a = mViewManager->AppUnitsPerDevPixel();
210 nsRect viewBounds(mDimBounds);
212 nsView* parent = GetParent();
213 nsIWidget* parentWidget = nullptr;
214 if (parent) {
215 nsPoint offset;
216 parentWidget = parent->GetNearestWidget(&offset, p2a);
217 // make viewBounds be relative to the parent widget, in appunits
218 viewBounds += offset;
220 if (parentWidget && aType == eWindowType_popup &&
221 IsEffectivelyVisible()) {
222 // put offset into screen coordinates. (based on client area origin)
223 nsIntPoint screenPoint = parentWidget->WidgetToScreenOffset();
224 viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a),
225 NSIntPixelsToAppUnits(screenPoint.y, p2a));
229 // Compute widget bounds in device pixels
230 nsIntRect newBounds = viewBounds.ToNearestPixels(p2a);
232 #ifdef XP_MACOSX
233 // cocoa rounds widget coordinates to the nearest global "display pixel"
234 // integer value. So we avoid fractional display pixel values by rounding
235 // to the nearest value that won't yield a fractional display pixel.
236 nsIWidget* widget = parentWidget ? parentWidget : mWindow;
237 uint32_t round;
238 if (aType == eWindowType_popup && widget &&
239 ((round = widget->RoundsWidgetCoordinatesTo()) > 1)) {
240 nsIntSize pixelRoundedSize = newBounds.Size();
241 // round the top left and bottom right to the nearest round pixel
242 newBounds.x = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.x, p2a) / round) * round;
243 newBounds.y = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.y, p2a) / round) * round;
244 newBounds.width =
245 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.XMost(), p2a) / round) * round - newBounds.x;
246 newBounds.height =
247 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.YMost(), p2a) / round) * round - newBounds.y;
248 // but if that makes the widget larger then our frame may not paint the
249 // extra pixels, so reduce the size to the nearest round value
250 if (newBounds.width > pixelRoundedSize.width) {
251 newBounds.width -= round;
253 if (newBounds.height > pixelRoundedSize.height) {
254 newBounds.height -= round;
257 #endif
259 // Compute where the top-left of our widget ended up relative to the parent
260 // widget, in appunits.
261 nsPoint roundedOffset(NSIntPixelsToAppUnits(newBounds.x, p2a),
262 NSIntPixelsToAppUnits(newBounds.y, p2a));
264 // mViewToWidgetOffset is added to coordinates relative to the view origin
265 // to get coordinates relative to the widget.
266 // The view origin, relative to the parent widget, is at
267 // (mPosX,mPosY) - mDimBounds.TopLeft() + viewBounds.TopLeft().
268 // Our widget, relative to the parent widget, is roundedOffset.
269 mViewToWidgetOffset = nsPoint(mPosX, mPosY)
270 - mDimBounds.TopLeft() + viewBounds.TopLeft() - roundedOffset;
272 return newBounds;
275 void nsView::DoResetWidgetBounds(bool aMoveOnly,
276 bool aInvalidateChangedSize) {
277 // The geometry of a root view's widget is controlled externally,
278 // NOT by sizing or positioning the view
279 if (mViewManager->GetRootView() == this) {
280 return;
283 NS_PRECONDITION(mWindow, "Why was this called??");
285 // Hold this ref to make sure it stays alive.
286 nsCOMPtr<nsIWidget> widget = mWindow;
288 // Stash a copy of these and use them so we can handle this being deleted (say
289 // from sync painting/flushing from Show/Move/Resize on the widget).
290 nsIntRect newBounds;
291 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
293 nsWindowType type = widget->WindowType();
295 nsIntRect curBounds;
296 widget->GetClientBounds(curBounds);
297 bool invisiblePopup = type == eWindowType_popup &&
298 ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) ||
299 mVis == nsViewVisibility_kHide);
301 if (invisiblePopup) {
302 // We're going to hit the early exit below, avoid calling CalcWidgetBounds.
303 } else {
304 newBounds = CalcWidgetBounds(type);
307 bool curVisibility = widget->IsVisible();
308 bool newVisibility = IsEffectivelyVisible();
309 if (curVisibility && !newVisibility) {
310 widget->Show(false);
313 if (invisiblePopup) {
314 // Don't manipulate empty or hidden popup widgets. For example there's no
315 // point moving hidden comboboxes around, or doing X server roundtrips
316 // to compute their true screen position. This could mean that WidgetToScreen
317 // operations on these widgets don't return up-to-date values, but popup
318 // positions aren't reliable anyway because of correction to be on or off-screen.
319 return;
322 bool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
323 bool changedSize = curBounds.Size() != newBounds.Size();
325 // Child views are never attached to top level widgets, this is safe.
327 // Coordinates are converted to display pixels for window Move/Resize APIs,
328 // because of the potential for device-pixel coordinate spaces for mixed
329 // hidpi/lodpi screens to overlap each other and result in bad placement
330 // (bug 814434).
331 double invScale;
333 // Bug 861270: for correct widget manipulation at arbitrary scale factors,
334 // prefer to base scaling on widget->GetDefaultScale(). But only do this if
335 // it matches the view manager's device context scale after allowing for the
336 // quantization to app units, because of OS X multiscreen issues (where the
337 // only two scales are 1.0 or 2.0, and so the quantization doesn't actually
338 // cause problems anyhow).
339 // In the case of a mismatch, fall back to scaling based on the dev context's
340 // unscaledAppUnitsPerDevPixel value. On platforms where the device-pixel
341 // scale is uniform across all displays (currently all except OS X), we'll
342 // always use the precise value from mWindow->GetDefaultScale here.
343 CSSToLayoutDeviceScale scale = widget->GetDefaultScale();
344 if (NSToIntRound(60.0 / scale.scale) == dx->UnscaledAppUnitsPerDevPixel()) {
345 invScale = 1.0 / scale.scale;
346 } else {
347 invScale = dx->UnscaledAppUnitsPerDevPixel() / 60.0;
350 if (changedPos) {
351 if (changedSize && !aMoveOnly) {
352 widget->ResizeClient(newBounds.x * invScale,
353 newBounds.y * invScale,
354 newBounds.width * invScale,
355 newBounds.height * invScale,
356 aInvalidateChangedSize);
357 } else {
358 widget->MoveClient(newBounds.x * invScale,
359 newBounds.y * invScale);
361 } else {
362 if (changedSize && !aMoveOnly) {
363 widget->ResizeClient(newBounds.width * invScale,
364 newBounds.height * invScale,
365 aInvalidateChangedSize);
366 } // else do nothing!
369 if (!curVisibility && newVisibility) {
370 widget->Show(true);
374 void nsView::SetDimensions(const nsRect& aRect, bool aPaint, bool aResizeWidget)
376 nsRect dims = aRect;
377 dims.MoveBy(mPosX, mPosY);
379 // Don't use nsRect's operator== here, since it returns true when
380 // both rects are empty even if they have different widths and we
381 // have cases where that sort of thing matters to us.
382 if (mDimBounds.TopLeft() == dims.TopLeft() &&
383 mDimBounds.Size() == dims.Size()) {
384 return;
387 mDimBounds = dims;
389 if (aResizeWidget) {
390 ResetWidgetBounds(false, false);
394 void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible)
396 if (!aEffectivelyVisible)
398 DropMouseGrabbing();
401 SetForcedRepaint(true);
403 if (nullptr != mWindow)
405 ResetWidgetBounds(false, false);
408 for (nsView* child = mFirstChild; child; child = child->mNextSibling) {
409 if (child->mVis == nsViewVisibility_kHide) {
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(nsViewVisibility aVisibility)
420 mVis = aVisibility;
421 NotifyEffectiveVisibilityChanged(IsEffectivelyVisible());
424 void nsView::SetFloating(bool aFloatingView)
426 if (aFloatingView)
427 mVFlags |= NS_VIEW_FLAG_FLOATING;
428 else
429 mVFlags &= ~NS_VIEW_FLAG_FLOATING;
432 void nsView::InvalidateHierarchy(nsViewManager *aViewManagerParent)
434 if (mViewManager->GetRootView() == this)
435 mViewManager->InvalidateHierarchy();
437 for (nsView *child = mFirstChild; child; child = child->GetNextSibling())
438 child->InvalidateHierarchy(aViewManagerParent);
441 void nsView::InsertChild(nsView *aChild, nsView *aSibling)
443 NS_PRECONDITION(nullptr != aChild, "null ptr");
445 if (nullptr != aChild)
447 if (nullptr != aSibling)
449 #ifdef DEBUG
450 NS_ASSERTION(aSibling->GetParent() == this, "tried to insert view with invalid sibling");
451 #endif
452 //insert after sibling
453 aChild->SetNextSibling(aSibling->GetNextSibling());
454 aSibling->SetNextSibling(aChild);
456 else
458 aChild->SetNextSibling(mFirstChild);
459 mFirstChild = aChild;
461 aChild->SetParent(this);
463 // If we just inserted a root view, then update the RootViewManager
464 // on all view managers in the new subtree.
466 nsViewManager *vm = aChild->GetViewManager();
467 if (vm->GetRootView() == aChild)
469 aChild->InvalidateHierarchy(nullptr); // don't care about releasing grabs
474 void nsView::RemoveChild(nsView *child)
476 NS_PRECONDITION(nullptr != child, "null ptr");
478 if (nullptr != child)
480 nsView* prevKid = nullptr;
481 nsView* kid = mFirstChild;
482 DebugOnly<bool> found = false;
483 while (nullptr != kid) {
484 if (kid == child) {
485 if (nullptr != prevKid) {
486 prevKid->SetNextSibling(kid->GetNextSibling());
487 } else {
488 mFirstChild = kid->GetNextSibling();
490 child->SetParent(nullptr);
491 found = true;
492 break;
494 prevKid = kid;
495 kid = kid->GetNextSibling();
497 NS_ASSERTION(found, "tried to remove non child");
499 // If we just removed a root view, then update the RootViewManager
500 // on all view managers in the removed subtree.
502 nsViewManager *vm = child->GetViewManager();
503 if (vm->GetRootView() == child)
505 child->InvalidateHierarchy(GetViewManager());
510 // Native widgets ultimately just can't deal with the awesome power of
511 // CSS2 z-index. However, we set the z-index on the widget anyway
512 // because in many simple common cases the widgets do end up in the
513 // right order. We set each widget's z-index to the z-index of the
514 // nearest ancestor that has non-auto z-index.
515 static void UpdateNativeWidgetZIndexes(nsView* aView, int32_t aZIndex)
517 if (aView->HasWidget()) {
518 nsIWidget* widget = aView->GetWidget();
519 if (widget->GetZIndex() != aZIndex) {
520 widget->SetZIndex(aZIndex);
522 } else {
523 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
524 if (v->GetZIndexIsAuto()) {
525 UpdateNativeWidgetZIndexes(v, aZIndex);
531 static int32_t FindNonAutoZIndex(nsView* aView)
533 while (aView) {
534 if (!aView->GetZIndexIsAuto()) {
535 return aView->GetZIndex();
537 aView = aView->GetParent();
539 return 0;
542 struct DefaultWidgetInitData : public nsWidgetInitData {
543 DefaultWidgetInitData() : nsWidgetInitData()
545 mWindowType = eWindowType_child;
546 clipChildren = true;
547 clipSiblings = true;
551 nsresult nsView::CreateWidget(nsWidgetInitData *aWidgetInitData,
552 bool aEnableDragDrop,
553 bool aResetVisibility)
555 AssertNoWindow();
556 NS_ABORT_IF_FALSE(!aWidgetInitData ||
557 aWidgetInitData->mWindowType != eWindowType_popup,
558 "Use CreateWidgetForPopup");
560 DefaultWidgetInitData defaultInitData;
561 bool initDataPassedIn = !!aWidgetInitData;
562 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
563 defaultInitData.mListenForResizes =
564 (!initDataPassedIn && GetParent() &&
565 GetParent()->GetViewManager() != mViewManager);
567 nsIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
569 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
571 nsIWidget* parentWidget =
572 GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr;
573 if (!parentWidget) {
574 NS_ERROR("nsView::CreateWidget without suitable parent widget??");
575 return NS_ERROR_FAILURE;
578 // XXX: using aForceUseIWidgetParent=true to preserve previous
579 // semantics. It's not clear that it's actually needed.
580 mWindow = parentWidget->CreateChild(trect, dx, aWidgetInitData,
581 true).take();
582 if (!mWindow) {
583 return NS_ERROR_FAILURE;
586 InitializeWindow(aEnableDragDrop, aResetVisibility);
588 return NS_OK;
591 nsresult nsView::CreateWidgetForParent(nsIWidget* aParentWidget,
592 nsWidgetInitData *aWidgetInitData,
593 bool aEnableDragDrop,
594 bool aResetVisibility)
596 AssertNoWindow();
597 NS_ABORT_IF_FALSE(!aWidgetInitData ||
598 aWidgetInitData->mWindowType != eWindowType_popup,
599 "Use CreateWidgetForPopup");
600 NS_ABORT_IF_FALSE(aParentWidget, "Parent widget required");
602 DefaultWidgetInitData defaultInitData;
603 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
605 nsIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
607 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
609 mWindow =
610 aParentWidget->CreateChild(trect, dx, aWidgetInitData).take();
611 if (!mWindow) {
612 return NS_ERROR_FAILURE;
615 InitializeWindow(aEnableDragDrop, aResetVisibility);
617 return NS_OK;
620 nsresult nsView::CreateWidgetForPopup(nsWidgetInitData *aWidgetInitData,
621 nsIWidget* aParentWidget,
622 bool aEnableDragDrop,
623 bool aResetVisibility)
625 AssertNoWindow();
626 NS_ABORT_IF_FALSE(aWidgetInitData, "Widget init data required");
627 NS_ABORT_IF_FALSE(aWidgetInitData->mWindowType == eWindowType_popup,
628 "Use one of the other CreateWidget methods");
630 nsIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
632 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
634 // XXX/cjones: having these two separate creation cases seems ... um
635 // ... unnecessary, but it's the way the old code did it. Please
636 // unify them by first finding a suitable parent nsIWidget, then
637 // getting rid of aForceUseIWidgetParent.
638 if (aParentWidget) {
639 // XXX: using aForceUseIWidgetParent=true to preserve previous
640 // semantics. It's not clear that it's actually needed.
641 mWindow = aParentWidget->CreateChild(trect, dx, aWidgetInitData,
642 true).take();
644 else {
645 nsIWidget* nearestParent = GetParent() ? GetParent()->GetNearestWidget(nullptr)
646 : nullptr;
647 if (!nearestParent) {
648 // Without a parent, we can't make a popup. This can happen
649 // when printing
650 return NS_ERROR_FAILURE;
653 mWindow =
654 nearestParent->CreateChild(trect, dx, aWidgetInitData).take();
656 if (!mWindow) {
657 return NS_ERROR_FAILURE;
660 InitializeWindow(aEnableDragDrop, aResetVisibility);
662 return NS_OK;
665 void
666 nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility)
668 NS_ABORT_IF_FALSE(mWindow, "Must have a window to initialize");
670 mWindow->SetWidgetListener(this);
672 if (aEnableDragDrop) {
673 mWindow->EnableDragDrop(true);
676 // propagate the z-index to the widget.
677 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
679 //make sure visibility state is accurate
681 if (aResetVisibility) {
682 SetVisibility(GetVisibility());
686 // Attach to a top level widget and start receiving mirrored events.
687 nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget)
689 NS_PRECONDITION(nullptr != aWidget, "null widget ptr");
690 /// XXXjimm This is a temporary workaround to an issue w/document
691 // viewer (bug 513162).
692 nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener();
693 if (listener) {
694 nsView *oldView = listener->GetView();
695 if (oldView) {
696 oldView->DetachFromTopLevelWidget();
700 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
702 // Note, the previous device context will be released. Detaching
703 // will not restore the old one.
704 nsresult rv = aWidget->AttachViewToTopLevel(!nsIWidget::UsePuppetWidgets(), dx);
705 if (NS_FAILED(rv))
706 return rv;
708 mWindow = aWidget;
709 NS_ADDREF(mWindow);
711 mWindow->SetAttachedWidgetListener(this);
712 mWindow->EnableDragDrop(true);
713 mWidgetIsTopLevel = true;
715 // Refresh the view bounds
716 CalcWidgetBounds(mWindow->WindowType());
718 return NS_OK;
721 // Detach this view from an attached widget.
722 nsresult nsView::DetachFromTopLevelWidget()
724 NS_PRECONDITION(mWidgetIsTopLevel, "Not attached currently!");
725 NS_PRECONDITION(mWindow, "null mWindow for DetachFromTopLevelWidget!");
727 mWindow->SetAttachedWidgetListener(nullptr);
728 NS_RELEASE(mWindow);
730 mWidgetIsTopLevel = false;
732 return NS_OK;
735 void nsView::SetZIndex(bool aAuto, int32_t aZIndex)
737 bool oldIsAuto = GetZIndexIsAuto();
738 mVFlags = (mVFlags & ~NS_VIEW_FLAG_AUTO_ZINDEX) | (aAuto ? NS_VIEW_FLAG_AUTO_ZINDEX : 0);
739 mZIndex = aZIndex;
741 if (HasWidget() || !oldIsAuto || !aAuto) {
742 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
746 void nsView::AssertNoWindow()
748 // XXX: it would be nice to make this a strong assert
749 if (MOZ_UNLIKELY(mWindow)) {
750 NS_ERROR("We already have a window for this view? BAD");
751 mWindow->SetWidgetListener(nullptr);
752 mWindow->Destroy();
753 NS_RELEASE(mWindow);
758 // internal window creation functions
760 void nsView::AttachWidgetEventHandler(nsIWidget* aWidget)
762 #ifdef DEBUG
763 NS_ASSERTION(!aWidget->GetWidgetListener(), "Already have a widget listener");
764 #endif
766 aWidget->SetWidgetListener(this);
769 void nsView::DetachWidgetEventHandler(nsIWidget* aWidget)
771 NS_ASSERTION(!aWidget->GetWidgetListener() ||
772 aWidget->GetWidgetListener()->GetView() == this, "Wrong view");
773 aWidget->SetWidgetListener(nullptr);
776 #ifdef DEBUG
777 void nsView::List(FILE* out, int32_t aIndent) const
779 int32_t i;
780 for (i = aIndent; --i >= 0; ) fputs(" ", out);
781 fprintf(out, "%p ", (void*)this);
782 if (nullptr != mWindow) {
783 nscoord p2a = mViewManager->AppUnitsPerDevPixel();
784 nsIntRect rect;
785 mWindow->GetClientBounds(rect);
786 nsRect windowBounds = rect.ToAppUnits(p2a);
787 mWindow->GetBounds(rect);
788 nsRect nonclientBounds = rect.ToAppUnits(p2a);
789 nsrefcnt widgetRefCnt = mWindow->AddRef() - 1;
790 mWindow->Release();
791 int32_t Z = mWindow->GetZIndex();
792 fprintf(out, "(widget=%p[%" PRIuPTR "] z=%d pos={%d,%d,%d,%d}) ",
793 (void*)mWindow, widgetRefCnt, Z,
794 nonclientBounds.x, nonclientBounds.y,
795 windowBounds.width, windowBounds.height);
797 nsRect brect = GetBounds();
798 fprintf(out, "{%d,%d,%d,%d}",
799 brect.x, brect.y, brect.width, brect.height);
800 fprintf(out, " z=%d vis=%d frame=%p <\n",
801 mZIndex, mVis, static_cast<void*>(mFrame));
802 for (nsView* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
803 NS_ASSERTION(kid->GetParent() == this, "incorrect parent");
804 kid->List(out, aIndent + 1);
806 for (i = aIndent; --i >= 0; ) fputs(" ", out);
807 fputs(">\n", out);
809 #endif // DEBUG
811 nsPoint nsView::GetOffsetTo(const nsView* aOther) const
813 return GetOffsetTo(aOther, GetViewManager()->AppUnitsPerDevPixel());
816 nsPoint nsView::GetOffsetTo(const nsView* aOther, const int32_t aAPD) const
818 NS_ABORT_IF_FALSE(GetParent() || !aOther || aOther->GetParent() ||
819 this == aOther, "caller of (outer) GetOffsetTo must not "
820 "pass unrelated views");
821 // We accumulate the final result in offset
822 nsPoint offset(0, 0);
823 // The offset currently accumulated at the current APD
824 nsPoint docOffset(0, 0);
825 const nsView* v = this;
826 nsViewManager* currVM = v->GetViewManager();
827 int32_t currAPD = currVM->AppUnitsPerDevPixel();
828 const nsView* root = nullptr;
829 for ( ; v != aOther && v; root = v, v = v->GetParent()) {
830 nsViewManager* newVM = v->GetViewManager();
831 if (newVM != currVM) {
832 int32_t newAPD = newVM->AppUnitsPerDevPixel();
833 if (newAPD != currAPD) {
834 offset += docOffset.ConvertAppUnits(currAPD, aAPD);
835 docOffset.x = docOffset.y = 0;
836 currAPD = newAPD;
838 currVM = newVM;
840 docOffset += v->GetPosition();
842 offset += docOffset.ConvertAppUnits(currAPD, aAPD);
844 if (v != aOther) {
845 // Looks like aOther wasn't an ancestor of |this|. So now we have
846 // the root-VM-relative position of |this| in |offset|. Get the
847 // root-VM-relative position of aOther and subtract it.
848 nsPoint negOffset = aOther->GetOffsetTo(root, aAPD);
849 offset -= negOffset;
852 return offset;
855 nsPoint nsView::GetOffsetToWidget(nsIWidget* aWidget) const
857 nsPoint pt;
858 // Get the view for widget
859 nsView* widgetView = GetViewFor(aWidget);
860 if (!widgetView) {
861 return pt;
864 // Get the offset to the widget view in the widget view's APD
865 // We get the offset in the widget view's APD first and then convert to our
866 // APD afterwards so that we can include the widget view's ViewToWidgetOffset
867 // in the sum in its native APD, and then convert the whole thing to our APD
868 // so that we don't have to convert the APD of the relatively small
869 // ViewToWidgetOffset by itself with a potentially large relative rounding
870 // error.
871 pt = -widgetView->GetOffsetTo(this);
872 // Add in the offset to the widget.
873 pt += widgetView->ViewToWidgetOffset();
875 // Convert to our appunits.
876 int32_t widgetAPD = widgetView->GetViewManager()->AppUnitsPerDevPixel();
877 int32_t ourAPD = GetViewManager()->AppUnitsPerDevPixel();
878 pt = pt.ConvertAppUnits(widgetAPD, ourAPD);
879 return pt;
882 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset) const
884 return GetNearestWidget(aOffset, GetViewManager()->AppUnitsPerDevPixel());
887 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset, const int32_t aAPD) const
889 // aOffset is based on the view's position, which ignores any chrome on
890 // attached parent widgets.
892 // We accumulate the final result in pt
893 nsPoint pt(0, 0);
894 // The offset currently accumulated at the current APD
895 nsPoint docPt(0,0);
896 const nsView* v = this;
897 nsViewManager* currVM = v->GetViewManager();
898 int32_t currAPD = currVM->AppUnitsPerDevPixel();
899 for ( ; v && !v->HasWidget(); v = v->GetParent()) {
900 nsViewManager* newVM = v->GetViewManager();
901 if (newVM != currVM) {
902 int32_t newAPD = newVM->AppUnitsPerDevPixel();
903 if (newAPD != currAPD) {
904 pt += docPt.ConvertAppUnits(currAPD, aAPD);
905 docPt.x = docPt.y = 0;
906 currAPD = newAPD;
908 currVM = newVM;
910 docPt += v->GetPosition();
912 if (!v) {
913 if (aOffset) {
914 pt += docPt.ConvertAppUnits(currAPD, aAPD);
915 *aOffset = pt;
917 return nullptr;
920 // pt is now the offset from v's origin to this view's origin.
921 // We add the ViewToWidgetOffset to get the offset to the widget.
922 if (aOffset) {
923 docPt += v->ViewToWidgetOffset();
924 pt += docPt.ConvertAppUnits(currAPD, aAPD);
925 *aOffset = pt;
927 return v->GetWidget();
930 bool nsView::IsRoot() const
932 NS_ASSERTION(mViewManager != nullptr," View manager is null in nsView::IsRoot()");
933 return mViewManager->GetRootView() == this;
936 nsRect
937 nsView::GetBoundsInParentUnits() const
939 nsView* parent = GetParent();
940 nsViewManager* VM = GetViewManager();
941 if (this != VM->GetRootView() || !parent) {
942 return mDimBounds;
944 int32_t ourAPD = VM->AppUnitsPerDevPixel();
945 int32_t parentAPD = parent->GetViewManager()->AppUnitsPerDevPixel();
946 return mDimBounds.ConvertAppUnitsRoundOut(ourAPD, parentAPD);
949 nsPoint
950 nsView::ConvertFromParentCoords(nsPoint aPt) const
952 const nsView* parent = GetParent();
953 if (parent) {
954 aPt = aPt.ConvertAppUnits(parent->GetViewManager()->AppUnitsPerDevPixel(),
955 GetViewManager()->AppUnitsPerDevPixel());
957 aPt -= GetPosition();
958 return aPt;
961 static bool
962 IsPopupWidget(nsIWidget* aWidget)
964 return (aWidget->WindowType() == eWindowType_popup);
967 nsIPresShell*
968 nsView::GetPresShell()
970 return GetViewManager()->GetPresShell();
973 bool
974 nsView::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y)
976 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
977 if (pm && IsPopupWidget(aWidget)) {
978 pm->PopupMoved(mFrame, nsIntPoint(x, y));
979 return true;
982 return false;
985 bool
986 nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight)
988 // The root view may not be set if this is the resize associated with
989 // window creation
990 SetForcedRepaint(true);
991 if (this == mViewManager->GetRootView()) {
992 nsRefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext();
993 // ensure DPI is up-to-date, in case of window being opened and sized
994 // on a non-default-dpi display (bug 829963)
995 devContext->CheckDPIChange();
996 int32_t p2a = devContext->AppUnitsPerDevPixel();
997 mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(aWidth, p2a),
998 NSIntPixelsToAppUnits(aHeight, p2a));
1000 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1001 if (pm) {
1002 nsIPresShell* presShell = mViewManager->GetPresShell();
1003 if (presShell && presShell->GetDocument()) {
1004 pm->AdjustPopupsOnWindowChange(presShell);
1008 return true;
1010 else if (IsPopupWidget(aWidget)) {
1011 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1012 if (pm) {
1013 pm->PopupResized(mFrame, nsIntSize(aWidth, aHeight));
1014 return true;
1018 return false;
1021 bool
1022 nsView::RequestWindowClose(nsIWidget* aWidget)
1024 if (mFrame && IsPopupWidget(aWidget) &&
1025 mFrame->GetType() == nsGkAtoms::menuPopupFrame) {
1026 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1027 if (pm) {
1028 pm->HidePopup(mFrame->GetContent(), false, true, false, false);
1029 return true;
1033 return false;
1036 void
1037 nsView::WillPaintWindow(nsIWidget* aWidget)
1039 nsRefPtr<nsViewManager> vm = mViewManager;
1040 vm->WillPaintWindow(aWidget);
1043 bool
1044 nsView::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion)
1046 NS_ASSERTION(this == nsView::GetViewFor(aWidget), "wrong view for widget?");
1048 nsRefPtr<nsViewManager> vm = mViewManager;
1049 bool result = vm->PaintWindow(aWidget, aRegion);
1050 return result;
1053 void
1054 nsView::DidPaintWindow()
1056 nsRefPtr<nsViewManager> vm = mViewManager;
1057 vm->DidPaintWindow();
1060 void
1061 nsView::DidCompositeWindow()
1063 nsIPresShell* presShell = mViewManager->GetPresShell();
1064 if (presShell) {
1065 nsAutoScriptBlocker scriptBlocker;
1066 presShell->GetPresContext()->GetDisplayRootPresContext()->GetRootPresContext()->NotifyDidPaintForSubtree(nsIPresShell::PAINT_COMPOSITE);
1070 void
1071 nsView::RequestRepaint()
1073 nsIPresShell* presShell = mViewManager->GetPresShell();
1074 if (presShell) {
1075 presShell->ScheduleViewManagerFlush();
1079 nsEventStatus
1080 nsView::HandleEvent(WidgetGUIEvent* aEvent,
1081 bool aUseAttachedEvents)
1083 NS_PRECONDITION(nullptr != aEvent->widget, "null widget ptr");
1085 nsEventStatus result = nsEventStatus_eIgnore;
1086 nsView* view;
1087 if (aUseAttachedEvents) {
1088 nsIWidgetListener* listener = aEvent->widget->GetAttachedWidgetListener();
1089 view = listener ? listener->GetView() : nullptr;
1091 else {
1092 view = GetViewFor(aEvent->widget);
1095 if (view) {
1096 nsRefPtr<nsViewManager> vm = view->GetViewManager();
1097 vm->DispatchEvent(aEvent, view, &result);
1100 return result;