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 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; }
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; }
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; }