Bug 1540028 [wpt PR 16099] - Catch more exceptions in Document-createElement-namespac...
[gecko.git] / view / nsView.cpp
blob8991edec98564bd8f6f9dcef2336dc9da3d17903
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 "nsIWidget.h"
16 #include "nsViewManager.h"
17 #include "nsIFrame.h"
18 #include "nsPresArena.h"
19 #include "nsXULPopupManager.h"
20 #include "nsIWidgetListener.h"
21 #include "nsContentUtils.h" // for nsAutoScriptBlocker
22 #include "mozilla/TimelineConsumers.h"
23 #include "mozilla/CompositeTimelineMarker.h"
25 using namespace mozilla;
27 static bool sShowPreviousPage = true;
29 nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility)
30 : mViewManager(aViewManager),
31 mParent(nullptr),
32 mNextSibling(nullptr),
33 mFirstChild(nullptr),
34 mFrame(nullptr),
35 mDirtyRegion(nullptr),
36 mZIndex(0),
37 mVis(aVisibility),
38 mPosX(0),
39 mPosY(0),
40 mVFlags(0),
41 mWidgetIsTopLevel(false),
42 mForcedRepaint(false),
43 mNeedsWindowPropertiesSync(false) {
44 MOZ_COUNT_CTOR(nsView);
46 // Views should be transparent by default. Not being transparent is
47 // a promise that the view will paint all its pixels opaquely. Views
48 // should make this promise explicitly by calling
49 // SetViewContentTransparency.
51 static bool sShowPreviousPageInitialized = false;
52 if (!sShowPreviousPageInitialized) {
53 Preferences::AddBoolVarCache(&sShowPreviousPage,
54 "layout.show_previous_page", true);
55 sShowPreviousPageInitialized = true;
59 void nsView::DropMouseGrabbing() {
60 if (PresShell* presShell = mViewManager->GetPresShell()) {
61 presShell->ClearMouseCaptureOnView(this);
65 nsView::~nsView() {
66 MOZ_COUNT_DTOR(nsView);
68 while (GetFirstChild()) {
69 nsView* child = GetFirstChild();
70 if (child->GetViewManager() == mViewManager) {
71 child->Destroy();
72 } else {
73 // just unhook it. Someone else will want to destroy this.
74 RemoveChild(child);
78 if (mViewManager) {
79 DropMouseGrabbing();
81 nsView* rootView = mViewManager->GetRootView();
83 if (rootView) {
84 // Root views can have parents!
85 if (mParent) {
86 mViewManager->RemoveChild(this);
89 if (rootView == this) {
90 // Inform the view manager that the root view has gone away...
91 mViewManager->SetRootView(nullptr);
93 } else if (mParent) {
94 mParent->RemoveChild(this);
97 mViewManager = nullptr;
98 } else if (mParent) {
99 mParent->RemoveChild(this);
102 if (mPreviousWindow) {
103 mPreviousWindow->SetPreviouslyAttachedWidgetListener(nullptr);
106 // Destroy and release the widget
107 DestroyWidget();
109 MOZ_RELEASE_ASSERT(!mFrame);
111 delete mDirtyRegion;
114 class DestroyWidgetRunnable : public Runnable {
115 public:
116 NS_DECL_NSIRUNNABLE
118 explicit DestroyWidgetRunnable(nsIWidget* aWidget)
119 : mozilla::Runnable("DestroyWidgetRunnable"), mWidget(aWidget) {}
121 private:
122 nsCOMPtr<nsIWidget> mWidget;
125 NS_IMETHODIMP DestroyWidgetRunnable::Run() {
126 mWidget->Destroy();
127 mWidget = nullptr;
128 return NS_OK;
131 void nsView::DestroyWidget() {
132 if (mWindow) {
133 // If we are not attached to a base window, we're going to tear down our
134 // widget here. However, if we're attached to somebody elses widget, we
135 // want to leave the widget alone: don't reset the client data or call
136 // Destroy. Just clear our event view ptr and free our reference to it.
137 if (mWidgetIsTopLevel) {
138 mWindow->SetAttachedWidgetListener(nullptr);
139 } else {
140 mWindow->SetWidgetListener(nullptr);
142 nsCOMPtr<nsIRunnable> widgetDestroyer =
143 new DestroyWidgetRunnable(mWindow);
145 // Don't leak if we happen to arrive here after the main thread
146 // has disappeared.
147 nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
148 if (mainThread) {
149 mainThread->Dispatch(widgetDestroyer.forget(), NS_DISPATCH_NORMAL);
153 mWindow = nullptr;
157 nsView* nsView::GetViewFor(nsIWidget* aWidget) {
158 MOZ_ASSERT(nullptr != aWidget, "null widget ptr");
160 nsIWidgetListener* listener = aWidget->GetWidgetListener();
161 if (listener) {
162 nsView* view = listener->GetView();
163 if (view) return view;
166 listener = aWidget->GetAttachedWidgetListener();
167 return listener ? listener->GetView() : nullptr;
170 void nsView::Destroy() {
171 this->~nsView();
172 mozWritePoison(this, sizeof(*this));
173 nsView::operator delete(this);
176 void nsView::SetPosition(nscoord aX, nscoord aY) {
177 mDimBounds.MoveBy(aX - mPosX, aY - mPosY);
178 mPosX = aX;
179 mPosY = aY;
181 NS_ASSERTION(GetParent() || (aX == 0 && aY == 0),
182 "Don't try to move the root widget to something non-zero");
184 ResetWidgetBounds(true, false);
187 void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync) {
188 if (mWindow) {
189 if (!aForceSync) {
190 // Don't change widget geometry synchronously, since that can
191 // cause synchronous painting.
192 mViewManager->PostPendingUpdate();
193 } else {
194 DoResetWidgetBounds(false, true);
196 return;
199 if (aRecurse) {
200 // reposition any widgets under this view
201 for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) {
202 v->ResetWidgetBounds(true, aForceSync);
207 bool nsView::IsEffectivelyVisible() {
208 for (nsView* v = this; v; v = v->mParent) {
209 if (v->GetVisibility() == nsViewVisibility_kHide) return false;
211 return true;
214 LayoutDeviceIntRect nsView::CalcWidgetBounds(nsWindowType aType) {
215 int32_t p2a = mViewManager->AppUnitsPerDevPixel();
217 nsRect viewBounds(mDimBounds);
219 nsView* parent = GetParent();
220 nsIWidget* parentWidget = nullptr;
221 if (parent) {
222 nsPoint offset;
223 parentWidget = parent->GetNearestWidget(&offset, p2a);
224 // make viewBounds be relative to the parent widget, in appunits
225 viewBounds += offset;
227 if (parentWidget && aType == eWindowType_popup && IsEffectivelyVisible()) {
228 // put offset into screen coordinates. (based on client area origin)
229 LayoutDeviceIntPoint screenPoint = parentWidget->WidgetToScreenOffset();
230 viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a),
231 NSIntPixelsToAppUnits(screenPoint.y, p2a));
235 // Compute widget bounds in device pixels
236 LayoutDeviceIntRect newBounds =
237 LayoutDeviceIntRect::FromUnknownRect(viewBounds.ToNearestPixels(p2a));
239 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
240 // cocoa and GTK round widget coordinates to the nearest global "display
241 // pixel" integer value. So we avoid fractional display pixel values by
242 // rounding to the nearest value that won't yield a fractional display pixel.
243 nsIWidget* widget = parentWidget ? parentWidget : mWindow.get();
244 uint32_t round;
245 if (aType == eWindowType_popup && widget &&
246 ((round = widget->RoundsWidgetCoordinatesTo()) > 1)) {
247 LayoutDeviceIntSize pixelRoundedSize = newBounds.Size();
248 // round the top left and bottom right to the nearest round pixel
249 newBounds.x =
250 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.x, p2a) / round) *
251 round;
252 newBounds.y =
253 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.y, p2a) / round) *
254 round;
255 newBounds.width =
256 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.XMost(), p2a) /
257 round) *
258 round -
259 newBounds.x;
260 newBounds.height =
261 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.YMost(), p2a) /
262 round) *
263 round -
264 newBounds.y;
265 // but if that makes the widget larger then our frame may not paint the
266 // extra pixels, so reduce the size to the nearest round value
267 if (newBounds.width > pixelRoundedSize.width) {
268 newBounds.width -= round;
270 if (newBounds.height > pixelRoundedSize.height) {
271 newBounds.height -= round;
274 #endif
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 void nsView::DoResetWidgetBounds(bool aMoveOnly, bool aInvalidateChangedSize) {
293 // The geometry of a root view's widget is controlled externally,
294 // NOT by sizing or positioning the view
295 if (mViewManager->GetRootView() == this) {
296 return;
299 MOZ_ASSERT(mWindow, "Why was this called??");
301 // Hold this ref to make sure it stays alive.
302 nsCOMPtr<nsIWidget> widget = mWindow;
304 // Stash a copy of these and use them so we can handle this being deleted (say
305 // from sync painting/flushing from Show/Move/Resize on the widget).
306 LayoutDeviceIntRect newBounds;
308 nsWindowType type = widget->WindowType();
310 LayoutDeviceIntRect curBounds = widget->GetClientBounds();
311 bool invisiblePopup = type == eWindowType_popup &&
312 ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) ||
313 mVis == nsViewVisibility_kHide);
315 if (invisiblePopup) {
316 // We're going to hit the early exit below, avoid calling CalcWidgetBounds.
317 } else {
318 newBounds = CalcWidgetBounds(type);
319 invisiblePopup = newBounds.IsEmpty();
322 bool curVisibility = widget->IsVisible();
323 bool newVisibility = !invisiblePopup && IsEffectivelyVisible();
324 if (curVisibility && !newVisibility) {
325 widget->Show(false);
328 if (invisiblePopup) {
329 // Don't manipulate empty or hidden popup widgets. For example there's no
330 // point moving hidden comboboxes around, or doing X server roundtrips
331 // to compute their true screen position. This could mean that
332 // WidgetToScreen operations on these widgets don't return up-to-date
333 // values, but popup positions aren't reliable anyway because of correction
334 // to be on or off-screen.
335 return;
338 bool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
339 bool changedSize = curBounds.Size() != newBounds.Size();
341 // Child views are never attached to top level widgets, this is safe.
343 // Coordinates are converted to desktop pixels for window Move/Resize APIs,
344 // because of the potential for device-pixel coordinate spaces for mixed
345 // hidpi/lodpi screens to overlap each other and result in bad placement
346 // (bug 814434).
348 DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScaleByScreen();
350 DesktopRect deskRect = newBounds / scale;
351 if (changedPos) {
352 if (changedSize && !aMoveOnly) {
353 widget->ResizeClient(deskRect.X(), deskRect.Y(), deskRect.Width(),
354 deskRect.Height(), aInvalidateChangedSize);
355 } else {
356 widget->MoveClient(deskRect.X(), deskRect.Y());
358 } else {
359 if (changedSize && !aMoveOnly) {
360 widget->ResizeClient(deskRect.Width(), deskRect.Height(),
361 aInvalidateChangedSize);
362 } // else do nothing!
365 if (!curVisibility && newVisibility) {
366 widget->Show(true);
370 void nsView::SetDimensions(const nsRect& aRect, bool aPaint,
371 bool aResizeWidget) {
372 nsRect dims = aRect;
373 dims.MoveBy(mPosX, mPosY);
375 // Don't use nsRect's operator== here, since it returns true when
376 // both rects are empty even if they have different widths and we
377 // have cases where that sort of thing matters to us.
378 if (mDimBounds.TopLeft() == dims.TopLeft() &&
379 mDimBounds.Size() == dims.Size()) {
380 return;
383 mDimBounds = dims;
385 if (aResizeWidget) {
386 ResetWidgetBounds(false, false);
390 void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible) {
391 if (!aEffectivelyVisible) {
392 DropMouseGrabbing();
395 SetForcedRepaint(true);
397 if (nullptr != mWindow) {
398 ResetWidgetBounds(false, false);
401 for (nsView* child = mFirstChild; child; child = child->mNextSibling) {
402 if (child->mVis == nsViewVisibility_kHide) {
403 // It was effectively hidden and still is
404 continue;
406 // Our child is visible if we are
407 child->NotifyEffectiveVisibilityChanged(aEffectivelyVisible);
411 void nsView::SetVisibility(nsViewVisibility aVisibility) {
412 mVis = aVisibility;
413 NotifyEffectiveVisibilityChanged(IsEffectivelyVisible());
416 void nsView::SetFloating(bool aFloatingView) {
417 if (aFloatingView)
418 mVFlags |= NS_VIEW_FLAG_FLOATING;
419 else
420 mVFlags &= ~NS_VIEW_FLAG_FLOATING;
423 void nsView::InvalidateHierarchy() {
424 if (mViewManager->GetRootView() == this) mViewManager->InvalidateHierarchy();
426 for (nsView* child = mFirstChild; child; child = child->GetNextSibling())
427 child->InvalidateHierarchy();
430 void nsView::InsertChild(nsView* aChild, nsView* aSibling) {
431 MOZ_ASSERT(nullptr != aChild, "null ptr");
433 if (nullptr != aChild) {
434 if (nullptr != aSibling) {
435 #ifdef DEBUG
436 NS_ASSERTION(aSibling->GetParent() == this,
437 "tried to insert view with invalid sibling");
438 #endif
439 // insert after sibling
440 aChild->SetNextSibling(aSibling->GetNextSibling());
441 aSibling->SetNextSibling(aChild);
442 } else {
443 aChild->SetNextSibling(mFirstChild);
444 mFirstChild = aChild;
446 aChild->SetParent(this);
448 // If we just inserted a root view, then update the RootViewManager
449 // on all view managers in the new subtree.
451 nsViewManager* vm = aChild->GetViewManager();
452 if (vm->GetRootView() == aChild) {
453 aChild->InvalidateHierarchy();
458 void nsView::RemoveChild(nsView* child) {
459 MOZ_ASSERT(nullptr != child, "null ptr");
461 if (nullptr != child) {
462 nsView* prevKid = nullptr;
463 nsView* kid = mFirstChild;
464 DebugOnly<bool> found = false;
465 while (nullptr != kid) {
466 if (kid == child) {
467 if (nullptr != prevKid) {
468 prevKid->SetNextSibling(kid->GetNextSibling());
469 } else {
470 mFirstChild = kid->GetNextSibling();
472 child->SetParent(nullptr);
473 found = true;
474 break;
476 prevKid = kid;
477 kid = kid->GetNextSibling();
479 NS_ASSERTION(found, "tried to remove non child");
481 // If we just removed a root view, then update the RootViewManager
482 // on all view managers in the removed subtree.
484 nsViewManager* vm = child->GetViewManager();
485 if (vm->GetRootView() == child) {
486 child->InvalidateHierarchy();
491 // Native widgets ultimately just can't deal with the awesome power of
492 // CSS2 z-index. However, we set the z-index on the widget anyway
493 // because in many simple common cases the widgets do end up in the
494 // right order. We set each widget's z-index to the z-index of the
495 // nearest ancestor that has non-auto z-index.
496 static void UpdateNativeWidgetZIndexes(nsView* aView, int32_t aZIndex) {
497 if (aView->HasWidget()) {
498 nsIWidget* widget = aView->GetWidget();
499 if (widget->GetZIndex() != aZIndex) {
500 widget->SetZIndex(aZIndex);
502 } else {
503 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
504 if (v->GetZIndexIsAuto()) {
505 UpdateNativeWidgetZIndexes(v, aZIndex);
511 static int32_t FindNonAutoZIndex(nsView* aView) {
512 while (aView) {
513 if (!aView->GetZIndexIsAuto()) {
514 return aView->GetZIndex();
516 aView = aView->GetParent();
518 return 0;
521 struct DefaultWidgetInitData : public nsWidgetInitData {
522 DefaultWidgetInitData() : nsWidgetInitData() {
523 mWindowType = eWindowType_child;
524 clipChildren = true;
525 clipSiblings = true;
529 nsresult nsView::CreateWidget(nsWidgetInitData* aWidgetInitData,
530 bool aEnableDragDrop, bool aResetVisibility) {
531 AssertNoWindow();
532 MOZ_ASSERT(
533 !aWidgetInitData || aWidgetInitData->mWindowType != eWindowType_popup,
534 "Use CreateWidgetForPopup");
536 DefaultWidgetInitData defaultInitData;
537 bool initDataPassedIn = !!aWidgetInitData;
538 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
539 defaultInitData.mListenForResizes =
540 (!initDataPassedIn && GetParent() &&
541 GetParent()->GetViewManager() != mViewManager);
543 LayoutDeviceIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
545 nsIWidget* parentWidget =
546 GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr;
547 if (!parentWidget) {
548 NS_ERROR("nsView::CreateWidget without suitable parent widget??");
549 return NS_ERROR_FAILURE;
552 // XXX: using aForceUseIWidgetParent=true to preserve previous
553 // semantics. It's not clear that it's actually needed.
554 mWindow = parentWidget->CreateChild(trect, aWidgetInitData, true);
555 if (!mWindow) {
556 return NS_ERROR_FAILURE;
559 InitializeWindow(aEnableDragDrop, aResetVisibility);
561 return NS_OK;
564 nsresult nsView::CreateWidgetForParent(nsIWidget* aParentWidget,
565 nsWidgetInitData* aWidgetInitData,
566 bool aEnableDragDrop,
567 bool aResetVisibility) {
568 AssertNoWindow();
569 MOZ_ASSERT(
570 !aWidgetInitData || aWidgetInitData->mWindowType != eWindowType_popup,
571 "Use CreateWidgetForPopup");
572 MOZ_ASSERT(aParentWidget, "Parent widget required");
574 DefaultWidgetInitData defaultInitData;
575 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
577 LayoutDeviceIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
579 mWindow = aParentWidget->CreateChild(trect, aWidgetInitData);
580 if (!mWindow) {
581 return NS_ERROR_FAILURE;
584 InitializeWindow(aEnableDragDrop, aResetVisibility);
586 return NS_OK;
589 nsresult nsView::CreateWidgetForPopup(nsWidgetInitData* aWidgetInitData,
590 nsIWidget* aParentWidget,
591 bool aEnableDragDrop,
592 bool aResetVisibility) {
593 AssertNoWindow();
594 MOZ_ASSERT(aWidgetInitData, "Widget init data required");
595 MOZ_ASSERT(aWidgetInitData->mWindowType == eWindowType_popup,
596 "Use one of the other CreateWidget methods");
598 LayoutDeviceIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
600 // XXX/cjones: having these two separate creation cases seems ... um
601 // ... unnecessary, but it's the way the old code did it. Please
602 // unify them by first finding a suitable parent nsIWidget, then
603 // getting rid of aForceUseIWidgetParent.
604 if (aParentWidget) {
605 // XXX: using aForceUseIWidgetParent=true to preserve previous
606 // semantics. It's not clear that it's actually needed.
607 mWindow = aParentWidget->CreateChild(trect, aWidgetInitData, true);
608 } else {
609 nsIWidget* nearestParent =
610 GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr;
611 if (!nearestParent) {
612 // Without a parent, we can't make a popup. This can happen
613 // when printing
614 return NS_ERROR_FAILURE;
617 mWindow = nearestParent->CreateChild(trect, aWidgetInitData);
619 if (!mWindow) {
620 return NS_ERROR_FAILURE;
623 InitializeWindow(aEnableDragDrop, aResetVisibility);
625 return NS_OK;
628 void nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility) {
629 MOZ_ASSERT(mWindow, "Must have a window to initialize");
631 mWindow->SetWidgetListener(this);
633 if (aEnableDragDrop) {
634 mWindow->EnableDragDrop(true);
637 // propagate the z-index to the widget.
638 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
640 // make sure visibility state is accurate
642 if (aResetVisibility) {
643 SetVisibility(GetVisibility());
647 void nsView::SetNeedsWindowPropertiesSync() {
648 mNeedsWindowPropertiesSync = true;
649 if (mViewManager) {
650 mViewManager->PostPendingUpdate();
654 // Attach to a top level widget and start receiving mirrored events.
655 nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget) {
656 MOZ_ASSERT(nullptr != aWidget, "null widget ptr");
658 /// XXXjimm This is a temporary workaround to an issue w/document
659 // viewer (bug 513162).
660 nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener();
661 if (listener) {
662 nsView* oldView = listener->GetView();
663 if (oldView) {
664 oldView->DetachFromTopLevelWidget();
668 // Note, the previous device context will be released. Detaching
669 // will not restore the old one.
670 aWidget->AttachViewToTopLevel(!nsIWidget::UsePuppetWidgets());
672 mWindow = aWidget;
674 mWindow->SetAttachedWidgetListener(this);
675 if (mWindow->WindowType() != eWindowType_invisible) {
676 nsresult rv = mWindow->AsyncEnableDragDrop(true);
677 NS_ENSURE_SUCCESS(rv, rv);
679 mWidgetIsTopLevel = true;
681 // Refresh the view bounds
682 CalcWidgetBounds(mWindow->WindowType());
684 return NS_OK;
687 // Detach this view from an attached widget.
688 nsresult nsView::DetachFromTopLevelWidget() {
689 MOZ_ASSERT(mWidgetIsTopLevel, "Not attached currently!");
690 MOZ_ASSERT(mWindow, "null mWindow for DetachFromTopLevelWidget!");
692 mWindow->SetAttachedWidgetListener(nullptr);
693 nsIWidgetListener* listener = mWindow->GetPreviouslyAttachedWidgetListener();
695 if (listener && listener->GetView()) {
696 // Ensure the listener doesn't think it's being used anymore
697 listener->GetView()->SetPreviousWidget(nullptr);
700 // If the new view's frame is paint suppressed then the window
701 // will want to use us instead until that's done
702 mWindow->SetPreviouslyAttachedWidgetListener(this);
704 mPreviousWindow = mWindow;
705 mWindow = nullptr;
707 mWidgetIsTopLevel = false;
709 return NS_OK;
712 void nsView::SetZIndex(bool aAuto, int32_t aZIndex) {
713 bool oldIsAuto = GetZIndexIsAuto();
714 mVFlags = (mVFlags & ~NS_VIEW_FLAG_AUTO_ZINDEX) |
715 (aAuto ? NS_VIEW_FLAG_AUTO_ZINDEX : 0);
716 mZIndex = aZIndex;
718 if (HasWidget() || !oldIsAuto || !aAuto) {
719 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
723 void nsView::AssertNoWindow() {
724 // XXX: it would be nice to make this a strong assert
725 if (MOZ_UNLIKELY(mWindow)) {
726 NS_ERROR("We already have a window for this view? BAD");
727 mWindow->SetWidgetListener(nullptr);
728 mWindow->Destroy();
729 mWindow = nullptr;
734 // internal window creation functions
736 void nsView::AttachWidgetEventHandler(nsIWidget* aWidget) {
737 #ifdef DEBUG
738 NS_ASSERTION(!aWidget->GetWidgetListener(), "Already have a widget listener");
739 #endif
741 aWidget->SetWidgetListener(this);
744 void nsView::DetachWidgetEventHandler(nsIWidget* aWidget) {
745 NS_ASSERTION(!aWidget->GetWidgetListener() ||
746 aWidget->GetWidgetListener()->GetView() == this,
747 "Wrong view");
748 aWidget->SetWidgetListener(nullptr);
751 #ifdef DEBUG
752 void nsView::List(FILE* out, int32_t aIndent) const {
753 int32_t i;
754 for (i = aIndent; --i >= 0;) fputs(" ", out);
755 fprintf(out, "%p ", (void*)this);
756 if (nullptr != mWindow) {
757 nscoord p2a = mViewManager->AppUnitsPerDevPixel();
758 LayoutDeviceIntRect rect = mWindow->GetClientBounds();
759 nsRect windowBounds = LayoutDeviceIntRect::ToAppUnits(rect, p2a);
760 rect = mWindow->GetBounds();
761 nsRect nonclientBounds = LayoutDeviceIntRect::ToAppUnits(rect, p2a);
762 nsrefcnt widgetRefCnt = mWindow.get()->AddRef() - 1;
763 mWindow.get()->Release();
764 int32_t Z = mWindow->GetZIndex();
765 fprintf(out, "(widget=%p[%" PRIuPTR "] z=%d pos={%d,%d,%d,%d}) ",
766 (void*)mWindow, widgetRefCnt, Z, nonclientBounds.X(),
767 nonclientBounds.Y(), windowBounds.Width(), windowBounds.Height());
769 nsRect brect = GetBounds();
770 fprintf(out, "{%d,%d,%d,%d}", brect.X(), brect.Y(), brect.Width(),
771 brect.Height());
772 fprintf(out, " z=%d vis=%d frame=%p <\n", mZIndex, mVis,
773 static_cast<void*>(mFrame));
774 for (nsView* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
775 NS_ASSERTION(kid->GetParent() == this, "incorrect parent");
776 kid->List(out, aIndent + 1);
778 for (i = aIndent; --i >= 0;) fputs(" ", out);
779 fputs(">\n", out);
781 #endif // DEBUG
783 nsPoint nsView::GetOffsetTo(const nsView* aOther) const {
784 return GetOffsetTo(aOther, GetViewManager()->AppUnitsPerDevPixel());
787 nsPoint nsView::GetOffsetTo(const nsView* aOther, const int32_t aAPD) const {
788 MOZ_ASSERT(GetParent() || !aOther || aOther->GetParent() || this == aOther,
789 "caller of (outer) GetOffsetTo must not pass unrelated views");
790 // We accumulate the final result in offset
791 nsPoint offset(0, 0);
792 // The offset currently accumulated at the current APD
793 nsPoint docOffset(0, 0);
794 const nsView* v = this;
795 nsViewManager* currVM = v->GetViewManager();
796 int32_t currAPD = currVM->AppUnitsPerDevPixel();
797 const nsView* root = nullptr;
798 for (; v != aOther && v; root = v, v = v->GetParent()) {
799 nsViewManager* newVM = v->GetViewManager();
800 if (newVM != currVM) {
801 int32_t newAPD = newVM->AppUnitsPerDevPixel();
802 if (newAPD != currAPD) {
803 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
804 docOffset.x = docOffset.y = 0;
805 currAPD = newAPD;
807 currVM = newVM;
809 docOffset += v->GetPosition();
811 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
813 if (v != aOther) {
814 // Looks like aOther wasn't an ancestor of |this|. So now we have
815 // the root-VM-relative position of |this| in |offset|. Get the
816 // root-VM-relative position of aOther and subtract it.
817 nsPoint negOffset = aOther->GetOffsetTo(root, aAPD);
818 offset -= negOffset;
821 return offset;
824 nsPoint nsView::GetOffsetToWidget(nsIWidget* aWidget) const {
825 nsPoint pt;
826 // Get the view for widget
827 nsView* widgetView = GetViewFor(aWidget);
828 if (!widgetView) {
829 return pt;
832 // Get the offset to the widget view in the widget view's APD
833 // We get the offset in the widget view's APD first and then convert to our
834 // APD afterwards so that we can include the widget view's ViewToWidgetOffset
835 // in the sum in its native APD, and then convert the whole thing to our APD
836 // so that we don't have to convert the APD of the relatively small
837 // ViewToWidgetOffset by itself with a potentially large relative rounding
838 // error.
839 pt = -widgetView->GetOffsetTo(this);
840 // Add in the offset to the widget.
841 pt += widgetView->ViewToWidgetOffset();
843 // Convert to our appunits.
844 int32_t widgetAPD = widgetView->GetViewManager()->AppUnitsPerDevPixel();
845 int32_t ourAPD = GetViewManager()->AppUnitsPerDevPixel();
846 pt = pt.ScaleToOtherAppUnits(widgetAPD, ourAPD);
847 return pt;
850 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset) const {
851 return GetNearestWidget(aOffset, GetViewManager()->AppUnitsPerDevPixel());
854 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset,
855 const int32_t aAPD) const {
856 // aOffset is based on the view's position, which ignores any chrome on
857 // attached parent widgets.
859 // We accumulate the final result in pt
860 nsPoint pt(0, 0);
861 // The offset currently accumulated at the current APD
862 nsPoint docPt(0, 0);
863 const nsView* v = this;
864 nsViewManager* currVM = v->GetViewManager();
865 int32_t currAPD = currVM->AppUnitsPerDevPixel();
866 for (; v && !v->HasWidget(); v = v->GetParent()) {
867 nsViewManager* newVM = v->GetViewManager();
868 if (newVM != currVM) {
869 int32_t newAPD = newVM->AppUnitsPerDevPixel();
870 if (newAPD != currAPD) {
871 pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
872 docPt.x = docPt.y = 0;
873 currAPD = newAPD;
875 currVM = newVM;
877 docPt += v->GetPosition();
879 if (!v) {
880 if (aOffset) {
881 pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
882 *aOffset = pt;
884 return nullptr;
887 // pt is now the offset from v's origin to this view's origin.
888 // We add the ViewToWidgetOffset to get the offset to the widget.
889 if (aOffset) {
890 docPt += v->ViewToWidgetOffset();
891 pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
892 *aOffset = pt;
894 return v->GetWidget();
897 bool nsView::IsRoot() const {
898 NS_ASSERTION(mViewManager != nullptr,
899 " View manager is null in nsView::IsRoot()");
900 return mViewManager->GetRootView() == this;
903 nsRect nsView::GetBoundsInParentUnits() const {
904 nsView* parent = GetParent();
905 nsViewManager* VM = GetViewManager();
906 if (this != VM->GetRootView() || !parent) {
907 return mDimBounds;
909 int32_t ourAPD = VM->AppUnitsPerDevPixel();
910 int32_t parentAPD = parent->GetViewManager()->AppUnitsPerDevPixel();
911 return mDimBounds.ScaleToOtherAppUnitsRoundOut(ourAPD, parentAPD);
914 nsPoint nsView::ConvertFromParentCoords(nsPoint aPt) const {
915 const nsView* parent = GetParent();
916 if (parent) {
917 aPt = aPt.ScaleToOtherAppUnits(
918 parent->GetViewManager()->AppUnitsPerDevPixel(),
919 GetViewManager()->AppUnitsPerDevPixel());
921 aPt -= GetPosition();
922 return aPt;
925 static bool IsPopupWidget(nsIWidget* aWidget) {
926 return (aWidget->WindowType() == eWindowType_popup);
929 PresShell* nsView::GetPresShell() { return GetViewManager()->GetPresShell(); }
931 bool nsView::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) {
932 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
933 if (pm && IsPopupWidget(aWidget)) {
934 pm->PopupMoved(mFrame, nsIntPoint(x, y));
935 return true;
938 return false;
941 bool nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth,
942 int32_t aHeight) {
943 // The root view may not be set if this is the resize associated with
944 // window creation
945 SetForcedRepaint(true);
946 if (this == mViewManager->GetRootView()) {
947 RefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext();
948 // ensure DPI is up-to-date, in case of window being opened and sized
949 // on a non-default-dpi display (bug 829963)
950 devContext->CheckDPIChange();
951 int32_t p2a = devContext->AppUnitsPerDevPixel();
952 mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(aWidth, p2a),
953 NSIntPixelsToAppUnits(aHeight, p2a));
955 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
956 if (pm) {
957 PresShell* presShell = mViewManager->GetPresShell();
958 if (presShell && presShell->GetDocument()) {
959 pm->AdjustPopupsOnWindowChange(presShell);
963 return true;
965 if (IsPopupWidget(aWidget)) {
966 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
967 if (pm) {
968 pm->PopupResized(mFrame, LayoutDeviceIntSize(aWidth, aHeight));
969 return true;
973 return false;
976 bool nsView::RequestWindowClose(nsIWidget* aWidget) {
977 if (mFrame && IsPopupWidget(aWidget) && mFrame->IsMenuPopupFrame()) {
978 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
979 if (pm) {
980 pm->HidePopup(mFrame->GetContent(), false, true, false, false);
981 return true;
985 return false;
988 void nsView::WillPaintWindow(nsIWidget* aWidget) {
989 RefPtr<nsViewManager> vm = mViewManager;
990 vm->WillPaintWindow(aWidget);
993 bool nsView::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion) {
994 NS_ASSERTION(this == nsView::GetViewFor(aWidget), "wrong view for widget?");
996 RefPtr<nsViewManager> vm = mViewManager;
997 bool result = vm->PaintWindow(aWidget, aRegion);
998 return result;
1001 void nsView::DidPaintWindow() {
1002 RefPtr<nsViewManager> vm = mViewManager;
1003 vm->DidPaintWindow();
1006 void nsView::DidCompositeWindow(mozilla::layers::TransactionId aTransactionId,
1007 const TimeStamp& aCompositeStart,
1008 const TimeStamp& aCompositeEnd) {
1009 PresShell* presShell = mViewManager->GetPresShell();
1010 if (!presShell) {
1011 return;
1014 nsAutoScriptBlocker scriptBlocker;
1016 nsPresContext* context = presShell->GetPresContext();
1017 nsRootPresContext* rootContext = context->GetRootPresContext();
1018 if (rootContext) {
1019 rootContext->NotifyDidPaintForSubtree(aTransactionId, aCompositeEnd);
1022 // If the two timestamps are identical, this was likely a fake composite
1023 // event which wouldn't be terribly useful to display.
1024 if (aCompositeStart == aCompositeEnd) {
1025 return;
1028 nsIDocShell* docShell = context->GetDocShell();
1029 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1031 if (timelines && timelines->HasConsumer(docShell)) {
1032 timelines->AddMarkerForDocShell(
1033 docShell, MakeUnique<CompositeTimelineMarker>(
1034 aCompositeStart, MarkerTracingType::START));
1035 timelines->AddMarkerForDocShell(
1036 docShell, MakeUnique<CompositeTimelineMarker>(aCompositeEnd,
1037 MarkerTracingType::END));
1041 void nsView::RequestRepaint() {
1042 PresShell* presShell = mViewManager->GetPresShell();
1043 if (presShell) {
1044 presShell->ScheduleViewManagerFlush();
1048 bool nsView::ShouldNotBeVisible() {
1049 if (mFrame && mFrame->IsMenuPopupFrame()) {
1050 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1051 return !pm || !pm->IsPopupOpen(mFrame->GetContent());
1054 return false;
1057 nsEventStatus nsView::HandleEvent(WidgetGUIEvent* aEvent,
1058 bool aUseAttachedEvents) {
1059 MOZ_ASSERT(nullptr != aEvent->mWidget, "null widget ptr");
1061 nsEventStatus result = nsEventStatus_eIgnore;
1062 nsView* view;
1063 if (aUseAttachedEvents) {
1064 nsIWidgetListener* listener = aEvent->mWidget->GetAttachedWidgetListener();
1065 view = listener ? listener->GetView() : nullptr;
1066 } else {
1067 view = GetViewFor(aEvent->mWidget);
1070 if (view) {
1071 RefPtr<nsViewManager> vm = view->GetViewManager();
1072 vm->DispatchEvent(aEvent, view, &result);
1075 return result;
1078 bool nsView::IsPrimaryFramePaintSuppressed() {
1079 return sShowPreviousPage && mFrame &&
1080 mFrame->PresShell()->IsPaintingSuppressed();