bool ContentCacheInParent::GetTextRect(uint32_t aOffset, LayoutDeviceIntRect& aTextRect) const { MOZ_LOG(sContentCacheLog, LogLevel::Info, ("ContentCacheInParent: 0x%p GetTextRect(aOffset=%u), " "mTextRectArray={ mStart=%u, mRects.Length()=%u }, " "mSelection={ mAnchor=%u, mFocus=%u }", this, aOffset, mTextRectArray.mStart, mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus)); if (!aOffset) { NS_WARN_IF(mFirstCharRect.IsEmpty()); aTextRect = mFirstCharRect; return !aTextRect.IsEmpty(); } if (aOffset == mSelection.mAnchor) { NS_WARN_IF(mSelection.mAnchorCharRect.IsEmpty()); aTextRect = mSelection.mAnchorCharRect; return !aTextRect.IsEmpty(); } if (aOffset == mSelection.mFocus) { NS_WARN_IF(mSelection.mFocusCharRect.IsEmpty()); aTextRect = mSelection.mFocusCharRect; return !aTextRect.IsEmpty(); } if (!mTextRectArray.InRange(aOffset)) { aTextRect.SetEmpty(); return false; } aTextRect = mTextRectArray.GetRect(aOffset); return true; }
already_AddRefed<gfx::DrawTarget> CompositorWidgetProxy::CreateBackBufferDrawTarget(gfx::DrawTarget* aScreenTarget, const LayoutDeviceIntRect& aRect, const LayoutDeviceIntRect& aClearRect) { MOZ_ASSERT(aScreenTarget); gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8; gfx::IntSize size = aRect.ToUnknownRect().Size(); gfx::IntSize clientSize(GetClientSize().ToUnknownSize()); RefPtr<gfx::DrawTarget> target; // Re-use back buffer if possible if (mLastBackBuffer && mLastBackBuffer->GetBackendType() == aScreenTarget->GetBackendType() && mLastBackBuffer->GetFormat() == format && size <= mLastBackBuffer->GetSize() && mLastBackBuffer->GetSize() <= clientSize) { target = mLastBackBuffer; target->SetTransform(gfx::Matrix()); if (!aClearRect.IsEmpty()) { gfx::IntRect clearRect = aClearRect.ToUnknownRect() - aRect.ToUnknownRect().TopLeft(); target->ClearRect(gfx::Rect(clearRect.x, clearRect.y, clearRect.width, clearRect.height)); } } else { target = aScreenTarget->CreateSimilarDrawTarget(size, format); mLastBackBuffer = target; } return target.forget(); }
void PuppetWidget::Resize(double aWidth, double aHeight, bool aRepaint) { LayoutDeviceIntRect oldBounds = mBounds; mBounds.SizeTo(LayoutDeviceIntSize(NSToIntRound(aWidth), NSToIntRound(aHeight))); if (mChild) { mChild->Resize(aWidth, aHeight, aRepaint); return; } // XXX: roc says that |aRepaint| dictates whether or not to // invalidate the expanded area if (oldBounds.Size() < mBounds.Size() && aRepaint) { LayoutDeviceIntRegion dirty(mBounds); dirty.Sub(dirty, oldBounds); InvalidateRegion(this, dirty); } // call WindowResized() on both the current listener, and possibly // also the previous one if we're in a state where we're drawing that one // because the current one is paint suppressed if (!oldBounds.IsEqualEdges(mBounds) && mAttachedWidgetListener) { if (GetCurrentWidgetListener() && GetCurrentWidgetListener() != mAttachedWidgetListener) { GetCurrentWidgetListener()->WindowResized(this, mBounds.width, mBounds.height); } mAttachedWidgetListener->WindowResized(this, mBounds.width, mBounds.height); } }
static void CalculatePluginClip(const LayoutDeviceIntRect& aBounds, const nsTArray<LayoutDeviceIntRect>& aPluginClipRects, const LayoutDeviceIntPoint& aContentOffset, const LayoutDeviceIntRegion& aParentLayerVisibleRegion, nsTArray<LayoutDeviceIntRect>& aResult, LayoutDeviceIntRect& aVisibleBounds, bool& aPluginIsVisible) { aPluginIsVisible = true; LayoutDeviceIntRegion contentVisibleRegion; // aPluginClipRects (plugin widget origin) - contains *visible* rects for (uint32_t idx = 0; idx < aPluginClipRects.Length(); idx++) { LayoutDeviceIntRect rect = aPluginClipRects[idx]; // shift to content origin rect.MoveBy(aBounds.x, aBounds.y); // accumulate visible rects contentVisibleRegion.OrWith(rect); } // apply layers clip (window origin) LayoutDeviceIntRegion region = aParentLayerVisibleRegion; region.MoveBy(-aContentOffset.x, -aContentOffset.y); contentVisibleRegion.AndWith(region); if (contentVisibleRegion.IsEmpty()) { aPluginIsVisible = false; return; } // shift to plugin widget origin contentVisibleRegion.MoveBy(-aBounds.x, -aBounds.y); LayoutDeviceIntRegion::RectIterator iter(contentVisibleRegion); for (const LayoutDeviceIntRect* rgnRect = iter.Next(); rgnRect; rgnRect = iter.Next()) { aResult.AppendElement(*rgnRect); aVisibleBounds.UnionRect(aVisibleBounds, *rgnRect); } }
void CompositorD3D9::EnsureSize() { LayoutDeviceIntRect rect; mWidget->GetClientBounds(rect); mSize = rect.Size(); }
void ClientLayerManager::MakeSnapshotIfRequired() { if (!mShadowTarget) { return; } if (mWidget) { if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { // The compositor doesn't draw to a different sized surface // when there's a rotation. Instead we rotate the result // when drawing into dt LayoutDeviceIntRect outerBounds; mWidget->GetBounds(outerBounds); IntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents()); if (mTargetRotation) { bounds = RotateRect(bounds, outerBounds.ToUnknownRect(), mTargetRotation); } SurfaceDescriptor inSnapshot; if (!bounds.IsEmpty() && mForwarder->AllocSurfaceDescriptor(bounds.Size(), gfxContentType::COLOR_ALPHA, &inSnapshot)) { // Make a copy of |inSnapshot| because the call to send it over IPC // will call forget() on the Shmem inside, and zero it out. SurfaceDescriptor outSnapshot = inSnapshot; if (remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) { RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(outSnapshot); DrawTarget* dt = mShadowTarget->GetDrawTarget(); Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height); Rect srcRect(0, 0, bounds.width, bounds.height); gfx::Matrix rotate = ComputeTransformForUnRotation(outerBounds.ToUnknownRect(), mTargetRotation); gfx::Matrix oldMatrix = dt->GetTransform(); dt->SetTransform(rotate * oldMatrix); dt->DrawSurface(surf, dstRect, srcRect, DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_OVER)); dt->SetTransform(oldMatrix); } mForwarder->DestroySurfaceDescriptor(&outSnapshot); } } } mShadowTarget = nullptr; }
LayoutDeviceIntRect ContentCache::TextRectArray::GetUnionRect(uint32_t aOffset, uint32_t aLength) const { LayoutDeviceIntRect rect; if (!InRange(aOffset, aLength)) { return rect; } for (uint32_t i = 0; i < aLength; i++) { rect = rect.Union(mRects[aOffset - mStart + i]); } return rect; }
bool ContentCacheInParent::GetCaretRect(uint32_t aOffset, LayoutDeviceIntRect& aCaretRect) const { MOZ_LOG(sContentCacheLog, LogLevel::Info, ("ContentCacheInParent: 0x%p GetCaretRect(aOffset=%u), " "mCaret={ mOffset=%u, mRect=%s, IsValid()=%s }, mTextRectArray={ " "mStart=%u, mRects.Length()=%u }, mSelection={ mAnchor=%u, mFocus=%u, " "mWritingMode=%s, mAnchorCharRect=%s, mFocusCharRect=%s }, " "mFirstCharRect=%s", this, aOffset, mCaret.mOffset, GetRectText(mCaret.mRect).get(), GetBoolName(mCaret.IsValid()), mTextRectArray.mStart, mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus, GetWritingModeName(mSelection.mWritingMode).get(), GetRectText(mSelection.mAnchorCharRect).get(), GetRectText(mSelection.mFocusCharRect).get(), GetRectText(mFirstCharRect).get())); if (mCaret.IsValid() && mCaret.mOffset == aOffset) { aCaretRect = mCaret.mRect; return true; } // Guess caret rect from the text rect if it's stored. if (!GetTextRect(aOffset, aCaretRect)) { // There might be previous character rect in the cache. If so, we can // guess the caret rect with it. if (!aOffset || !GetTextRect(aOffset - 1, aCaretRect)) { aCaretRect.SetEmpty(); return false; } if (mSelection.mWritingMode.IsVertical()) { aCaretRect.y = aCaretRect.YMost(); } else { // XXX bidi-unaware. aCaretRect.x = aCaretRect.XMost(); } } // XXX This is not bidi aware because we don't cache each character's // direction. However, this is usually used by IME, so, assuming the // character is in LRT context must not cause any problem. if (mSelection.mWritingMode.IsVertical()) { aCaretRect.height = mCaret.IsValid() ? mCaret.mRect.height : 1; } else { aCaretRect.width = mCaret.IsValid() ? mCaret.mRect.width : 1; } return true; }
bool ContentCacheInChild::QueryCharRect(nsIWidget* aWidget, uint32_t aOffset, LayoutDeviceIntRect& aCharRect) const { aCharRect.SetEmpty(); nsEventStatus status = nsEventStatus_eIgnore; WidgetQueryContentEvent textRect(true, NS_QUERY_TEXT_RECT, aWidget); textRect.InitForQueryTextRect(aOffset, 1); aWidget->DispatchEvent(&textRect, status); if (NS_WARN_IF(!textRect.mSucceeded)) { return false; } aCharRect = textRect.mReply.mRect; // Guarantee the rect is not empty. if (NS_WARN_IF(!aCharRect.height)) { aCharRect.height = 1; } if (NS_WARN_IF(!aCharRect.width)) { aCharRect.width = 1; } return true; }
LayoutDeviceIntRect ContentCache::TextRectArray::GetUnionRectAsFarAsPossible( uint32_t aOffset, uint32_t aLength) const { LayoutDeviceIntRect rect; if (!IsOverlappingWith(aOffset, aLength)) { return rect; } uint32_t startOffset = std::max(aOffset, mStart); uint32_t endOffset = std::min(aOffset + aLength, EndOffset()); for (uint32_t i = 0; i < endOffset - startOffset; i++) { rect = rect.Union(mRects[startOffset - mStart + i]); } return rect; }
nsresult PuppetWidget::ConfigureChildren(const nsTArray<Configuration>& aConfigurations) { for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { const Configuration& configuration = aConfigurations[i]; PuppetWidget* w = static_cast<PuppetWidget*>(configuration.mChild.get()); NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); w->SetWindowClipRegion(configuration.mClipRegion, true); LayoutDeviceIntRect bounds = w->GetBounds(); if (bounds.Size() != configuration.mBounds.Size()) { w->Resize(configuration.mBounds.x, configuration.mBounds.y, configuration.mBounds.width, configuration.mBounds.height, true); } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) { w->Move(configuration.mBounds.x, configuration.mBounds.y); } w->SetWindowClipRegion(configuration.mClipRegion, false); } return NS_OK; }
bool ContentCacheInParent::GetUnionTextRects( uint32_t aOffset, uint32_t aLength, LayoutDeviceIntRect& aUnionTextRect) const { MOZ_LOG(sContentCacheLog, LogLevel::Info, ("ContentCacheInParent: 0x%p GetUnionTextRects(aOffset=%u, " "aLength=%u), mTextRectArray={ mStart=%u, mRects.Length()=%u }, " "mSelection={ mAnchor=%u, mFocus=%u }", this, aOffset, aLength, mTextRectArray.mStart, mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus)); CheckedInt<uint32_t> endOffset = CheckedInt<uint32_t>(aOffset) + aLength; if (!endOffset.isValid()) { return false; } if (!mSelection.Collapsed() && aOffset == mSelection.StartOffset() && aLength == mSelection.Length()) { NS_WARN_IF(mSelection.mRect.IsEmpty()); aUnionTextRect = mSelection.mRect; return !aUnionTextRect.IsEmpty(); } if (aLength == 1) { if (!aOffset) { NS_WARN_IF(mFirstCharRect.IsEmpty()); aUnionTextRect = mFirstCharRect; return !aUnionTextRect.IsEmpty(); } if (aOffset == mSelection.mAnchor) { NS_WARN_IF(mSelection.mAnchorCharRect.IsEmpty()); aUnionTextRect = mSelection.mAnchorCharRect; return !aUnionTextRect.IsEmpty(); } if (aOffset == mSelection.mFocus) { NS_WARN_IF(mSelection.mFocusCharRect.IsEmpty()); aUnionTextRect = mSelection.mFocusCharRect; return !aUnionTextRect.IsEmpty(); } } // Even if some text rects are not cached of the queried range, // we should return union rect when the first character's rect is cached // since the first character rect is important and the others are not so // in most cases. if (!aOffset && aOffset != mSelection.mAnchor && aOffset != mSelection.mFocus && !mTextRectArray.InRange(aOffset)) { // The first character rect isn't cached. return false; } if (mTextRectArray.IsOverlappingWith(aOffset, aLength)) { aUnionTextRect = mTextRectArray.GetUnionRectAsFarAsPossible(aOffset, aLength); } else { aUnionTextRect.SetEmpty(); } if (!aOffset) { aUnionTextRect = aUnionTextRect.Union(mFirstCharRect); } if (aOffset <= mSelection.mAnchor && mSelection.mAnchor < endOffset.value()) { aUnionTextRect = aUnionTextRect.Union(mSelection.mAnchorCharRect); } if (aOffset <= mSelection.mFocus && mSelection.mFocus < endOffset.value()) { aUnionTextRect = aUnionTextRect.Union(mSelection.mFocusCharRect); } return !aUnionTextRect.IsEmpty(); }
/** * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in * every widget child of aWidgetView, plus aWidgetView's own widget */ void nsViewManager::InvalidateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion) { NS_ASSERTION(aWidgetView->GetViewManager() == this, "InvalidateWidgetArea called on view we don't own"); nsIWidget* widget = aWidgetView->GetWidget(); #if 0 nsRect dbgBounds = aDamagedRegion.GetBounds(); printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n", aWidgetView, aWidgetView->IsAttachedToTopLevel(), widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height); #endif // If the widget is hidden, it don't cover nothing if (widget && !widget->IsVisible()) { return; } if (!widget) { // The root view or a scrolling view might not have a widget // (for example, during printing). We get here when we scroll // during printing to show selected options in a listbox, for example. return; } // Update all child widgets with the damage. In the process, // accumulate the union of all the child widget areas, or at least // some subset of that. nsRegion children; if (widget->GetTransparencyMode() != eTransparencyTransparent) { for (nsIWidget* childWidget = widget->GetFirstChild(); childWidget; childWidget = childWidget->GetNextSibling()) { nsView* view = nsView::GetViewFor(childWidget); NS_ASSERTION(view != aWidgetView, "will recur infinitely"); nsWindowType type = childWidget->WindowType(); if (view && childWidget->IsVisible() && type != eWindowType_popup) { NS_ASSERTION(childWidget->IsPlugin(), "Only plugin or popup widgets can be children!"); // We do not need to invalidate in plugin widgets, but we should // exclude them from the invalidation region IF we're not on // Mac. On Mac we need to draw under plugin widgets, because // plugin widgets are basically invisible #ifndef XP_MACOSX // GetBounds should compensate for chrome on a toplevel widget LayoutDeviceIntRect bounds; childWidget->GetBounds(bounds); nsTArray<LayoutDeviceIntRect> clipRects; childWidget->GetWindowClipRegion(&clipRects); for (uint32_t i = 0; i < clipRects.Length(); ++i) { nsRect rr = LayoutDeviceIntRect::ToAppUnits( clipRects[i] + bounds.TopLeft(), AppUnitsPerDevPixel()); children.Or(children, rr - aWidgetView->ViewToWidgetOffset()); children.SimplifyInward(20); } #endif } } } nsRegion leftOver; leftOver.Sub(aDamagedRegion, children); if (!leftOver.IsEmpty()) { for (auto iter = leftOver.RectIter(); !iter.Done(); iter.Next()) { LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get()); widget->Invalidate(bounds); } } }
NS_METHOD PuppetWidget::GetScreenBounds(LayoutDeviceIntRect& aRect) { aRect.MoveTo(WidgetToScreenOffset()); aRect.SizeTo(LayoutDeviceIntSize::FromUnknownSize(mBounds.Size())); return NS_OK; }
void ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) { mInTransaction = true; mTransactionStart = TimeStamp::Now(); #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG(("[----- BeginTransaction")); Log(); #endif NS_ASSERTION(!InTransaction(), "Nested transactions not allowed"); mPhase = PHASE_CONSTRUCTION; MOZ_ASSERT(mKeepAlive.IsEmpty(), "uncommitted txn?"); RefPtr<gfxContext> targetContext = aTarget; // If the last transaction was incomplete (a failed DoEmptyTransaction), // don't signal a new transaction to ShadowLayerForwarder. Carry on adding // to the previous transaction. dom::ScreenOrientationInternal orientation; if (dom::TabChild* window = mWidget->GetOwningTabChild()) { orientation = window->GetOrientation(); } else { hal::ScreenConfiguration currentConfig; hal::GetCurrentScreenConfiguration(¤tConfig); orientation = currentConfig.orientation(); } LayoutDeviceIntRect targetBounds = mWidget->GetNaturalBounds(); targetBounds.x = targetBounds.y = 0; mForwarder->BeginTransaction(targetBounds.ToUnknownRect(), mTargetRotation, orientation); // If we're drawing on behalf of a context with async pan/zoom // enabled, then the entire buffer of painted layers might be // composited (including resampling) asynchronously before we get // a chance to repaint, so we have to ensure that it's all valid // and not rotated. // // Desktop does not support async zoom yet, so we ignore this for those // platforms. #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_UIKIT) if (mWidget && mWidget->GetOwningTabChild()) { mCompositorMightResample = AsyncPanZoomEnabled(); } #endif // If we have a non-default target, we need to let our shadow manager draw // to it. This will happen at the end of the transaction. if (aTarget && XRE_IsParentProcess()) { mShadowTarget = aTarget; } else { NS_ASSERTION(!aTarget, "Content-process ClientLayerManager::BeginTransactionWithTarget not supported"); } // If this is a new paint, increment the paint sequence number. if (!mIsRepeatTransaction) { // Increment the paint sequence number even if test logging isn't // enabled in this process; it may be enabled in the parent process, // and the parent process expects unique sequence numbers. ++mPaintSequenceNumber; if (gfxPrefs::APZTestLoggingEnabled()) { mApzTestData.StartNewPaint(mPaintSequenceNumber); } } }
bool ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) { // Wait for any previous async paints to complete before starting to paint again. GetCompositorBridgeChild()->FlushAsyncPaints(); MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder"); if (!mForwarder->IPCOpen()) { gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died."; return false; } if (XRE_IsContentProcess() && mForwarder->DeviceCanReset() && mDeviceResetSequenceNumber != CompositorBridgeChild::Get()->DeviceResetSequenceNumber()) { // The compositor has informed this process that a device reset occurred, // but it has not finished informing each TabChild of its new // TextureFactoryIdentifier. Until then, it's illegal to paint. Note that // it is also illegal to request a new TIF synchronously, because we're // not guaranteed the UI process has finished acquiring new compositors // for each widget. // // Note that we only do this for accelerated backends, since we do not // perform resets on basic compositors. gfxCriticalNote << "Discarding a paint since a device reset has not yet been acknowledged."; return false; } mInTransaction = true; mTransactionStart = TimeStamp::Now(); #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG(("[----- BeginTransaction")); Log(); #endif NS_ASSERTION(!InTransaction(), "Nested transactions not allowed"); mPhase = PHASE_CONSTRUCTION; MOZ_ASSERT(mKeepAlive.IsEmpty(), "uncommitted txn?"); // If the last transaction was incomplete (a failed DoEmptyTransaction), // don't signal a new transaction to ShadowLayerForwarder. Carry on adding // to the previous transaction. dom::ScreenOrientationInternal orientation; if (dom::TabChild* window = mWidget->GetOwningTabChild()) { orientation = window->GetOrientation(); } else { hal::ScreenConfiguration currentConfig; hal::GetCurrentScreenConfiguration(¤tConfig); orientation = currentConfig.orientation(); } LayoutDeviceIntRect targetBounds = mWidget->GetNaturalBounds(); targetBounds.x = targetBounds.y = 0; mForwarder->BeginTransaction(targetBounds.ToUnknownRect(), mTargetRotation, orientation); // If we're drawing on behalf of a context with async pan/zoom // enabled, then the entire buffer of painted layers might be // composited (including resampling) asynchronously before we get // a chance to repaint, so we have to ensure that it's all valid // and not rotated. // // Desktop does not support async zoom yet, so we ignore this for those // platforms. #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT) if (mWidget && mWidget->GetOwningTabChild()) { mCompositorMightResample = AsyncPanZoomEnabled(); } #endif // If we have a non-default target, we need to let our shadow manager draw // to it. This will happen at the end of the transaction. if (aTarget && XRE_IsParentProcess()) { mShadowTarget = aTarget; } else { NS_ASSERTION(!aTarget, "Content-process ClientLayerManager::BeginTransactionWithTarget not supported"); } // If this is a new paint, increment the paint sequence number. if (!mIsRepeatTransaction) { // Increment the paint sequence number even if test logging isn't // enabled in this process; it may be enabled in the parent process, // and the parent process expects unique sequence numbers. ++mPaintSequenceNumber; if (gfxPrefs::APZTestLoggingEnabled()) { mApzTestData.StartNewPaint(mPaintSequenceNumber); } } return true; }
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; } NS_PRECONDITION(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; RefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext(); nsWindowType type = widget->WindowType(); LayoutDeviceIntRect curBounds; widget->GetClientBounds(curBounds); bool invisiblePopup = type == eWindowType_popup && ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) || mVis == nsViewVisibility_kHide); if (invisiblePopup) { // We're going to hit the early exit below, avoid calling CalcWidgetBounds. } else { newBounds = CalcWidgetBounds(type); } bool curVisibility = widget->IsVisible(); bool newVisibility = IsEffectivelyVisible(); if (curVisibility && !newVisibility) { widget->Show(false); } 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; } bool changedPos = curBounds.TopLeft() != newBounds.TopLeft(); bool changedSize = curBounds.Size() != newBounds.Size(); // 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). DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScale(); #ifdef XP_MACOSX // On OS X, this can be called before Cocoa has updated the backing scale // factor of our widget, in which case |scale| is wrong here. To work // around this, we check the device context and override |scale| if it // doesn't match. (This happens when a popup window that has previously // been created and hidden is being moved between hi- and lo-dpi screens, // but is not currently visible; Cocoa doesn't notify it of the scale // factor change until it gets shown on the new screen, which is too late // for us because we'll have already done the computations involving scale // here to move/size it.) // It might be better to avoid this by keeping calculations such as // CalcWidgetBounds entirely in appUnits, rather than using device pixels, // but that seems like a more extensive and potentially risky change. int32_t appPerDev = dx->AppUnitsPerDevPixelAtUnitFullZoom(); if (NSToIntRound(60.0 / scale.scale) != appPerDev) { scale = DesktopToLayoutDeviceScale(60.0 / appPerDev); } #endif DesktopRect deskRect = newBounds / scale; if (changedPos) { if (changedSize && !aMoveOnly) { widget->ResizeClient(deskRect.x, deskRect.y, deskRect.width, deskRect.height, aInvalidateChangedSize); } else { widget->MoveClient(deskRect.x, deskRect.y); } } else { if (changedSize && !aMoveOnly) { widget->ResizeClient(deskRect.width, deskRect.height, aInvalidateChangedSize); } // else do nothing! } if (!curVisibility && newVisibility) { widget->Show(true); } }
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; } NS_PRECONDITION(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; RefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext(); nsWindowType type = widget->WindowType(); LayoutDeviceIntRect curBounds; widget->GetClientBounds(curBounds); bool invisiblePopup = type == eWindowType_popup && ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) || mVis == nsViewVisibility_kHide); if (invisiblePopup) { // We're going to hit the early exit below, avoid calling CalcWidgetBounds. } else { newBounds = LayoutDeviceIntRect::FromUnknownRect(CalcWidgetBounds(type)); } bool curVisibility = widget->IsVisible(); bool newVisibility = IsEffectivelyVisible(); if (curVisibility && !newVisibility) { widget->Show(false); } 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; } bool changedPos = curBounds.TopLeft() != newBounds.TopLeft(); bool changedSize = curBounds.Size() != newBounds.Size(); // Child views are never attached to top level widgets, this is safe. // Coordinates are converted to display 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). double invScale; // Bug 861270: for correct widget manipulation at arbitrary scale factors, // prefer to base scaling on widget->GetDefaultScale(). But only do this if // it matches the view manager's device context scale after allowing for the // quantization to app units, because of OS X multiscreen issues (where the // only two scales are 1.0 or 2.0, and so the quantization doesn't actually // cause problems anyhow). // In the case of a mismatch, fall back to scaling based on the dev context's // AppUnitsPerDevPixelAtUnitFullZoom value. On platforms where the device-pixel // scale is uniform across all displays (currently all except OS X), we'll // always use the precise value from mWindow->GetDefaultScale here. CSSToLayoutDeviceScale scale = widget->GetDefaultScale(); if (NSToIntRound(60.0 / scale.scale) == dx->AppUnitsPerDevPixelAtUnitFullZoom()) { invScale = 1.0 / scale.scale; } else { invScale = dx->AppUnitsPerDevPixelAtUnitFullZoom() / 60.0; } if (changedPos) { if (changedSize && !aMoveOnly) { widget->ResizeClient(newBounds.x * invScale, newBounds.y * invScale, newBounds.width * invScale, newBounds.height * invScale, aInvalidateChangedSize); } else { widget->MoveClient(newBounds.x * invScale, newBounds.y * invScale); } } else { if (changedSize && !aMoveOnly) { widget->ResizeClient(newBounds.width * invScale, newBounds.height * invScale, aInvalidateChangedSize); } // else do nothing! } if (!curVisibility && newVisibility) { widget->Show(true); } }
nsresult nsTitleBarFrame::HandleEvent(nsPresContext* aPresContext, WidgetGUIEvent* aEvent, nsEventStatus* aEventStatus) { NS_ENSURE_ARG_POINTER(aEventStatus); if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { return NS_OK; } bool doDefault = true; switch (aEvent->mMessage) { case eMouseDown: { if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { // titlebar has no effect in non-chrome shells nsCOMPtr<nsIDocShellTreeItem> dsti = aPresContext->GetDocShell(); if (dsti) { if (dsti->ItemType() == nsIDocShellTreeItem::typeChrome) { // we're tracking. mTrackingMouseMove = true; // start capture. nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED); // remember current mouse coordinates. mLastPoint = aEvent->mRefPoint; } } *aEventStatus = nsEventStatus_eConsumeNoDefault; doDefault = false; } } break; case eMouseUp: { if (mTrackingMouseMove && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { // we're done tracking. mTrackingMouseMove = false; // end capture nsIPresShell::SetCapturingContent(nullptr, 0); *aEventStatus = nsEventStatus_eConsumeNoDefault; doDefault = false; } } break; case eMouseMove: { if(mTrackingMouseMove) { LayoutDeviceIntPoint nsMoveBy = aEvent->mRefPoint - mLastPoint; nsIFrame* parent = GetParent(); while (parent) { nsMenuPopupFrame* popupFrame = do_QueryFrame(parent); if (popupFrame) break; parent = parent->GetParent(); } // if the titlebar is in a popup, move the popup frame, otherwise // move the widget associated with the window if (parent) { nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame*>(parent); nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget(); LayoutDeviceIntRect bounds = widget->GetScreenBounds(); CSSPoint cssPos = (bounds.TopLeft() + nsMoveBy) / aPresContext->CSSToDevPixelScale(); menuPopupFrame->MoveTo(RoundedToInt(cssPos), false); } else { nsIPresShell* presShell = aPresContext->PresShell(); nsPIDOMWindowOuter *window = presShell->GetDocument()->GetWindow(); if (window) { window->MoveBy(nsMoveBy.x, nsMoveBy.y); } } *aEventStatus = nsEventStatus_eConsumeNoDefault; doDefault = false; } } break; case eMouseClick: { WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); if (mouseEvent->IsLeftClickEvent()) { MouseClicked(mouseEvent); } break; } default: break; } if ( doDefault ) return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus); else return NS_OK; }
nsresult nsResizerFrame::HandleEvent(nsPresContext* aPresContext, WidgetGUIEvent* aEvent, nsEventStatus* aEventStatus) { NS_ENSURE_ARG_POINTER(aEventStatus); if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { return NS_OK; } nsWeakFrame weakFrame(this); bool doDefault = true; switch (aEvent->mMessage) { case eTouchStart: case eMouseDown: { if (aEvent->mClass == eTouchEventClass || (aEvent->mClass == eMouseEventClass && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton)) { nsCOMPtr<nsIBaseWindow> window; nsIPresShell* presShell = aPresContext->GetPresShell(); nsIContent* contentToResize = GetContentToResize(presShell, getter_AddRefs(window)); if (contentToResize) { nsIFrame* frameToResize = contentToResize->GetPrimaryFrame(); if (!frameToResize) break; // cache the content rectangle for the frame to resize // GetScreenRectInAppUnits returns the border box rectangle, so // adjust to get the desired content rectangle. nsRect rect = frameToResize->GetScreenRectInAppUnits(); switch (frameToResize->StylePosition()->mBoxSizing) { case StyleBoxSizing::Content: rect.Deflate(frameToResize->GetUsedPadding()); MOZ_FALLTHROUGH; case StyleBoxSizing::Padding: rect.Deflate(frameToResize->GetUsedBorder()); MOZ_FALLTHROUGH; case StyleBoxSizing::Border: // nothing break; } mMouseDownRect = LayoutDeviceIntRect::FromAppUnitsToNearest(rect, aPresContext->AppUnitsPerDevPixel()); doDefault = false; } else { // If there is no window, then resizing isn't allowed. if (!window) break; doDefault = false; // ask the widget implementation to begin a resize drag if it can Direction direction = GetDirection(); nsresult rv = aEvent->mWidget->BeginResizeDrag(aEvent, direction.mHorizontal, direction.mVertical); // for native drags, don't set the fields below if (rv != NS_ERROR_NOT_IMPLEMENTED) break; // if there's no native resize support, we need to do window // resizing ourselves window->GetPositionAndSize(&mMouseDownRect.x, &mMouseDownRect.y, &mMouseDownRect.width, &mMouseDownRect.height); } // remember current mouse coordinates LayoutDeviceIntPoint refPoint; if (!GetEventPoint(aEvent, refPoint)) return NS_OK; mMouseDownPoint = refPoint + aEvent->mWidget->WidgetToScreenOffset(); // we're tracking mTrackingMouseMove = true; nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED); } } break; case eTouchEnd: case eMouseUp: { if (aEvent->mClass == eTouchEventClass || (aEvent->mClass == eMouseEventClass && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton)) { // we're done tracking. mTrackingMouseMove = false; nsIPresShell::SetCapturingContent(nullptr, 0); doDefault = false; } } break; case eTouchMove: case eMouseMove: { if (mTrackingMouseMove) { nsCOMPtr<nsIBaseWindow> window; nsIPresShell* presShell = aPresContext->GetPresShell(); nsCOMPtr<nsIContent> contentToResize = GetContentToResize(presShell, getter_AddRefs(window)); // check if the returned content really is a menupopup nsMenuPopupFrame* menuPopupFrame = nullptr; if (contentToResize) { menuPopupFrame = do_QueryFrame(contentToResize->GetPrimaryFrame()); } // both MouseMove and direction are negative when pointing to the // top and left, and positive when pointing to the bottom and right // retrieve the offset of the mousemove event relative to the mousedown. // The difference is how much the resize needs to be LayoutDeviceIntPoint refPoint; if (!GetEventPoint(aEvent, refPoint)) return NS_OK; LayoutDeviceIntPoint screenPoint = refPoint + aEvent->mWidget->WidgetToScreenOffset(); LayoutDeviceIntPoint mouseMove(screenPoint - mMouseDownPoint); // Determine which direction to resize by checking the dir attribute. // For windows and menus, ensure that it can be resized in that direction. Direction direction = GetDirection(); if (window || menuPopupFrame) { if (menuPopupFrame) { menuPopupFrame->CanAdjustEdges( (direction.mHorizontal == -1) ? NS_SIDE_LEFT : NS_SIDE_RIGHT, (direction.mVertical == -1) ? NS_SIDE_TOP : NS_SIDE_BOTTOM, mouseMove); } } else if (!contentToResize) { break; // don't do anything if there's nothing to resize } LayoutDeviceIntRect rect = mMouseDownRect; // Check if there are any size constraints on this window. widget::SizeConstraints sizeConstraints; if (window) { nsCOMPtr<nsIWidget> widget; window->GetMainWidget(getter_AddRefs(widget)); sizeConstraints = widget->GetSizeConstraints(); } AdjustDimensions(&rect.x, &rect.width, sizeConstraints.mMinSize.width, sizeConstraints.mMaxSize.width, mouseMove.x, direction.mHorizontal); AdjustDimensions(&rect.y, &rect.height, sizeConstraints.mMinSize.height, sizeConstraints.mMaxSize.height, mouseMove.y, direction.mVertical); // Don't allow resizing a window or a popup past the edge of the screen, // so adjust the rectangle to fit within the available screen area. if (window) { nsCOMPtr<nsIScreen> screen; nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1")); if (sm) { nsIntRect frameRect = GetScreenRect(); // ScreenForRect requires display pixels, so scale from device pix double scale; window->GetUnscaledDevicePixelsPerCSSPixel(&scale); sm->ScreenForRect(NSToIntRound(frameRect.x / scale), NSToIntRound(frameRect.y / scale), 1, 1, getter_AddRefs(screen)); if (screen) { LayoutDeviceIntRect screenRect; screen->GetRect(&screenRect.x, &screenRect.y, &screenRect.width, &screenRect.height); rect.IntersectRect(rect, screenRect); } } } else if (menuPopupFrame) { nsRect frameRect = menuPopupFrame->GetScreenRectInAppUnits(); nsIFrame* rootFrame = aPresContext->PresShell()->FrameManager()->GetRootFrame(); nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits(); nsPopupLevel popupLevel = menuPopupFrame->PopupLevel(); int32_t appPerDev = aPresContext->AppUnitsPerDevPixel(); LayoutDeviceIntRect screenRect = menuPopupFrame->GetConstraintRect (LayoutDeviceIntRect::FromAppUnitsToNearest(frameRect, appPerDev), // round using ...ToInside as it's better to be a pixel too small // than be too large. If the popup is too large it could get flipped // to the opposite side of the anchor point while resizing. LayoutDeviceIntRect::FromAppUnitsToInside(rootScreenRect, appPerDev), popupLevel); rect.IntersectRect(rect, screenRect); } if (contentToResize) { // convert the rectangle into css pixels. When changing the size in a // direction, don't allow the new size to be less that the resizer's // size. This ensures that content isn't resized too small as to make // the resizer invisible. nsRect appUnitsRect = ToAppUnits(rect.ToUnknownRect(), aPresContext->AppUnitsPerDevPixel()); if (appUnitsRect.width < mRect.width && mouseMove.x) appUnitsRect.width = mRect.width; if (appUnitsRect.height < mRect.height && mouseMove.y) appUnitsRect.height = mRect.height; nsIntRect cssRect = appUnitsRect.ToInsidePixels(nsPresContext::AppUnitsPerCSSPixel()); LayoutDeviceIntRect oldRect; nsWeakFrame weakFrame(menuPopupFrame); if (menuPopupFrame) { nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget(); if (widget) widget->GetScreenBounds(oldRect); // convert the new rectangle into outer window coordinates LayoutDeviceIntPoint clientOffset = widget->GetClientOffset(); rect.x -= clientOffset.x; rect.y -= clientOffset.y; } SizeInfo sizeInfo, originalSizeInfo; sizeInfo.width.AppendInt(cssRect.width); sizeInfo.height.AppendInt(cssRect.height); ResizeContent(contentToResize, direction, sizeInfo, &originalSizeInfo); MaybePersistOriginalSize(contentToResize, originalSizeInfo); // Move the popup to the new location unless it is anchored, since // the position shouldn't change. nsMenuPopupFrame::SetPopupPosition // will instead ensure that the popup's position is anchored at the // right place. if (weakFrame.IsAlive() && (oldRect.x != rect.x || oldRect.y != rect.y) && (!menuPopupFrame->IsAnchored() || menuPopupFrame->PopupLevel() != ePopupLevelParent)) { CSSPoint cssPos = rect.TopLeft() / aPresContext->CSSToDevPixelScale(); menuPopupFrame->MoveTo(RoundedToInt(cssPos), true); } } else { window->SetPositionAndSize(rect.x, rect.y, rect.width, rect.height, true); // do the repaint. } doDefault = false; } } break; case eMouseClick: { WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); if (mouseEvent->IsLeftClickEvent()) { MouseClicked(mouseEvent); } break; } case eMouseDoubleClick: if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { nsCOMPtr<nsIBaseWindow> window; nsIPresShell* presShell = aPresContext->GetPresShell(); nsIContent* contentToResize = GetContentToResize(presShell, getter_AddRefs(window)); if (contentToResize) { nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(contentToResize->GetPrimaryFrame()); if (menuPopupFrame) break; // Don't restore original sizing for menupopup frames until // we handle screen constraints here. (Bug 357725) RestoreOriginalSize(contentToResize); } } break; default: break; } if (!doDefault) *aEventStatus = nsEventStatus_eConsumeNoDefault; if (doDefault && weakFrame.IsAlive()) return nsTitleBarFrame::HandleEvent(aPresContext, aEvent, aEventStatus); return NS_OK; }
LayoutDeviceIntRect nsView::CalcWidgetBounds(nsWindowType aType) { int32_t p2a = mViewManager->AppUnitsPerDevPixel(); nsRect viewBounds(mDimBounds); 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 == eWindowType_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 LayoutDeviceIntRect newBounds = LayoutDeviceIntRect::FromUnknownRect(viewBounds.ToNearestPixels(p2a)); #if defined(XP_MACOSX) || (MOZ_WIDGET_GTK == 3) // 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. nsIWidget* widget = parentWidget ? parentWidget : mWindow.get(); uint32_t round; if (aType == eWindowType_popup && widget && ((round = widget->RoundsWidgetCoordinatesTo()) > 1)) { LayoutDeviceIntSize pixelRoundedSize = newBounds.Size(); // round the top left and bottom right to the nearest round pixel newBounds.x = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.x, p2a) / round) * round; newBounds.y = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.y, p2a) / round) * round; newBounds.width = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.XMost(), p2a) / round) * round - newBounds.x; newBounds.height = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.YMost(), p2a) / round) * round - newBounds.y; // but if that makes the widget larger then our frame may not paint the // extra pixels, so reduce the size to the nearest round value if (newBounds.width > pixelRoundedSize.width) { newBounds.width -= round; } if (newBounds.height > pixelRoundedSize.height) { newBounds.height -= round; } } #endif // 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; return newBounds; }