bool ClientTiledPaintedLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion, const nsIntRegion& aVisibleRegion, LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) { // Render the low precision buffer, if the visible region is larger than the // critical display port. if (!nsIntRegion(LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)).Contains(aVisibleRegion)) { nsIntRegion oldValidRegion = mContentClient->mLowPrecisionTiledBuffer.GetValidRegion(); oldValidRegion.And(oldValidRegion, aVisibleRegion); bool updatedBuffer = false; // If the frame resolution or format have changed, invalidate the buffer if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution || mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) { if (!mLowPrecisionValidRegion.IsEmpty()) { updatedBuffer = true; } oldValidRegion.SetEmpty(); mLowPrecisionValidRegion.SetEmpty(); mContentClient->mLowPrecisionTiledBuffer.ResetPaintedAndValidState(); mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution); aInvalidRegion = aVisibleRegion; } // Invalidate previously valid content that is no longer visible if (mPaintData.mLowPrecisionPaintCount == 1) { mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, aVisibleRegion); } mPaintData.mLowPrecisionPaintCount++; // Remove the valid high-precision region from the invalid low-precision // region. We don't want to spend time drawing things twice. aInvalidRegion.Sub(aInvalidRegion, mValidRegion); TILING_LOG("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, Stringify(aInvalidRegion).c_str()); TILING_LOG("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, Stringify(oldValidRegion).c_str()); if (!aInvalidRegion.IsEmpty()) { updatedBuffer = mContentClient->mLowPrecisionTiledBuffer.ProgressiveUpdate( mLowPrecisionValidRegion, aInvalidRegion, oldValidRegion, &mPaintData, aCallback, aCallbackData); } TILING_LOG("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, Stringify(mLowPrecisionValidRegion).c_str()); return updatedBuffer; } if (!mLowPrecisionValidRegion.IsEmpty()) { TILING_LOG("TILING %p: Clearing low-precision buffer\n", this); // Clear the low precision tiled buffer. mLowPrecisionValidRegion.SetEmpty(); mContentClient->mLowPrecisionTiledBuffer.ResetPaintedAndValidState(); // Return true here so we send a Painted callback after clearing the valid // region of the low precision buffer. This allows the shadow buffer's valid // region to be updated and the associated resources to be freed. return true; } return false; }
void ClientTiledPaintedLayer::RenderLayer() { LayerManager::DrawPaintedLayerCallback callback = ClientManager()->GetPaintedLayerCallback(); void *data = ClientManager()->GetPaintedLayerCallbackData(); if (!callback) { ClientManager()->SetTransactionIncomplete(); return; } if (!mContentClient) { mContentClient = new TiledContentClient(this, ClientManager()); mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } if (mContentClient->mTiledBuffer.HasFormatChanged()) { mValidRegion = nsIntRegion(); mContentClient->mTiledBuffer.ResetPaintedAndValidState(); } TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str()); TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str()); TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str()); nsIntRegion neededRegion = mVisibleRegion; #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile if (MayResample()) { // If we're resampling then bilinear filtering can read up to 1 pixel // outside of our texture coords. Make the visible region a single rect, // and pad it out by 1 pixel (restricted to tile boundaries) so that // we always have valid content or transparent pixels to sample from. nsIntRect bounds = neededRegion.GetBounds(); nsIntRect wholeTiles = bounds; wholeTiles.Inflate(nsIntSize( gfxPlatform::GetPlatform()->GetTileWidth(), gfxPlatform::GetPlatform()->GetTileHeight())); nsIntRect padded = bounds; padded.Inflate(1); padded.IntersectRect(padded, wholeTiles); neededRegion = padded; } #endif nsIntRegion invalidRegion; invalidRegion.Sub(neededRegion, mValidRegion); if (invalidRegion.IsEmpty()) { EndPaint(); return; } if (!ClientManager()->IsRepeatTransaction()) { // Only paint the mask layer on the first transaction. if (GetMaskLayer()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } // In some cases we can take a fast path and just be done with it. if (UseFastPath()) { TILING_LOG("TILING %p: Taking fast-path\n", this); mValidRegion = neededRegion; mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); return; } // For more complex cases we need to calculate a bunch of metrics before we // can do the paint. BeginPaint(); if (mPaintData.mPaintFinished) { return; } // Make sure that tiles that fall outside of the visible region or outside of the // critical displayport are discarded on the first update. Also make sure that we // only draw stuff inside the critical displayport on the first update. mValidRegion.And(mValidRegion, neededRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(mValidRegion).c_str()); TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); } else { if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); } nsIntRegion lowPrecisionInvalidRegion; if (gfxPrefs::UseLowPrecisionBuffer()) { // Calculate the invalid region for the low precision buffer. Make sure // to remove the valid high-precision area so we don't double-paint it. lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion); lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); } TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str()); bool updatedHighPrecision = RenderHighPrecision(invalidRegion, neededRegion, callback, data); if (updatedHighPrecision) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); if (!mPaintData.mPaintFinished) { // There is still more high-res stuff to paint, so we're not // done yet. A subsequent transaction will take care of this. ClientManager()->SetRepeatTransaction(); return; } } // If there is nothing to draw in low-precision, then we're done. if (lowPrecisionInvalidRegion.IsEmpty()) { EndPaint(); return; } if (updatedHighPrecision) { // If there are low precision updates, but we just did some high-precision // updates, then mark the paint as unfinished and request a repeat transaction. // This is so that we don't perform low-precision updates in the same transaction // as high-precision updates. TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this); ClientManager()->SetRepeatTransaction(); mPaintData.mLowPrecisionPaintCount = 1; mPaintData.mPaintFinished = false; return; } bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion, neededRegion, callback, data); if (updatedLowPrecision) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER); if (!mPaintData.mPaintFinished) { // There is still more low-res stuff to paint, so we're not // done yet. A subsequent transaction will take care of this. ClientManager()->SetRepeatTransaction(); return; } } // If we get here, we've done all the high- and low-precision // paints we wanted to do, so we can finish the paint and chill. EndPaint(); }
void ClientTiledPaintedLayer::BeginPaint() { mPaintData.mLowPrecisionPaintCount = 0; mPaintData.mPaintFinished = false; mPaintData.mCompositionBounds.SetEmpty(); mPaintData.mCriticalDisplayPort.SetEmpty(); if (!GetBaseTransform().Is2D()) { // Give up if there is a complex CSS transform on the layer. We might // eventually support these but for now it's too complicated to handle // given that it's a pretty rare scenario. return; } // Get the metrics of the nearest scrollable layer and the nearest layer // with a displayport. LayerMetricsWrapper scrollAncestor; LayerMetricsWrapper displayPortAncestor; GetAncestorLayers(&scrollAncestor, &displayPortAncestor); if (!displayPortAncestor || !scrollAncestor) { // No displayport or scroll ancestor, so we can't do progressive rendering. #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G) // Both Android and b2g are guaranteed to have a displayport set, so this // should never happen. NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor"); #endif return; } TILING_LOG("TILING %p: Found scrollAncestor %p and displayPortAncestor %p\n", this, scrollAncestor.GetLayer(), displayPortAncestor.GetLayer()); const FrameMetrics& scrollMetrics = scrollAncestor.Metrics(); const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics(); // Calculate the transform required to convert ParentLayer space of our // display port ancestor to the Layer space of this layer. gfx::Matrix4x4 transformDisplayPortToLayer = GetTransformToAncestorsParentLayer(this, displayPortAncestor); transformDisplayPortToLayer.Invert(); // Compute the critical display port that applies to this layer in the // LayoutDevice space of this layer. ParentLayerRect criticalDisplayPort = (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom()) + displayportMetrics.mCompositionBounds.TopLeft(); mPaintData.mCriticalDisplayPort = RoundedOut( ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort)); TILING_LOG("TILING %p: Critical displayport %s\n", this, Stringify(mPaintData.mCriticalDisplayPort).c_str()); // Store the resolution from the displayport ancestor layer. Because this is Gecko-side, // before any async transforms have occurred, we can use the zoom for this. mPaintData.mResolution = displayportMetrics.GetZoom(); TILING_LOG("TILING %p: Resolution %f\n", this, mPaintData.mResolution.scale); // Store the applicable composition bounds in this layer's Layer units. mPaintData.mTransformToCompBounds = GetTransformToAncestorsParentLayer(this, scrollAncestor); gfx::Matrix4x4 transformToBounds = mPaintData.mTransformToCompBounds; transformToBounds.Invert(); mPaintData.mCompositionBounds = ApplyParentLayerToLayerTransform( transformToBounds, scrollMetrics.mCompositionBounds); TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str()); // Calculate the scroll offset since the last transaction mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom(); TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str()); }
void APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, nsEventStatus aApzResponse, nsEventStatus aContentResponse) { if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() > 0) { mActiveElementManager->SetTargetElement(aEvent.mTouches[0]->GetTarget()); mLastTouchIdentifier = aEvent.mTouches[0]->Identifier(); } bool isTouchPrevented = aContentResponse == nsEventStatus_eConsumeNoDefault; bool sentContentResponse = false; APZES_LOG("Handling event type %d\n", aEvent.mMessage); switch (aEvent.mMessage) { case eTouchStart: { mTouchEndCancelled = false; sentContentResponse = SendPendingTouchPreventedResponse(false); // sentContentResponse can be true here if we get two TOUCH_STARTs in a row // and just responded to the first one. // We're about to send a response back to APZ, but we should only do it // for events that went through APZ (which should be all of them). MOZ_ASSERT(aEvent.mFlags.mHandledByAPZ); if (isTouchPrevented) { mContentReceivedInputBlockCallback(aGuid, aInputBlockId, isTouchPrevented); sentContentResponse = true; } else { APZES_LOG("Event not prevented; pending response for %" PRIu64 " %s\n", aInputBlockId, Stringify(aGuid).c_str()); mPendingTouchPreventedResponse = true; mPendingTouchPreventedGuid = aGuid; mPendingTouchPreventedBlockId = aInputBlockId; } break; } case eTouchEnd: if (isTouchPrevented) { mTouchEndCancelled = true; mEndTouchIsClick = false; } MOZ_FALLTHROUGH; case eTouchCancel: mActiveElementManager->HandleTouchEndEvent(mEndTouchIsClick); MOZ_FALLTHROUGH; case eTouchMove: { if (mPendingTouchPreventedResponse) { MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid); } sentContentResponse = SendPendingTouchPreventedResponse(isTouchPrevented); break; } default: NS_WARNING("Unknown touch event type"); } if (sentContentResponse && aApzResponse == nsEventStatus_eConsumeDoDefault && gfxPrefs::PointerEventsEnabled()) { WidgetTouchEvent cancelEvent(aEvent); cancelEvent.mMessage = eTouchCancel; cancelEvent.mFlags.mCancelable = false; // mMessage != eTouchCancel; for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) { if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) { touch->convertToPointer = true; } } nsEventStatus status; cancelEvent.mWidget->DispatchEvent(&cancelEvent, status); } }
void UpdateStatusLines() { std::string rule = currlayer->algo->getrule(); status1 = "Pattern="; if (currlayer->dirty) { // display asterisk to indicate pattern has been modified status1 += "*"; } status1 += currlayer->currname; status1 += widescreen ? large_algo_prefix : small_algo_prefix; status1 += GetAlgoName(currlayer->algtype); status1 += widescreen ? large_rule_prefix : small_rule_prefix; status1 += rule; // show rule name if one exists and is not same as rule // (best NOT to remove any suffix like ":T100,200" in case we allow // users to name "B3/S23:T100,200" as "Life on torus") std::string rulename = GetRuleName(rule); if (!rulename.empty() && rulename != rule) { status1 += " ["; status1 += rulename; status1 += "]"; } char scalestr[32]; int mag = currlayer->view->getmag(); if (mag < 0) { sprintf(scalestr, "2^%d:1", -mag); } else { sprintf(scalestr, "1:%d", 1 << mag); } char stepstr[32]; if (currlayer->currexpo < 0) { // show delay in secs sprintf(stepstr, "Delay=%gs", (double)GetCurrentDelay() / 1000.0); } else { sprintf(stepstr, "Step=%d^%d", currlayer->currbase, currlayer->currexpo); } status2 = widescreen ? large_gen_prefix : small_gen_prefix; if (nopattupdate) { status2 += "0"; } else { status2 += Stringify(currlayer->algo->getGeneration()); } status2 += widescreen ? large_pop_prefix : small_pop_prefix; if (nopattupdate) { status2 += "0"; } else { bigint popcount = currlayer->algo->getPopulation(); if (popcount.sign() < 0) { // getPopulation returns -1 if it can't be calculated status2 += "?"; } else { status2 += Stringify(popcount); } } status2 += widescreen ? large_scale_prefix : small_scale_prefix; status2 += scalestr; status2 += widescreen ? large_step_prefix : small_step_prefix; status2 += stepstr; status2 += widescreen ? large_xy_prefix : small_xy_prefix; #ifdef WEB_GUI // in web app we show the cursor's current cell location, // or nothing if the cursor is outside the viewport (ie. showxy is false) if (showxy) { bigint xo, yo; bigint xpos = currx; xpos -= currlayer->originx; bigint ypos = curry; ypos -= currlayer->originy; if (mathcoords) { // Y values increase upwards bigint temp = 0; temp -= ypos; ypos = temp; } status2 += Stringify(xpos); status2 += " "; status2 += Stringify(ypos); } #else // in iOS and Android apps we show location of the cell in middle of viewport status2 += Stringify(currlayer->view->x); status2 += " "; status2 += Stringify(currlayer->view->y); #endif }
void ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion, const nsIntRegion& aDirtyRegion, LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData, bool aIsProgressive) { mWasLastPaintProgressive = aIsProgressive; // Compare layer valid region size to current backbuffer size, discard if not matching. gfx::IntSize size = aNewValidRegion.GetBounds().Size(); gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft(); nsIntRegion paintRegion = aPaintRegion; RefPtr<TextureClient> discardedFrontBuffer = nullptr; RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr; nsIntRegion discardedValidRegion; if (mSize != size || mTilingOrigin != origin) { discardedFrontBuffer = mTile.mFrontBuffer; discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite; discardedValidRegion = mValidRegion; TILING_LOG("TILING %p: Single-tile valid region changed. Discarding buffers.\n", &mPaintedLayer) ; ResetPaintedAndValidState(); mSize = size; mTilingOrigin = origin; paintRegion = aNewValidRegion; } SurfaceMode mode; gfxContentType content = GetContentType(&mode); mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content); if (mTile.IsPlaceholderTile()) { mTile.SetTextureAllocator(this); } // The dirty region relative to the top-left of the tile. nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin); nsIntRegion extraPainted; RefPtr<TextureClient> backBufferOnWhite; RefPtr<TextureClient> backBuffer = mTile.GetBackBuffer(mCompositableClient, tileDirtyRegion, content, mode, extraPainted, &backBufferOnWhite); mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(extraPainted.GetBounds()); extraPainted.MoveBy(mTilingOrigin); extraPainted.And(extraPainted, aNewValidRegion); mPaintedRegion.OrWith(paintRegion); mPaintedRegion.OrWith(extraPainted); if (!backBuffer) { return; } RefPtr<gfx::DrawTarget> dt = backBuffer->BorrowDrawTarget(); RefPtr<gfx::DrawTarget> dtOnWhite; if (backBufferOnWhite) { dtOnWhite = backBufferOnWhite->BorrowDrawTarget(); } if (mode != SurfaceMode::SURFACE_OPAQUE) { for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) { const gfx::IntRect& rect = iter.Get(); if (dtOnWhite) { dt->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height), gfx::ColorPattern(gfx::Color(0.0, 0.0, 0.0, 1.0))); dtOnWhite->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height), gfx::ColorPattern(gfx::Color(1.0, 1.0, 1.0, 1.0))); } else { dt->ClearRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height)); } } } // If the old frontbuffer was discarded then attempt to copy what we // can from it to the new backbuffer. if (discardedFrontBuffer) { nsIntRegion copyableRegion; copyableRegion.And(aNewValidRegion, discardedValidRegion); copyableRegion.SubOut(aDirtyRegion); if (!copyableRegion.IsEmpty()) { TextureClientAutoLock frontLock(discardedFrontBuffer, OpenMode::OPEN_READ); if (frontLock.Succeeded()) { for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) { const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft(); const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin; discardedFrontBuffer->CopyToTextureClient(backBuffer, &rect, &dest); } } if (discardedFrontBufferOnWhite && backBufferOnWhite) { TextureClientAutoLock frontOnWhiteLock(discardedFrontBufferOnWhite, OpenMode::OPEN_READ); if (frontOnWhiteLock.Succeeded()) { for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) { const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft(); const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin; discardedFrontBufferOnWhite->CopyToTextureClient(backBufferOnWhite, &rect, &dest); } } } TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str()); // We don't need to repaint valid content that was just copied. paintRegion.SubOut(copyableRegion); } } if (dtOnWhite) { dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite); dtOnWhite = nullptr; } { RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt); if (!ctx) { gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt); return; } ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y)); aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData); } // Mark the area we just drew into the back buffer as invalid in the front buffer as they're // now out of sync. mTile.mInvalidFront.OrWith(tileDirtyRegion); // The new buffer is now validated, remove the dirty region from it. mTile.mInvalidBack.SubOut(tileDirtyRegion); dt = nullptr; mTile.Flip(); UnlockTile(mTile); if (backBuffer->HasIntermediateBuffer()) { // If our new buffer has an internal buffer, we don't want to keep another // TextureClient around unnecessarily, so discard the back-buffer. mTile.DiscardBackBuffer(); } mValidRegion = aNewValidRegion; mLastPaintSurfaceMode = mode; mLastPaintContentType = content; }
void APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, nsEventStatus aApzResponse, nsEventStatus aContentResponse) { if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() > 0) { mActiveElementManager->SetTargetElement(aEvent.mTouches[0]->GetTarget()); } bool isTouchPrevented = aContentResponse == nsEventStatus_eConsumeNoDefault; bool sentContentResponse = false; APZES_LOG("Handling event type %d\n", aEvent.mMessage); switch (aEvent.mMessage) { case eTouchStart: { mTouchEndCancelled = false; sentContentResponse = SendPendingTouchPreventedResponse(false); // sentContentResponse can be true here if we get two TOUCH_STARTs in a row // and just responded to the first one. if (!aEvent.mFlags.mHandledByAPZ) { // This condition being true means this touchstart is synthetic and is // coming from TabParent.injectTouchEvent. // Since APZ doesn't know about it we don't want to send a response for // this block; we want to just skip over it from the point of view of // prevent-default notifications. APZES_LOG("Got a synthetic touch-start!\n"); break; } if (isTouchPrevented) { mContentReceivedInputBlockCallback(aGuid, aInputBlockId, isTouchPrevented); sentContentResponse = true; } else { APZES_LOG("Event not prevented; pending response for %" PRIu64 " %s\n", aInputBlockId, Stringify(aGuid).c_str()); mPendingTouchPreventedResponse = true; mPendingTouchPreventedGuid = aGuid; mPendingTouchPreventedBlockId = aInputBlockId; } break; } case eTouchEnd: if (isTouchPrevented) { mTouchEndCancelled = true; mEndTouchIsClick = false; } MOZ_FALLTHROUGH; case eTouchCancel: mActiveElementManager->HandleTouchEndEvent(mEndTouchIsClick); MOZ_FALLTHROUGH; case eTouchMove: { if (mPendingTouchPreventedResponse) { MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid); } sentContentResponse = SendPendingTouchPreventedResponse(isTouchPrevented); break; } default: NS_WARNING("Unknown touch event type"); } if (sentContentResponse && aApzResponse == nsEventStatus_eConsumeDoDefault && gfxPrefs::PointerEventsEnabled()) { WidgetTouchEvent cancelEvent(aEvent); cancelEvent.mMessage = eTouchCancel; cancelEvent.mFlags.mCancelable = false; // mMessage != eTouchCancel; for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) { if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) { touch->convertToPointer = true; } } nsEventStatus status; cancelEvent.widget->DispatchEvent(&cancelEvent, status); } }