/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Views should be transparent by default. Not being transparent is // a promise that the view will paint all its pixels opaquely. Views // should make this promise explicitly by calling // SetViewContentTransparency.
}
void nsView::DropMouseGrabbing() { if (mViewManager->GetPresShell()) {
PresShell::ClearMouseCaptureOnView(this);
}
}
nsView::~nsView() {
MOZ_COUNT_DTOR(nsView);
while (GetFirstChild()) {
nsView* child = GetFirstChild(); if (child->GetViewManager() == mViewManager) {
child->Destroy();
} else { // just unhook it. Someone else will want to destroy this.
RemoveChild(child);
}
}
if (mViewManager) {
DropMouseGrabbing();
nsView* rootView = mViewManager->GetRootView();
if (rootView) { // Root views can have parents! if (mParent) {
mViewManager->RemoveChild(this);
}
if (rootView == this) { // Inform the view manager that the root view has gone away...
mViewManager->SetRootView(nullptr);
}
} elseif (mParent) {
mParent->RemoveChild(this);
}
void nsView::DestroyWidget() { if (mWindow) { // If we are not attached to a base window, we're going to tear down our // widget here. However, if we're attached to somebody elses widget, we // want to leave the widget alone: don't reset the client data or call // Destroy. Just clear our event view ptr and free our reference to it. if (mWidgetIsTopLevel) {
mWindow->SetAttachedWidgetListener(nullptr);
} else {
mWindow->SetWidgetListener(nullptr);
nsCOMPtr<nsIRunnable> widgetDestroyer = new DestroyWidgetRunnable(mWindow);
// Don't leak if we happen to arrive here after the main thread // has disappeared.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); if (mainThread) {
mainThread->Dispatch(widgetDestroyer.forget(), NS_DISPATCH_NORMAL);
}
}
NS_ASSERTION(GetParent() || (aX == 0 && aY == 0), "Don't try to move the root widget to something non-zero");
ResetWidgetBounds(true, false);
}
void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync) { if (mWindow) { if (!aForceSync) { // Don't change widget geometry synchronously, since that can // cause synchronous painting.
mViewManager->PostPendingUpdate();
} else {
DoResetWidgetBounds(false, true);
} return;
}
if (aRecurse) { // reposition any widgets under this view for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) {
v->ResetWidgetBounds(true, aForceSync);
}
}
}
bool nsView::IsEffectivelyVisible() { for (nsView* v = this; v; v = v->mParent) { if (v->GetVisibility() == ViewVisibility::Hide) returnfalse;
} returntrue;
}
// Cocoa and GTK round widget coordinates to the nearest global "display pixel" // integer value. So we avoid fractional display pixel values by rounding to // the nearest value that won't yield a fractional display pixel. static LayoutDeviceIntRect MaybeRoundToDisplayPixels( const LayoutDeviceIntRect& aRect, TransparencyMode aTransparency,
int32_t aRound) { if (aRound == 1) { return aRect;
}
// If the widget doesn't support transparency, we prefer truncating to // ceiling, so that we don't have extra pixels not painted by our frame. auto size = aTransparency == TransparencyMode::Opaque
? aRect.Size().TruncatedToMultiple(aRound)
: aRect.Size().CeiledToMultiple(aRound);
Unused << NS_WARN_IF(aTransparency == TransparencyMode::Opaque &&
size != aRect.Size()); return {aRect.TopLeft().RoundedToMultiple(aRound), size};
}
nsView* parent = GetParent();
nsIWidget* parentWidget = nullptr; if (parent) {
nsPoint offset;
parentWidget = parent->GetNearestWidget(&offset, p2a); // make viewBounds be relative to the parent widget, in appunits
viewBounds += offset;
if (parentWidget && aType == WindowType::Popup && IsEffectivelyVisible()) { // put offset into screen coordinates. (based on client area origin)
LayoutDeviceIntPoint screenPoint = parentWidget->WidgetToScreenOffset();
viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a),
NSIntPixelsToAppUnits(screenPoint.y, p2a));
}
}
// Compute widget bounds in device pixels const LayoutDeviceIntRect newBounds = [&] { // TODO(emilio): We should probably use outside pixels for transparent // windows (not just popups) as well. if (aType != WindowType::Popup) { return LayoutDeviceIntRect::FromUnknownRect(
viewBounds.ToNearestPixels(p2a));
} // We use outside pixels for transparent windows if possible, so that we // don't truncate the contents. For opaque popups, we use nearest pixels // which prevents having pixels not drawn by the frame. constbool opaque = aTransparency == TransparencyMode::Opaque; constauto idealBounds = LayoutDeviceIntRect::FromUnknownRect(
opaque ? viewBounds.ToNearestPixels(p2a)
: viewBounds.ToOutsidePixels(p2a));
// Compute where the top-left of our widget ended up relative to the parent // widget, in appunits.
nsPoint roundedOffset(NSIntPixelsToAppUnits(newBounds.X(), p2a),
NSIntPixelsToAppUnits(newBounds.Y(), p2a));
// mViewToWidgetOffset is added to coordinates relative to the view origin // to get coordinates relative to the widget. // The view origin, relative to the parent widget, is at // (mPosX,mPosY) - mDimBounds.TopLeft() + viewBounds.TopLeft(). // Our widget, relative to the parent widget, is roundedOffset.
mViewToWidgetOffset = nsPoint(mPosX, mPosY) - mDimBounds.TopLeft() +
viewBounds.TopLeft() - roundedOffset;
void nsView::DoResetWidgetBounds(bool aMoveOnly, bool aInvalidateChangedSize) { // The geometry of a root view's widget is controlled externally, // NOT by sizing or positioning the view if (mViewManager->GetRootView() == this) { return;
}
MOZ_ASSERT(mWindow, "Why was this called??");
// Hold this ref to make sure it stays alive.
nsCOMPtr<nsIWidget> widget = mWindow;
// Stash a copy of these and use them so we can handle this being deleted (say // from sync painting/flushing from Show/Move/Resize on the widget).
LayoutDeviceIntRect newBounds;
if (invisiblePopup) { // Don't manipulate empty or hidden popup widgets. For example there's no // point moving hidden comboboxes around, or doing X server roundtrips // to compute their true screen position. This could mean that // WidgetToScreen operations on these widgets don't return up-to-date // values, but popup positions aren't reliable anyway because of correction // to be on or off-screen. return;
}
// Apply the widget size constraints to newBounds.
widget->ConstrainSize(&newBounds.width, &newBounds.height);
// Child views are never attached to top level widgets, this is safe.
// Coordinates are converted to desktop pixels for window Move/Resize APIs, // because of the potential for device-pixel coordinate spaces for mixed // hidpi/lodpi screens to overlap each other and result in bad placement // (bug 814434).
// Don't use nsRect's operator== here, since it returns true when // both rects are empty even if they have different widths and we // have cases where that sort of thing matters to us. if (mDimBounds.TopLeft() == dims.TopLeft() &&
mDimBounds.Size() == dims.Size()) { return;
}
mDimBounds = dims;
if (aResizeWidget) {
ResetWidgetBounds(false, false);
}
}
void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible) { if (!aEffectivelyVisible) {
DropMouseGrabbing();
}
SetForcedRepaint(true);
if (mWindow) {
ResetWidgetBounds(false, false);
}
for (nsView* child = mFirstChild; child; child = child->mNextSibling) { if (child->mVis == ViewVisibility::Hide) { // It was effectively hidden and still is continue;
} // Our child is visible if we are
child->NotifyEffectiveVisibilityChanged(aEffectivelyVisible);
}
}
nsresult nsView::CreateWidgetForPopup(widget::InitData* aWidgetInitData,
nsIWidget* aParent) {
AssertNoWindow();
MOZ_ASSERT(aWidgetInitData, "Widget init data required");
MOZ_ASSERT(aWidgetInitData->mWindowType == WindowType::Popup, "Use one of the other CreateWidget methods");
MOZ_ASSERT(aParent);
// Attach to a top level widget and start receiving mirrored events.
nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget) {
MOZ_ASSERT(aWidget, "null widget ptr");
/// XXXjimm This is a temporary workaround to an issue w/document // viewer (bug 513162).
nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener(); if (listener) {
nsView* oldView = listener->GetView(); if (oldView) {
oldView->DetachFromTopLevelWidget();
}
}
// Note, the previous device context will be released. Detaching // will not restore the old one.
aWidget->AttachViewToTopLevel(!nsIWidget::UsePuppetWidgets());
// Refresh the view bounds
RecalcWidgetBounds(); return NS_OK;
}
// Detach this view from an attached widget.
nsresult nsView::DetachFromTopLevelWidget() {
MOZ_ASSERT(mWidgetIsTopLevel, "Not attached currently!");
MOZ_ASSERT(mWindow, "null mWindow for DetachFromTopLevelWidget!");
if (listener && listener->GetView()) { // Ensure the listener doesn't think it's being used anymore
listener->GetView()->SetPreviousWidget(nullptr);
}
// If the new view's frame is paint suppressed then the window // will want to use us instead until that's done
mWindow->SetPreviouslyAttachedWidgetListener(this);
mPreviousWindow = mWindow;
mWindow = nullptr;
mWidgetIsTopLevel = false;
return NS_OK;
}
void nsView::AssertNoWindow() { // XXX: it would be nice to make this a strong assert if (MOZ_UNLIKELY(mWindow)) {
NS_ERROR("We already have a window for this view? BAD");
mWindow->SetWidgetListener(nullptr);
mWindow->Destroy();
mWindow = nullptr;
}
}
// // internal window creation functions // void nsView::AttachWidgetEventHandler(nsIWidget* aWidget) { #ifdef DEBUG
NS_ASSERTION(!aWidget->GetWidgetListener(), "Already have a widget listener"); #endif
nsPoint nsView::GetOffsetTo(const nsView* aOther, const int32_t aAPD) const {
MOZ_ASSERT(GetParent() || !aOther || aOther->GetParent() || this == aOther, "caller of (outer) GetOffsetTo must not pass unrelated views"); // We accumulate the final result in offset
nsPoint offset(0, 0); // The offset currently accumulated at the current APD
nsPoint docOffset(0, 0); const nsView* v = this;
nsViewManager* currVM = v->GetViewManager();
int32_t currAPD = currVM->AppUnitsPerDevPixel(); const nsView* root = nullptr; for (; v != aOther && v; root = v, v = v->GetParent()) {
nsViewManager* newVM = v->GetViewManager(); if (newVM != currVM) {
int32_t newAPD = newVM->AppUnitsPerDevPixel(); if (newAPD != currAPD) {
offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
docOffset.x = docOffset.y = 0;
currAPD = newAPD;
}
currVM = newVM;
}
docOffset += v->GetPosition();
}
offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
if (v != aOther) { // Looks like aOther wasn't an ancestor of |this|. So now we have // the root-VM-relative position of |this| in |offset|. Get the // root-VM-relative position of aOther and subtract it.
nsPoint negOffset = aOther->GetOffsetTo(root, aAPD);
offset -= negOffset;
}
return offset;
}
nsPoint nsView::GetOffsetToWidget(nsIWidget* aWidget) const {
nsPoint pt; // Get the view for widget
nsView* widgetView = GetViewFor(aWidget); if (!widgetView) { return pt;
}
// Get the offset to the widget view in the widget view's APD // We get the offset in the widget view's APD first and then convert to our // APD afterwards so that we can include the widget view's ViewToWidgetOffset // in the sum in its native APD, and then convert the whole thing to our APD // so that we don't have to convert the APD of the relatively small // ViewToWidgetOffset by itself with a potentially large relative rounding // error.
pt = -widgetView->GetOffsetTo(this); // Add in the offset to the widget.
pt += widgetView->ViewToWidgetOffset();
nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset, const int32_t aAPD) const { // aOffset is based on the view's position, which ignores any chrome on // attached parent widgets.
// We accumulate the final result in pt
nsPoint pt(0, 0); // The offset currently accumulated at the current APD
nsPoint docPt(0, 0); const nsView* v = this;
nsViewManager* currVM = v->GetViewManager();
int32_t currAPD = currVM->AppUnitsPerDevPixel(); for (; v && !v->HasWidget(); v = v->GetParent()) {
nsViewManager* newVM = v->GetViewManager(); if (newVM != currVM) {
int32_t newAPD = newVM->AppUnitsPerDevPixel(); if (newAPD != currAPD) {
pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
docPt.x = docPt.y = 0;
currAPD = newAPD;
}
currVM = newVM;
}
docPt += v->GetPosition();
} if (!v) { if (aOffset) {
pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
*aOffset = pt;
} return nullptr;
}
// pt is now the offset from v's origin to this view's origin. // We add the ViewToWidgetOffset to get the offset to the widget. if (aOffset) {
docPt += v->ViewToWidgetOffset();
pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
*aOffset = pt;
} return v->GetWidget();
}
bool nsView::IsRoot() const {
NS_ASSERTION(mViewManager != nullptr, " View manager is null in nsView::IsRoot()"); return mViewManager->GetRootView() == this;
}
bool nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth,
int32_t aHeight) { // The root view may not be set if this is the resize associated with // window creation
SetForcedRepaint(true); if (this == mViewManager->GetRootView()) {
RefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext(); // ensure DPI is up-to-date, in case of window being opened and sized // on a non-default-dpi display (bug 829963)
devContext->CheckDPIChange();
int32_t p2a = devContext->AppUnitsPerDevPixel(); if (auto* frame = GetFrame()) { // Usually the resize would deal with this, but there are some cases (like // web-extension popups) where frames might already be correctly sized etc // due to a call to e.g. nsDocumentViewer::GetContentSize or so.
frame->InvalidateFrame();
}
if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
PresShell* presShell = mViewManager->GetPresShell(); if (presShell && presShell->GetDocument()) {
pm->AdjustPopupsOnWindowChange(presShell);
}
}
returntrue;
} if (IsPopupWidget(aWidget)) {
nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm) {
pm->PopupResized(mFrame, LayoutDeviceIntSize(aWidth, aHeight)); returntrue;
}
}
returnfalse;
}
#ifdefined(MOZ_WIDGET_ANDROID) void nsView::DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight) {
MOZ_ASSERT(XRE_IsParentProcess(), "Should be only called for the browser parent process");
MOZ_ASSERT(this == mViewManager->GetRootView(), "Should be called for the root view");
void nsView::DynamicToolbarOffsetChanged(ScreenIntCoord aOffset) {
MOZ_ASSERT(XRE_IsParentProcess(), "Should be only called for the browser parent process");
MOZ_ASSERT(this == mViewManager->GetRootView(), "Should be called for the root view");
CallOnAllRemoteChildren(
[aOffset](dom::BrowserParent* aBrowserParent) -> CallState { // Skip background tabs. if (!aBrowserParent->GetDocShellIsActive()) { return CallState::Continue;
}
void nsView::KeyboardHeightChanged(ScreenIntCoord aHeight) {
MOZ_ASSERT(XRE_IsParentProcess(), "Should be only called for the browser parent process");
MOZ_ASSERT(this == mViewManager->GetRootView(), "Should be called for the root view");
CallOnAllRemoteChildren(
[aHeight](dom::BrowserParent* aBrowserParent) -> CallState { // Skip background tabs. if (!aBrowserParent->GetDocShellIsActive()) { return CallState::Continue;
}
// If the two timestamps are identical, this was likely a fake composite // event which wouldn't be terribly useful to display. if (aCompositeStart == aCompositeEnd) { return;
}
}
// https://github.com/w3c/csswg-drafts/issues/4670 // Actually we don't set this value on sub document. This behaviour is // same as Blink.
CallOnAllRemoteChildren([windowSafeAreaInsets](
dom::BrowserParent* aBrowserParent) -> CallState {
Unused << aBrowserParent->SendSafeAreaInsetsChanged(windowSafeAreaInsets); return CallState::Continue;
});
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.