tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

APZCCallbackHelper.cpp (42974B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "APZCCallbackHelper.h"
      8 
      9 #include "APZEventState.h"  // for PrecedingPointerDown
     10 
     11 #include "gfxPlatform.h"  // For gfxPlatform::UseTiling
     12 
     13 #include "mozilla/AsyncEventDispatcher.h"
     14 #include "mozilla/EventForwards.h"
     15 #include "mozilla/dom/CustomEvent.h"
     16 #include "mozilla/dom/Element.h"
     17 #include "mozilla/dom/MouseEventBinding.h"
     18 #include "mozilla/dom/BrowserParent.h"
     19 #include "mozilla/dom/ScriptSettings.h"
     20 #include "mozilla/IntegerPrintfMacros.h"
     21 #include "mozilla/layers/RepaintRequest.h"
     22 #include "mozilla/layers/WebRenderLayerManager.h"
     23 #include "mozilla/layers/WebRenderBridgeChild.h"
     24 #include "mozilla/DisplayPortUtils.h"
     25 #include "mozilla/PresShell.h"
     26 #include "mozilla/ScrollContainerFrame.h"
     27 #include "mozilla/ToString.h"
     28 #include "mozilla/ViewportUtils.h"
     29 #include "nsContainerFrame.h"
     30 #include "nsContentUtils.h"
     31 #include "nsIContent.h"
     32 #include "nsIDOMWindowUtils.h"
     33 #include "mozilla/dom/Document.h"
     34 #include "nsIInterfaceRequestorUtils.h"
     35 #include "nsLayoutUtils.h"
     36 #include "nsMenuPopupFrame.h"
     37 #include "nsPrintfCString.h"
     38 #include "nsPIDOMWindow.h"
     39 #include "nsRefreshDriver.h"
     40 #include "nsString.h"
     41 
     42 static mozilla::LazyLogModule sApzHlpLog("apz.helper");
     43 #define APZCCH_LOG(...) MOZ_LOG(sApzHlpLog, LogLevel::Debug, (__VA_ARGS__))
     44 static mozilla::LazyLogModule sDisplayportLog("apz.displayport");
     45 
     46 namespace mozilla {
     47 namespace layers {
     48 
     49 using dom::BrowserParent;
     50 
     51 uint64_t APZCCallbackHelper::sLastTargetAPZCNotificationInputBlock =
     52    uint64_t(-1);
     53 
     54 static ScreenMargin RecenterDisplayPort(const ScreenMargin& aDisplayPort) {
     55  ScreenMargin margins = aDisplayPort;
     56  margins.right = margins.left = margins.LeftRight() / 2;
     57  margins.top = margins.bottom = margins.TopBottom() / 2;
     58  return margins;
     59 }
     60 
     61 static PresShell* GetPresShell(const nsIContent* aContent) {
     62  if (dom::Document* doc = aContent->GetComposedDoc()) {
     63    return doc->GetPresShell();
     64  }
     65  return nullptr;
     66 }
     67 
     68 static CSSPoint ScrollFrameTo(ScrollContainerFrame* aFrame,
     69                              const RepaintRequest& aRequest,
     70                              bool& aSuccessOut) {
     71  aSuccessOut = false;
     72  CSSPoint targetScrollPosition = aRequest.GetLayoutScrollOffset();
     73 
     74  if (!aFrame) {
     75    return targetScrollPosition;
     76  }
     77 
     78  CSSPoint geckoScrollPosition =
     79      CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
     80 
     81  // If the repaint request was triggered due to a previous main-thread scroll
     82  // offset update sent to the APZ, then we don't need to do another scroll here
     83  // and we can just return.
     84  if (!aRequest.GetScrollOffsetUpdated()) {
     85    return geckoScrollPosition;
     86  }
     87 
     88  // If this frame is overflow:hidden, then the expectation is that it was
     89  // sized in a way that respects its scrollable boundaries. For the root
     90  // frame, this means that it cannot be scrolled in such a way that it moves
     91  // the layout viewport. For a non-root frame, this means that it cannot be
     92  // scrolled at all.
     93  //
     94  // In either case, |targetScrollPosition| should be the same as
     95  // |geckoScrollPosition| here.
     96  //
     97  // However, this is slightly racy. We query the overflow property of the
     98  // scroll frame at the time the repaint request arrives at the main thread
     99  // (i.e., right now), but APZ made the decision of whether or not to allow
    100  // scrolling based on the information it had at the time it processed the
    101  // scroll event. The overflow property could have changed at some time
    102  // between the two events and so APZ may have computed a scrollable region
    103  // that is larger than what is actually allowed.
    104  //
    105  // Currently, we allow the scroll position to change even though the frame is
    106  // overflow:hidden (that is, we take |targetScrollPosition|). If this turns
    107  // out to be problematic, an alternative solution would be to ignore the
    108  // scroll position change (that is, use |geckoScrollPosition|).
    109  if (aFrame->GetScrollStyles().mVertical == StyleOverflow::Hidden &&
    110      targetScrollPosition.y != geckoScrollPosition.y) {
    111    NS_WARNING(
    112        nsPrintfCString(
    113            "APZCCH: targetScrollPosition.y (%f) != geckoScrollPosition.y (%f)",
    114            targetScrollPosition.y.value, geckoScrollPosition.y.value)
    115            .get());
    116  }
    117  if (aFrame->GetScrollStyles().mHorizontal == StyleOverflow::Hidden &&
    118      targetScrollPosition.x != geckoScrollPosition.x) {
    119    NS_WARNING(
    120        nsPrintfCString(
    121            "APZCCH: targetScrollPosition.x (%f) != geckoScrollPosition.x (%f)",
    122            targetScrollPosition.x.value, geckoScrollPosition.x.value)
    123            .get());
    124  }
    125 
    126  // If the scrollable frame is currently in the middle of an async or smooth
    127  // scroll then we don't want to interrupt it (see bug 961280).
    128  // Also if the scrollable frame got a scroll request from a higher priority
    129  // origin since the last layers update, then we don't want to push our scroll
    130  // request because we'll clobber that one, which is bad.
    131  bool scrollInProgress = APZCCallbackHelper::IsScrollInProgress(aFrame);
    132  if (!scrollInProgress) {
    133    ScrollSnapTargetIds snapTargetIds = aRequest.GetLastSnapTargetIds();
    134    aFrame->ScrollToCSSPixelsForApz(targetScrollPosition,
    135                                    std::move(snapTargetIds));
    136    geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
    137    aSuccessOut = true;
    138  }
    139  // Return the final scroll position after setting it so that anything that
    140  // relies on it can have an accurate value. Note that even if we set it above
    141  // re-querying it is a good idea because it may have gotten clamped or
    142  // rounded.
    143  return geckoScrollPosition;
    144 }
    145 
    146 /**
    147 * Scroll the scroll frame associated with |aContent| to the scroll position
    148 * requested in |aRequest|.
    149 *
    150 * Any difference between the requested and actual scroll positions is used to
    151 * update the callback-transform stored on the content, and return a new
    152 * display port.
    153 */
    154 static DisplayPortMargins ScrollFrame(nsIContent* aContent,
    155                                      const RepaintRequest& aRequest) {
    156  // Scroll the window to the desired spot
    157  ScrollContainerFrame* sf =
    158      nsLayoutUtils::FindScrollContainerFrameFor(aRequest.GetScrollId());
    159  if (sf) {
    160    sf->ResetScrollInfoIfNeeded(aRequest.GetScrollGeneration(),
    161                                aRequest.GetScrollGenerationOnApz(),
    162                                aRequest.GetScrollAnimationType(),
    163                                ScrollContainerFrame::InScrollingGesture(
    164                                    aRequest.IsInScrollingGesture()));
    165    sf->SetScrollableByAPZ(!aRequest.IsScrollInfoLayer());
    166    if (sf->IsRootScrollFrameOfDocument()) {
    167      if (!APZCCallbackHelper::IsScrollInProgress(sf)) {
    168        APZCCH_LOG("Setting VV offset to %s\n",
    169                   ToString(aRequest.GetVisualScrollOffset()).c_str());
    170        if (sf->SetVisualViewportOffset(
    171                CSSPoint::ToAppUnits(aRequest.GetVisualScrollOffset()),
    172                /* aRepaint = */ false)) {
    173          // sf can't be destroyed if SetVisualViewportOffset returned true.
    174          sf->MarkEverScrolled();
    175        }
    176      }
    177    }
    178  }
    179  // sf might have been destroyed by the call to SetVisualViewportOffset, so
    180  // re-get it.
    181  sf = nsLayoutUtils::FindScrollContainerFrameFor(aRequest.GetScrollId());
    182  bool scrollUpdated = false;
    183  auto displayPortMargins = DisplayPortMargins::ForScrollContainerFrame(
    184      sf, aRequest.GetDisplayPortMargins());
    185  CSSPoint apzScrollOffset = aRequest.GetVisualScrollOffset();
    186  CSSPoint actualScrollOffset = ScrollFrameTo(sf, aRequest, scrollUpdated);
    187  CSSPoint scrollDelta = apzScrollOffset - actualScrollOffset;
    188 
    189  if (scrollUpdated) {
    190    if (aRequest.IsScrollInfoLayer()) {
    191      // In cases where the APZ scroll offset is different from the content
    192      // scroll offset, we want to interpret the margins as relative to the APZ
    193      // scroll offset except when the frame is not scrollable by APZ.
    194      // Therefore, if the layer is a scroll info layer, we leave the margins
    195      // as-is and they will be interpreted as relative to the content scroll
    196      // offset.
    197      if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
    198        frame->SchedulePaint();
    199      }
    200    } else {
    201      // Correct the display port due to the difference between the requested
    202      // and actual scroll offsets.
    203      displayPortMargins =
    204          DisplayPortMargins::FromAPZ(aRequest.GetDisplayPortMargins(),
    205                                      apzScrollOffset, actualScrollOffset);
    206    }
    207  } else if (aRequest.IsRootContent() &&
    208             apzScrollOffset != aRequest.GetLayoutScrollOffset()) {
    209    // APZ uses the visual viewport's offset to calculate where to place the
    210    // display port, so the display port is misplaced when a pinch zoom occurs.
    211    //
    212    // We need to force a display port adjustment in the following paint to
    213    // account for a difference between the requested and actual scroll
    214    // offsets in repaints requested by
    215    // AsyncPanZoomController::NotifyLayersUpdated.
    216    displayPortMargins = DisplayPortMargins::FromAPZ(
    217        aRequest.GetDisplayPortMargins(), apzScrollOffset, actualScrollOffset);
    218  } else {
    219    // For whatever reason we couldn't update the scroll offset on the scroll
    220    // frame, which means the data APZ used for its displayport calculation is
    221    // stale. Fall back to a sane default behaviour. Note that we don't
    222    // tile-align the recentered displayport because tile-alignment depends on
    223    // the scroll position, and the scroll position here is out of our control.
    224    // See bug 966507 comment 21 for a more detailed explanation.
    225    displayPortMargins = DisplayPortMargins::ForScrollContainerFrame(
    226        sf, RecenterDisplayPort(aRequest.GetDisplayPortMargins()));
    227  }
    228 
    229  // APZ transforms inputs assuming we applied the exact scroll offset it
    230  // requested (|apzScrollOffset|). Since we may not have, record the difference
    231  // between what APZ asked for and what we actually applied, and apply it to
    232  // input events to compensate.
    233  // Note that if the main-thread had a change in its scroll position, we don't
    234  // want to record that difference here, because it can be large and throw off
    235  // input events by a large amount. It is also going to be transient, because
    236  // any main-thread scroll position change will be synced to APZ and we will
    237  // get another repaint request when APZ confirms. In the interval while this
    238  // is happening we can just leave the callback transform as it was.
    239  bool mainThreadScrollChanged =
    240      sf && sf->CurrentScrollGeneration() != aRequest.GetScrollGeneration() &&
    241      nsLayoutUtils::CanScrollOriginClobberApz(sf->LastScrollOrigin());
    242  if (aContent && !mainThreadScrollChanged) {
    243    aContent->SetProperty(nsGkAtoms::apzCallbackTransform,
    244                          new CSSPoint(scrollDelta),
    245                          nsINode::DeleteProperty<CSSPoint>);
    246  }
    247 
    248  return displayPortMargins;
    249 }
    250 
    251 static void SetDisplayPortMargins(PresShell* aPresShell, nsIContent* aContent,
    252                                  const DisplayPortMargins& aDisplayPortMargins,
    253                                  CSSSize aDisplayPortBase) {
    254  if (!aContent) {
    255    return;
    256  }
    257 
    258  bool hadDisplayPort = DisplayPortUtils::HasDisplayPort(aContent);
    259  if (MOZ_LOG_TEST(sDisplayportLog, LogLevel::Debug)) {
    260    if (!hadDisplayPort) {
    261      mozilla::layers::ScrollableLayerGuid::ViewID viewID =
    262          mozilla::layers::ScrollableLayerGuid::NULL_SCROLL_ID;
    263      nsLayoutUtils::FindIDFor(aContent, &viewID);
    264      MOZ_LOG(
    265          sDisplayportLog, LogLevel::Debug,
    266          ("APZCCH installing displayport margins %s on scrollId=%" PRIu64 "\n",
    267           ToString(aDisplayPortMargins).c_str(), viewID));
    268    }
    269  }
    270  DisplayPortUtils::SetDisplayPortMargins(
    271      aContent, aPresShell, aDisplayPortMargins,
    272      hadDisplayPort ? DisplayPortUtils::ClearMinimalDisplayPortProperty::No
    273                     : DisplayPortUtils::ClearMinimalDisplayPortProperty::Yes,
    274      0);
    275  if (!hadDisplayPort) {
    276    DisplayPortUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
    277        aContent->GetPrimaryFrame());
    278  }
    279 
    280  nsRect base(0, 0, aDisplayPortBase.width * AppUnitsPerCSSPixel(),
    281              aDisplayPortBase.height * AppUnitsPerCSSPixel());
    282  DisplayPortUtils::SetDisplayPortBaseIfNotSet(aContent, base);
    283 }
    284 
    285 static void SetPaintRequestTime(nsIContent* aContent,
    286                                const TimeStamp& aPaintRequestTime) {
    287  aContent->SetProperty(nsGkAtoms::paintRequestTime,
    288                        new TimeStamp(aPaintRequestTime),
    289                        nsINode::DeleteProperty<TimeStamp>);
    290 }
    291 
    292 void APZCCallbackHelper::NotifyLayerTransforms(
    293    const nsTArray<MatrixMessage>& aTransforms) {
    294  MOZ_ASSERT(NS_IsMainThread());
    295  for (const MatrixMessage& msg : aTransforms) {
    296    BrowserParent* parent =
    297        BrowserParent::GetBrowserParentFromLayersId(msg.GetLayersId());
    298    if (parent) {
    299      parent->SetChildToParentConversionMatrix(
    300          ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
    301              msg.GetMatrix(),
    302              PixelCastJustification::ContentProcessIsLayerInUiProcess),
    303          msg.GetTopLevelViewportVisibleRectInBrowserCoords());
    304    }
    305  }
    306 }
    307 
    308 void APZCCallbackHelper::UpdateRootFrame(const RepaintRequest& aRequest) {
    309  if (aRequest.GetScrollId() == ScrollableLayerGuid::NULL_SCROLL_ID) {
    310    return;
    311  }
    312  RefPtr<nsIContent> content =
    313      nsLayoutUtils::FindContentFor(aRequest.GetScrollId());
    314  if (!content) {
    315    return;
    316  }
    317 
    318  RefPtr<PresShell> presShell = GetPresShell(content);
    319  if (!presShell || aRequest.GetPresShellId() != presShell->GetPresShellId()) {
    320    return;
    321  }
    322 
    323  APZCCH_LOG("Handling request %s\n", ToString(aRequest).c_str());
    324  if (nsLayoutUtils::AllowZoomingForDocument(presShell->GetDocument()) &&
    325      aRequest.GetAsyncZoom().scale != 1.0) {
    326    // If zooming is disabled then we don't really want to let APZ fiddle
    327    // with these things. In theory setting the resolution here should be a
    328    // no-op, but setting the visual viewport size is bad because it can cause a
    329    // stale value to be returned by window.innerWidth/innerHeight (see bug
    330    // 1187792).
    331 
    332    float presShellResolution = presShell->GetResolution();
    333 
    334    // If the pres shell resolution has changed on the content side side
    335    // the time this repaint request was fired, consider this request out of
    336    // date and drop it; setting a zoom based on the out-of-date resolution can
    337    // have the effect of getting us stuck with the stale resolution.
    338    // One might think that if the last ResolutionChangeOrigin was apz then the
    339    // pres shell resolutions should match but
    340    // that is not the case. We can get multiple repaint requests that has the
    341    // same pres shell resolution (because apz didn't receive a content layers
    342    // update inbetween) if the first has async zoom we apply that and chance
    343    // the content pres shell resolution and thus when handling the second
    344    // repaint request the pres shell resolution won't match. So that's why we
    345    // also check if the last resolution change origin was apz (aka 'us').
    346    if (!FuzzyEqualsMultiplicative(presShellResolution,
    347                                   aRequest.GetPresShellResolution()) &&
    348        presShell->GetLastResolutionChangeOrigin() !=
    349            ResolutionChangeOrigin::Apz) {
    350      return;
    351    }
    352 
    353    // The pres shell resolution is updated by the the async zoom since the
    354    // last paint.
    355    // We want to calculate the new presshell resolution as
    356    // |aRequest.GetPresShellResolution() * aRequest.GetAsyncZoom()| but that
    357    // calculation can lead to small inaccuracies due to limited floating point
    358    // precision. Specifically,
    359    // clang-format off
    360    // asyncZoom = zoom / layerPixelsPerCSSPixel
    361    //           = zoom / (devPixelsPerCSSPixel * cumulativeResolution)
    362    // clang-format on
    363    // Since this is a root frame we generally do not allow css transforms to
    364    // scale it, so it is very likely that cumulativeResolution ==
    365    // presShellResoluion. So
    366    // clang-format off
    367    // newPresShellResoluion = presShellResoluion * asyncZoom
    368    // = presShellResoluion * zoom / (devPixelsPerCSSPixel * presShellResoluion)
    369    // = zoom / devPixelsPerCSSPixel
    370    // clang-format on
    371    // However, we want to keep the calculation general and so we do not assume
    372    // presShellResoluion == cumulativeResolution, but rather factor those
    373    // values out so they cancel and the floating point division has a very high
    374    // probability of being exactly 1.
    375    presShellResolution =
    376        (aRequest.GetPresShellResolution() /
    377         aRequest.GetCumulativeResolution().scale) *
    378        (aRequest.GetZoom() / aRequest.GetDevPixelsPerCSSPixel()).scale;
    379    presShell->SetResolutionAndScaleTo(presShellResolution,
    380                                       ResolutionChangeOrigin::Apz);
    381 
    382    // Changing the resolution will trigger a reflow which will cause the
    383    // main-thread scroll position to be realigned in layer pixels. This
    384    // (subpixel) scroll mutation can trigger a scroll update to APZ which
    385    // is undesirable. Instead of having that happen as part of the post-reflow
    386    // code, we force it to happen here with ScrollOrigin::Apz so that it
    387    // doesn't trigger a scroll update to APZ.
    388    ScrollContainerFrame* sf =
    389        nsLayoutUtils::FindScrollContainerFrameFor(aRequest.GetScrollId());
    390    CSSPoint currentScrollPosition =
    391        CSSPoint::FromAppUnits(sf->GetScrollPosition());
    392    ScrollSnapTargetIds snapTargetIds = aRequest.GetLastSnapTargetIds();
    393    sf->ScrollToCSSPixelsForApz(currentScrollPosition,
    394                                std::move(snapTargetIds));
    395  }
    396 
    397  // Do this as late as possible since scrolling can flush layout. It also
    398  // adjusts the display port margins, so do it before we set those.
    399  DisplayPortMargins displayPortMargins = ScrollFrame(content, aRequest);
    400 
    401  SetDisplayPortMargins(presShell, content, displayPortMargins,
    402                        aRequest.CalculateCompositedSizeInCssPixels());
    403  SetPaintRequestTime(content, aRequest.GetPaintRequestTime());
    404 }
    405 
    406 void APZCCallbackHelper::UpdateSubFrame(const RepaintRequest& aRequest) {
    407  if (aRequest.GetScrollId() == ScrollableLayerGuid::NULL_SCROLL_ID) {
    408    return;
    409  }
    410  RefPtr<nsIContent> content =
    411      nsLayoutUtils::FindContentFor(aRequest.GetScrollId());
    412  if (!content) {
    413    return;
    414  }
    415 
    416  // We don't currently support zooming for subframes, so nothing extra
    417  // needs to be done beyond the tasks common to this and UpdateRootFrame.
    418  DisplayPortMargins displayPortMargins = ScrollFrame(content, aRequest);
    419  if (RefPtr<PresShell> presShell = GetPresShell(content)) {
    420    SetDisplayPortMargins(presShell, content, displayPortMargins,
    421                          aRequest.CalculateCompositedSizeInCssPixels());
    422  }
    423  SetPaintRequestTime(content, aRequest.GetPaintRequestTime());
    424 }
    425 
    426 bool APZCCallbackHelper::GetOrCreateScrollIdentifiers(
    427    nsIContent* aContent, uint32_t* aPresShellIdOut,
    428    ScrollableLayerGuid::ViewID* aViewIdOut) {
    429  if (!aContent) {
    430    return false;
    431  }
    432  *aViewIdOut = nsLayoutUtils::FindOrCreateIDFor(aContent);
    433  if (PresShell* presShell = GetPresShell(aContent)) {
    434    *aPresShellIdOut = presShell->GetPresShellId();
    435    return true;
    436  }
    437  return false;
    438 }
    439 
    440 void APZCCallbackHelper::InitializeRootDisplayport(PresShell* aPresShell) {
    441  // Create a view-id and set a zero-margin displayport for the root element
    442  // of the root document in the chrome process. This ensures that the scroll
    443  // frame for this element gets an APZC, which in turn ensures that all content
    444  // in the chrome processes is covered by an APZC.
    445  // The displayport is zero-margin because this element is generally not
    446  // actually scrollable (if it is, APZC will set proper margins when it's
    447  // scrolled).
    448  if (!aPresShell) {
    449    return;
    450  }
    451 
    452  MOZ_ASSERT(aPresShell->GetDocument());
    453  nsIContent* content = aPresShell->GetDocument()->GetDocumentElement();
    454  if (!content) {
    455    return;
    456  }
    457 
    458  uint32_t presShellId;
    459  ScrollableLayerGuid::ViewID viewId;
    460  if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(content, &presShellId,
    461                                                       &viewId)) {
    462    MOZ_LOG(
    463        sDisplayportLog, LogLevel::Debug,
    464        ("Initializing root displayport on scrollId=%" PRIu64 "\n", viewId));
    465    Maybe<nsRect> baseRect =
    466        DisplayPortUtils::GetRootDisplayportBase(aPresShell);
    467    if (baseRect) {
    468      DisplayPortUtils::SetDisplayPortBaseIfNotSet(content, *baseRect);
    469    }
    470 
    471    DisplayPortUtils::SetDisplayPortMargins(
    472        content, aPresShell, DisplayPortMargins::Empty(content),
    473        DisplayPortUtils::ClearMinimalDisplayPortProperty::Yes, 0);
    474    DisplayPortUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
    475        content->GetPrimaryFrame());
    476  }
    477 }
    478 
    479 void APZCCallbackHelper::InitializeRootDisplayport(nsIFrame* aFrame) {
    480  MOZ_ASSERT(XRE_IsParentProcess(),
    481             "The root displayport should be only used in the parent process");
    482  MOZ_ASSERT(aFrame && aFrame->IsMenuPopupFrame(),
    483             "This function is only available for popup frames.");
    484 
    485  nsIContent* content = aFrame->GetContent();
    486  if (!content) {
    487    return;
    488  }
    489 
    490  uint32_t presShellId;
    491  ScrollableLayerGuid::ViewID viewId;
    492  if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(content, &presShellId,
    493                                                       &viewId)) {
    494    MOZ_LOG(sDisplayportLog, LogLevel::Debug,
    495            ("Initializing root displayport on scrollId=%" PRIu64, viewId));
    496    nsRect baseRect = DisplayPortUtils::GetDisplayportBase(aFrame);
    497    DisplayPortUtils::SetDisplayPortBaseIfNotSet(content, baseRect);
    498 
    499    DisplayPortUtils::SetDisplayPortMargins(
    500        content, aFrame->PresShell(), DisplayPortMargins::Empty(content),
    501        DisplayPortUtils::ClearMinimalDisplayPortProperty::Yes, 0);
    502 
    503    // Unlike normal root displayport, we don't need to walk up the frame tree
    504    // to set zero margin displayport for ancestor frames since this popup frame
    505    // is the root frame of the popuped window.
    506  }
    507 }
    508 
    509 nsPresContext* APZCCallbackHelper::GetPresContextForContent(
    510    nsIContent* aContent) {
    511  dom::Document* doc = aContent->GetComposedDoc();
    512  if (!doc) {
    513    return nullptr;
    514  }
    515  PresShell* presShell = doc->GetPresShell();
    516  if (!presShell) {
    517    return nullptr;
    518  }
    519  return presShell->GetPresContext();
    520 }
    521 
    522 PresShell* APZCCallbackHelper::GetRootContentDocumentPresShellForContent(
    523    nsIContent* aContent) {
    524  nsPresContext* context = GetPresContextForContent(aContent);
    525  if (!context) {
    526    return nullptr;
    527  }
    528  context = context->GetInProcessRootContentDocumentPresContext();
    529  if (!context) {
    530    return nullptr;
    531  }
    532  return context->PresShell();
    533 }
    534 
    535 nsEventStatus APZCCallbackHelper::DispatchWidgetEvent(WidgetGUIEvent& aEvent) {
    536  if (aEvent.mWidget) {
    537    return aEvent.mWidget->DispatchEvent(&aEvent);
    538  }
    539  return nsEventStatus_eConsumeNoDefault;
    540 }
    541 
    542 nsEventStatus APZCCallbackHelper::DispatchSynthesizedMouseEvent(
    543    EventMessage aMsg, const LayoutDevicePoint& aRefPoint, uint32_t aPointerId,
    544    Modifiers aModifiers, int32_t aClickCount,
    545    PrecedingPointerDown aPrecedingPointerDownState, nsIWidget* aWidget,
    546    SynthesizeForTests aSynthesizeForTests) {
    547  MOZ_ASSERT(aMsg == eMouseMove || aMsg == eMouseDown || aMsg == eMouseUp ||
    548             aMsg == eMouseLongTap);
    549 
    550  WidgetMouseEvent event(true, aMsg, aWidget, WidgetMouseEvent::eReal);
    551  event.mFlags.mIsSynthesizedForTests = static_cast<bool>(aSynthesizeForTests);
    552  event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y);
    553  event.mButton = MouseButton::ePrimary;
    554  event.mButtons = aMsg == eMouseDown ? MouseButtonsFlag::ePrimaryFlag
    555                                      : MouseButtonsFlag::eNoButtons;
    556  event.mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH;
    557  if (aMsg == eMouseLongTap) {
    558    event.mFlags.mOnlyChromeDispatch = true;
    559  }
    560  // If the preceding `pointerdown` was canceled by content, we should not
    561  // dispatch the compatibility mouse events into the content, but they are
    562  // required to dispatch `click`, `dblclick` and `auxclick` events by
    563  // EventStateManager.  Therefore, we need to dispatch them only to chrome.
    564  else if (aPrecedingPointerDownState ==
    565           PrecedingPointerDown::ConsumedByContent) {
    566    event.PreventDefault(false);
    567    event.mFlags.mOnlyChromeDispatch = true;
    568  }
    569  if (aMsg != eMouseMove) {
    570    event.mClickCount = aClickCount;
    571  }
    572  event.mModifiers = aModifiers;
    573  event.pointerId = aPointerId;
    574  // Real touch events will generate corresponding pointer events. We set
    575  // convertToPointer to false to prevent the synthesized mouse events generate
    576  // pointer events again.
    577  event.convertToPointer = false;
    578  return DispatchWidgetEvent(event);
    579 }
    580 
    581 PreventDefaultResult APZCCallbackHelper::DispatchSynthesizedContextmenuEvent(
    582    const LayoutDevicePoint& aRefPoint, uint32_t aPointerId,
    583    Modifiers aModifiers, nsIWidget* aWidget,
    584    SynthesizeForTests aSynthesizeForTests) {
    585  WidgetPointerEvent event(true, eContextMenu, aWidget);
    586  event.mFlags.mIsSynthesizedForTests = static_cast<bool>(aSynthesizeForTests);
    587  event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y);
    588  event.mButton = MouseButton::ePrimary;
    589  event.mButtons = MouseButtonsFlag::ePrimaryFlag;
    590  event.mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH;
    591  event.mModifiers = aModifiers;
    592  event.pointerId = aPointerId;
    593  // contextmenu events will never generate pointer events.
    594  event.convertToPointer = false;
    595  nsEventStatus result = DispatchWidgetEvent(event);
    596  if (result != nsEventStatus_eConsumeNoDefault) {
    597    return PreventDefaultResult::No;
    598  }
    599 
    600  return event.mFlags.mDefaultPreventedByContent
    601             ? PreventDefaultResult::ByContent
    602             : PreventDefaultResult::ByChrome;
    603 }
    604 
    605 void APZCCallbackHelper::FireSingleTapEvent(
    606    const LayoutDevicePoint& aPoint, uint32_t aPointerId, Modifiers aModifiers,
    607    int32_t aClickCount, PrecedingPointerDown aPrecedingPointerDownState,
    608    nsIWidget* aWidget, SynthesizeForTests aSynthesizeForTests) {
    609  if (aWidget->Destroyed()) {
    610    return;
    611  }
    612  APZCCH_LOG("Dispatching single-tap component events to %s\n",
    613             ToString(aPoint).c_str());
    614  DispatchSynthesizedMouseEvent(eMouseMove, aPoint, aPointerId, aModifiers,
    615                                aClickCount, aPrecedingPointerDownState,
    616                                aWidget, aSynthesizeForTests);
    617  DispatchSynthesizedMouseEvent(eMouseDown, aPoint, aPointerId, aModifiers,
    618                                aClickCount, aPrecedingPointerDownState,
    619                                aWidget, aSynthesizeForTests);
    620  DispatchSynthesizedMouseEvent(eMouseUp, aPoint, aPointerId, aModifiers,
    621                                aClickCount, aPrecedingPointerDownState,
    622                                aWidget, aSynthesizeForTests);
    623 }
    624 
    625 static dom::Element* GetDisplayportElementFor(
    626    ScrollContainerFrame* aScrollContainerFrame) {
    627  if (!aScrollContainerFrame) {
    628    return nullptr;
    629  }
    630  nsIFrame* scrolledFrame = aScrollContainerFrame->GetScrolledFrame();
    631  if (!scrolledFrame) {
    632    return nullptr;
    633  }
    634  // |scrolledFrame| should at this point be the root content frame of the
    635  // nearest ancestor scrollable frame. The element corresponding to this
    636  // frame should be the one with the displayport set on it, so find that
    637  // element and return it.
    638  nsIContent* content = scrolledFrame->GetContent();
    639  MOZ_ASSERT(content->IsElement());  // roc says this must be true
    640  return content->AsElement();
    641 }
    642 
    643 static dom::Element* GetRootElementFor(nsIWidget* aWidget) {
    644  // This returns the root element that ChromeProcessController sets the
    645  // displayport on during initialization.
    646  auto* frame = aWidget->GetFrame();
    647  if (!frame) {
    648    return nullptr;
    649  }
    650  if (frame->IsMenuPopupFrame()) {
    651    return frame->GetContent()->AsElement();
    652  }
    653  return frame->PresContext()->Document()->GetDocumentElement();
    654 }
    655 
    656 namespace {
    657 
    658 using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
    659 
    660 ScrollContainerFrame* GetScrollContainerFor(nsIFrame* aTarget,
    661                                            const nsIFrame* aRootFrame) {
    662  if (!aTarget) {
    663    return !aRootFrame->IsMenuPopupFrame()
    664               ? aRootFrame->PresShell()->GetRootScrollContainerFrame()
    665               : nullptr;
    666  }
    667 
    668  return nsLayoutUtils::GetAsyncScrollableAncestorFrame(aTarget);
    669 }
    670 
    671 // Determine the scrollable target frame for the given point and add it to
    672 // the target list. If the frame doesn't have a displayport, set one.
    673 // Return whether or not the frame had a displayport that has already been
    674 // painted (in this case, the caller can send the SetTargetAPZC notification
    675 // right away, rather than waiting for a transaction to propagate the
    676 // displayport to APZ first).
    677 static bool PrepareForSetTargetAPZCNotification(
    678    nsIWidget* aWidget, const LayersId& aLayersId, nsIFrame* aRootFrame,
    679    const LayoutDeviceIntPoint& aRefPoint,
    680    nsTArray<ScrollableLayerGuid>* aTargets) {
    681  ScrollableLayerGuid guid(aLayersId, 0, ScrollableLayerGuid::NULL_SCROLL_ID);
    682  RelativeTo relativeTo{aRootFrame, ViewportType::Visual};
    683  nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
    684      aWidget, aRefPoint, relativeTo);
    685  nsIFrame* target = nsLayoutUtils::GetFrameForPoint(relativeTo, point);
    686  ScrollContainerFrame* scrollAncestor =
    687      GetScrollContainerFor(target, aRootFrame);
    688 
    689  // Assuming that if there's no scrollAncestor, there's already a displayPort.
    690  nsCOMPtr<dom::Element> dpElement =
    691      scrollAncestor ? GetDisplayportElementFor(scrollAncestor)
    692                     : GetRootElementFor(aWidget);
    693 
    694  if (MOZ_LOG_TEST(sApzHlpLog, LogLevel::Debug)) {
    695    nsAutoString dpElementDesc;
    696    if (dpElement) {
    697      dpElement->Describe(dpElementDesc);
    698    }
    699    APZCCH_LOG("For event at %s found scrollable element %p (%s)\n",
    700               ToString(aRefPoint).c_str(), dpElement.get(),
    701               NS_LossyConvertUTF16toASCII(dpElementDesc).get());
    702  }
    703 
    704  bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
    705      dpElement, &(guid.mPresShellId), &(guid.mScrollId));
    706  aTargets->AppendElement(guid);
    707 
    708  if (!guidIsValid) {
    709    return false;
    710  }
    711 
    712  // Stop suppressing displayport while the page is still loading.
    713  if (MOZ_UNLIKELY(aRootFrame->PresShell()->IsDocumentLoading())) {
    714    aRootFrame->PresShell()->SuppressDisplayport(false);
    715  }
    716 
    717  if (DisplayPortUtils::HasNonMinimalNonZeroDisplayPort(dpElement)) {
    718    // If the element has a displayport but it hasn't been painted yet,
    719    // we want the caller to wait for the paint to happen, but we don't
    720    // need to set the displayport here since it's already been set.
    721    return !DisplayPortUtils::HasPaintedDisplayPort(dpElement);
    722  }
    723 
    724  if (!scrollAncestor) {
    725    // This can happen if the document element gets swapped out after
    726    // ChromeProcessController runs InitializeRootDisplayport. In this case
    727    // let's try to set a displayport again and bail out on this operation.
    728    APZCCH_LOG("Widget %p's document element %p didn't have a displayport\n",
    729               aWidget, dpElement.get());
    730    if (aRootFrame->IsMenuPopupFrame()) {
    731      // XXX It's unclear whether this swapped root element case can happen
    732      // in popup window.
    733      APZCCallbackHelper::InitializeRootDisplayport(aRootFrame);
    734    } else {
    735      APZCCallbackHelper::InitializeRootDisplayport(aRootFrame->PresShell());
    736    }
    737    return false;
    738  }
    739 
    740  APZCCH_LOG("%p didn't have a displayport, so setting one...\n",
    741             dpElement.get());
    742  MOZ_LOG(sDisplayportLog, LogLevel::Debug,
    743          ("Activating displayport on scrollId=%" PRIu64 " for SetTargetAPZC\n",
    744           guid.mScrollId));
    745  bool activated = DisplayPortUtils::CalculateAndSetDisplayPortMargins(
    746      scrollAncestor, DisplayPortUtils::RepaintMode::Repaint);
    747  if (!activated) {
    748    return false;
    749  }
    750 
    751  DisplayPortUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
    752      scrollAncestor);
    753 
    754  return !DisplayPortUtils::HasPaintedDisplayPort(dpElement);
    755 }
    756 
    757 static void SendLayersDependentApzcTargetConfirmation(
    758    nsIWidget* aWidget, uint64_t aInputBlockId,
    759    nsTArray<ScrollableLayerGuid>&& aTargets) {
    760  WindowRenderer* renderer = aWidget->GetWindowRenderer();
    761  if (!renderer) {
    762    return;
    763  }
    764 
    765  if (WebRenderLayerManager* wrlm = renderer->AsWebRender()) {
    766    if (WebRenderBridgeChild* wrbc = wrlm->WrBridge()) {
    767      wrbc->SendSetConfirmedTargetAPZC(aInputBlockId, aTargets);
    768    }
    769    return;
    770  }
    771 }
    772 
    773 }  // namespace
    774 
    775 DisplayportSetListener::DisplayportSetListener(
    776    nsIWidget* aWidget, nsPresContext* aPresContext,
    777    const uint64_t& aInputBlockId, nsTArray<ScrollableLayerGuid>&& aTargets)
    778    : ManagedPostRefreshObserver(aPresContext),
    779      mWidget(aWidget),
    780      mInputBlockId(aInputBlockId),
    781      mTargets(std::move(aTargets)) {
    782  MOZ_ASSERT(!mAction, "Setting Action twice");
    783  mAction = [instance = MOZ_KnownLive(this)](bool aWasCanceled) {
    784    instance->OnPostRefresh();
    785    return Unregister::Yes;
    786  };
    787 }
    788 
    789 DisplayportSetListener::~DisplayportSetListener() = default;
    790 
    791 void DisplayportSetListener::Register() {
    792  APZCCH_LOG("DisplayportSetListener::Register\n");
    793  mPresContext->RegisterManagedPostRefreshObserver(this);
    794 }
    795 
    796 void DisplayportSetListener::OnPostRefresh() {
    797  APZCCH_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n",
    798             mInputBlockId);
    799  SendLayersDependentApzcTargetConfirmation(mWidget, mInputBlockId,
    800                                            std::move(mTargets));
    801 }
    802 
    803 nsIFrame* GetRootFrameForWidget(const nsIWidget* aWidget,
    804                                const PresShell* aPresShell) {
    805  if (auto* popup = aWidget->GetPopupFrame()) {
    806    // In the case where the widget is popup window and uses APZ, the widget
    807    // frame (i.e. menu popup frame) is the reference frame used for building
    808    // the display list for hit-testing inside the popup.
    809    return popup;
    810  }
    811 
    812  return aPresShell->GetRootFrame();
    813 }
    814 
    815 already_AddRefed<DisplayportSetListener>
    816 APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
    817                                                  dom::Document* aDocument,
    818                                                  const WidgetGUIEvent& aEvent,
    819                                                  const LayersId& aLayersId,
    820                                                  uint64_t aInputBlockId) {
    821  if (!aWidget || !aDocument) {
    822    return nullptr;
    823  }
    824  if (aInputBlockId == sLastTargetAPZCNotificationInputBlock) {
    825    // We have already confirmed the target APZC for a previous event of this
    826    // input block. If we activated a scroll frame for this input block,
    827    // sending another target APZC confirmation would be harmful, as it might
    828    // race the original confirmation (which needs to go through a layers
    829    // transaction).
    830    APZCCH_LOG("Not resending target APZC confirmation for input block %" PRIu64
    831               "\n",
    832               aInputBlockId);
    833    return nullptr;
    834  }
    835  sLastTargetAPZCNotificationInputBlock = aInputBlockId;
    836  PresShell* presShell = aDocument->GetPresShell();
    837  if (!presShell) {
    838    return nullptr;
    839  }
    840 
    841  nsIFrame* rootFrame = GetRootFrameForWidget(aWidget, presShell);
    842  if (!rootFrame) {
    843    return nullptr;
    844  }
    845 
    846  bool waitForRefresh = false;
    847  nsTArray<ScrollableLayerGuid> targets;
    848 
    849  if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
    850    for (size_t i = 0; i < touchEvent->mTouches.Length(); i++) {
    851      waitForRefresh |= PrepareForSetTargetAPZCNotification(
    852          aWidget, aLayersId, rootFrame, touchEvent->mTouches[i]->mRefPoint,
    853          &targets);
    854    }
    855  } else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) {
    856    waitForRefresh = PrepareForSetTargetAPZCNotification(
    857        aWidget, aLayersId, rootFrame, wheelEvent->mRefPoint, &targets);
    858  } else if (const WidgetMouseEvent* mouseEvent = aEvent.AsMouseEvent()) {
    859    waitForRefresh = PrepareForSetTargetAPZCNotification(
    860        aWidget, aLayersId, rootFrame, mouseEvent->mRefPoint, &targets);
    861  }
    862  // TODO: Do other types of events need to be handled?
    863 
    864  if (!targets.IsEmpty()) {
    865    if (waitForRefresh) {
    866      APZCCH_LOG(
    867          "At least one target got a new displayport, need to wait for "
    868          "refresh\n");
    869      return MakeAndAddRef<DisplayportSetListener>(
    870          aWidget, presShell->GetPresContext(), aInputBlockId,
    871          std::move(targets));
    872    }
    873    APZCCH_LOG("Sending target APZCs for input block %" PRIu64 "\n",
    874               aInputBlockId);
    875    aWidget->SetConfirmedTargetAPZC(aInputBlockId, targets);
    876  }
    877 
    878  return nullptr;
    879 }
    880 
    881 void APZCCallbackHelper::NotifyMozMouseScrollEvent(
    882    const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent) {
    883  nsCOMPtr<nsIContent> targetContent = nsLayoutUtils::FindContentFor(aScrollId);
    884  if (!targetContent) {
    885    return;
    886  }
    887  RefPtr<dom::Document> ownerDoc = targetContent->OwnerDoc();
    888  if (!ownerDoc) {
    889    return;
    890  }
    891 
    892  nsContentUtils::DispatchEventOnlyToChrome(ownerDoc, targetContent, aEvent,
    893                                            CanBubble::eYes, Cancelable::eYes);
    894 }
    895 
    896 void APZCCallbackHelper::NotifyFlushComplete(PresShell* aPresShell) {
    897  MOZ_ASSERT(NS_IsMainThread());
    898  // In some cases, flushing the APZ state to the main thread doesn't actually
    899  // trigger a flush and repaint (this is an intentional optimization - the
    900  // stuff visible to the user is still correct). However, reftests update their
    901  // snapshot based on invalidation events that are emitted during paints,
    902  // so we ensure that we kick off a paint when an APZ flush is done. Note that
    903  // only chrome/testing code can trigger this behaviour.
    904  if (aPresShell && aPresShell->GetRootFrame()) {
    905    aPresShell->GetRootFrame()->SchedulePaint(nsIFrame::PAINT_DEFAULT, false);
    906  }
    907 
    908  nsCOMPtr<nsIObserverService> observerService =
    909      mozilla::services::GetObserverService();
    910  MOZ_ASSERT(observerService);
    911  observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr);
    912 }
    913 
    914 /* static */
    915 bool APZCCallbackHelper::IsScrollInProgress(ScrollContainerFrame* aFrame) {
    916  using AnimationState = ScrollContainerFrame::AnimationState;
    917 
    918  return aFrame->ScrollAnimationState().contains(AnimationState::MainThread) ||
    919         nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin());
    920 }
    921 
    922 /* static */
    923 void APZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(
    924    uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId,
    925    ScrollDirection aDirection) {
    926  MOZ_ASSERT(NS_IsMainThread());
    927  if (ScrollContainerFrame* scrollContainerFrame =
    928          nsLayoutUtils::FindScrollContainerFrameFor(aScrollId)) {
    929    scrollContainerFrame->AsyncScrollbarDragInitiated(aDragBlockId, aDirection);
    930  }
    931 }
    932 
    933 /* static */
    934 void APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(
    935    const ScrollableLayerGuid::ViewID& aScrollId) {
    936  MOZ_ASSERT(NS_IsMainThread());
    937  if (ScrollContainerFrame* scrollContainerFrame =
    938          nsLayoutUtils::FindScrollContainerFrameFor(aScrollId)) {
    939    scrollContainerFrame->AsyncScrollbarDragRejected();
    940  }
    941 }
    942 
    943 /* static */
    944 void APZCCallbackHelper::NotifyAsyncAutoscrollRejected(
    945    const ScrollableLayerGuid::ViewID& aScrollId) {
    946  MOZ_ASSERT(NS_IsMainThread());
    947  nsCOMPtr<nsIObserverService> observerService =
    948      mozilla::services::GetObserverService();
    949  MOZ_ASSERT(observerService);
    950 
    951  nsAutoString data;
    952  data.AppendInt(aScrollId);
    953  observerService->NotifyObservers(nullptr, "autoscroll-rejected-by-apz",
    954                                   data.get());
    955 }
    956 
    957 /* static */
    958 void APZCCallbackHelper::CancelAutoscroll(
    959    const ScrollableLayerGuid::ViewID& aScrollId) {
    960  MOZ_ASSERT(NS_IsMainThread());
    961  nsCOMPtr<nsIObserverService> observerService =
    962      mozilla::services::GetObserverService();
    963  MOZ_ASSERT(observerService);
    964 
    965  nsAutoString data;
    966  data.AppendInt(aScrollId);
    967  observerService->NotifyObservers(nullptr, "apz:cancel-autoscroll",
    968                                   data.get());
    969 }
    970 
    971 /* static */
    972 void APZCCallbackHelper::NotifyScaleGestureComplete(
    973    const nsCOMPtr<nsIWidget>& aWidget, float aScale) {
    974  MOZ_ASSERT(NS_IsMainThread());
    975  nsIFrame* frame = aWidget->GetFrame();
    976  if (!frame) {
    977    return;
    978  }
    979  dom::Document* doc = frame->PresShell()->GetDocument();
    980  MOZ_ASSERT(doc);
    981  nsPIDOMWindowInner* win = doc->GetInnerWindow();
    982  if (!win) {
    983    return;
    984  }
    985  dom::AutoJSAPI jsapi;
    986  if (!jsapi.Init(win)) {
    987    return;
    988  }
    989  JSContext* cx = jsapi.cx();
    990  JS::Rooted<JS::Value> detail(cx, JS::Float32Value(aScale));
    991  RefPtr<dom::CustomEvent> event = NS_NewDOMCustomEvent(doc, nullptr, nullptr);
    992  event->InitCustomEvent(cx, u"MozScaleGestureComplete"_ns,
    993                         /* CanBubble */ true,
    994                         /* Cancelable */ false, detail);
    995  event->SetTrusted(true);
    996  auto* dispatcher =
    997      new AsyncEventDispatcher(doc, event.forget(), ChromeOnlyDispatch::eYes);
    998  dispatcher->PostDOMEvent();
    999 }
   1000 
   1001 /* static */
   1002 void APZCCallbackHelper::NotifyPinchGesture(
   1003    PinchGestureInput::PinchGestureType aType,
   1004    const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange,
   1005    Modifiers aModifiers, const nsCOMPtr<nsIWidget>& aWidget) {
   1006  APZCCH_LOG("APZCCallbackHelper dispatching pinch gesture\n");
   1007  EventMessage msg;
   1008  switch (aType) {
   1009    case PinchGestureInput::PINCHGESTURE_START:
   1010      msg = eMagnifyGestureStart;
   1011      break;
   1012    case PinchGestureInput::PINCHGESTURE_SCALE:
   1013      msg = eMagnifyGestureUpdate;
   1014      break;
   1015    case PinchGestureInput::PINCHGESTURE_FINGERLIFTED:
   1016    case PinchGestureInput::PINCHGESTURE_END:
   1017      msg = eMagnifyGesture;
   1018      break;
   1019  }
   1020 
   1021  WidgetSimpleGestureEvent event(true, msg, aWidget.get());
   1022  // XXX mDelta for the eMagnifyGesture event is supposed to be the
   1023  // cumulative magnification over the entire gesture (per docs in
   1024  // SimpleGestureEvent.webidl) but currently APZ just sends us a zero
   1025  // aSpanChange for that event, so the mDelta is wrong. Nothing relies
   1026  // on that currently, but we might want to fix it at some point.
   1027  event.mDelta = aSpanChange;
   1028  event.mModifiers = aModifiers;
   1029  event.mRefPoint = RoundedToInt(aFocusPoint);
   1030 
   1031  DispatchWidgetEvent(event);
   1032 }
   1033 
   1034 }  // namespace layers
   1035 
   1036 std::ostream& operator<<(std::ostream& aOut,
   1037                         const PreventDefaultResult aPreventDefaultResult) {
   1038  switch (aPreventDefaultResult) {
   1039    case PreventDefaultResult::No:
   1040      aOut << "unhandled";
   1041      break;
   1042    case PreventDefaultResult::ByContent:
   1043      aOut << "handled-by-content";
   1044      break;
   1045    case PreventDefaultResult::ByChrome:
   1046      aOut << "handled-by-chrome";
   1047      break;
   1048  }
   1049  return aOut;
   1050 }
   1051 
   1052 }  // namespace mozilla