tor-browser

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

ChromeProcessController.cpp (13458B)


      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 "ChromeProcessController.h"
      8 
      9 #include "MainThreadUtils.h"  // for NS_IsMainThread()
     10 #include "base/task.h"
     11 #include "mozilla/PresShell.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "mozilla/dom/Element.h"
     14 #include "mozilla/dom/PointerEventHandler.h"
     15 #include "mozilla/layers/CompositorBridgeParent.h"
     16 #include "mozilla/layers/APZCCallbackHelper.h"
     17 #include "mozilla/layers/APZEventState.h"
     18 #include "mozilla/layers/APZThreadUtils.h"
     19 #include "mozilla/layers/IAPZCTreeManager.h"
     20 #include "mozilla/layers/InputAPZContext.h"
     21 #include "mozilla/layers/DoubleTapToZoom.h"
     22 #include "mozilla/layers/RepaintRequest.h"
     23 #include "nsIInterfaceRequestorUtils.h"
     24 #include "nsLayoutUtils.h"
     25 
     26 static mozilla::LazyLogModule sApzChromeLog("apz.cc.chrome");
     27 
     28 using namespace mozilla;
     29 using namespace mozilla::layers;
     30 using namespace mozilla::widget;
     31 
     32 ChromeProcessController::ChromeProcessController(
     33    nsIWidget* aWidget, APZEventState* aAPZEventState,
     34    IAPZCTreeManager* aAPZCTreeManager)
     35    : mWidget(aWidget),
     36      mAPZEventState(aAPZEventState),
     37      mAPZCTreeManager(aAPZCTreeManager),
     38      mUIThread(NS_GetCurrentThread()) {
     39  // Otherwise we're initializing mUIThread incorrectly.
     40  MOZ_ASSERT(NS_IsMainThread());
     41  MOZ_ASSERT(aAPZEventState);
     42  MOZ_ASSERT(aAPZCTreeManager);
     43 
     44  mUIThread->Dispatch(
     45      NewRunnableMethod("layers::ChromeProcessController::InitializeRoot", this,
     46                        &ChromeProcessController::InitializeRoot));
     47 }
     48 
     49 ChromeProcessController::~ChromeProcessController() = default;
     50 
     51 void ChromeProcessController::InitializeRoot() {
     52  nsIFrame* widgetFrame = GetWidgetFrame();
     53  if (widgetFrame && widgetFrame->IsMenuPopupFrame()) {
     54    // For popup window, the menu frame should be the root and have
     55    // the display port.
     56    APZCCallbackHelper::InitializeRootDisplayport(widgetFrame);
     57    return;
     58  }
     59 
     60  APZCCallbackHelper::InitializeRootDisplayport(GetPresShell());
     61 }
     62 
     63 void ChromeProcessController::NotifyLayerTransforms(
     64    nsTArray<MatrixMessage>&& aTransforms) {
     65  if (!mUIThread->IsOnCurrentThread()) {
     66    mUIThread->Dispatch(
     67        NewRunnableMethod<StoreCopyPassByRRef<nsTArray<MatrixMessage>>>(
     68            "layers::ChromeProcessController::NotifyLayerTransforms", this,
     69            &ChromeProcessController::NotifyLayerTransforms,
     70            std::move(aTransforms)));
     71    return;
     72  }
     73 
     74  APZCCallbackHelper::NotifyLayerTransforms(aTransforms);
     75 }
     76 
     77 void ChromeProcessController::RequestContentRepaint(
     78    const RepaintRequest& aRequest) {
     79  MOZ_ASSERT(IsRepaintThread());
     80 
     81  if (aRequest.IsRootContent()) {
     82    APZCCallbackHelper::UpdateRootFrame(aRequest);
     83  } else {
     84    APZCCallbackHelper::UpdateSubFrame(aRequest);
     85  }
     86 }
     87 
     88 bool ChromeProcessController::IsRepaintThread() { return NS_IsMainThread(); }
     89 
     90 void ChromeProcessController::DispatchToRepaintThread(
     91    already_AddRefed<Runnable> aTask) {
     92  NS_DispatchToMainThread(std::move(aTask));
     93 }
     94 
     95 void ChromeProcessController::Destroy() {
     96  if (!mUIThread->IsOnCurrentThread()) {
     97    mUIThread->Dispatch(
     98        NewRunnableMethod("layers::ChromeProcessController::Destroy", this,
     99                          &ChromeProcessController::Destroy));
    100    return;
    101  }
    102 
    103  MOZ_ASSERT(mUIThread->IsOnCurrentThread());
    104  mWidget = nullptr;
    105  if (mAPZEventState) {
    106    mAPZEventState->Destroy();
    107  }
    108  mAPZEventState = nullptr;
    109 }
    110 
    111 PresShell* ChromeProcessController::GetPresShell() const {
    112  if (!mWidget) {
    113    return nullptr;
    114  }
    115  auto* frame = mWidget->GetFrame();
    116  return frame ? frame->PresShell() : nullptr;
    117 }
    118 
    119 dom::Document* ChromeProcessController::GetRootDocument() const {
    120  if (PresShell* presShell = GetPresShell()) {
    121    return presShell->GetDocument();
    122  }
    123  return nullptr;
    124 }
    125 
    126 dom::Document* ChromeProcessController::GetRootContentDocument(
    127    const ScrollableLayerGuid::ViewID& aScrollId) const {
    128  nsIContent* content = nsLayoutUtils::FindContentFor(aScrollId);
    129  if (!content) {
    130    return nullptr;
    131  }
    132  if (PresShell* presShell =
    133          APZCCallbackHelper::GetRootContentDocumentPresShellForContent(
    134              content)) {
    135    return presShell->GetDocument();
    136  }
    137  return nullptr;
    138 }
    139 
    140 void ChromeProcessController::HandleDoubleTap(
    141    const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
    142    const ScrollableLayerGuid& aGuid,
    143    const DoubleTapToZoomMetrics& aDoubleTapToZoomMetrics) {
    144  MOZ_LOG(sApzChromeLog, LogLevel::Debug, ("HandleDoubleTap\n"));
    145  MOZ_ASSERT(mUIThread->IsOnCurrentThread());
    146 
    147  RefPtr<dom::Document> document = GetRootContentDocument(aGuid.mScrollId);
    148  if (!document) {
    149    return;
    150  }
    151 
    152  ZoomTarget zoomTarget =
    153      CalculateRectToZoomTo(document, aPoint, aDoubleTapToZoomMetrics);
    154 
    155  mAPZCTreeManager->ZoomToRect(aGuid, zoomTarget,
    156                               ZoomToRectBehavior::DEFAULT_BEHAVIOR);
    157 }
    158 
    159 void ChromeProcessController::HandleTap(
    160    TapType aType, const mozilla::LayoutDevicePoint& aPoint,
    161    Modifiers aModifiers, const ScrollableLayerGuid& aGuid,
    162    uint64_t aInputBlockId,
    163    const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
    164  MOZ_LOG(sApzChromeLog, LogLevel::Debug,
    165          ("HandleTap called with %d\n", (int)aType));
    166  if (!mUIThread->IsOnCurrentThread()) {
    167    MOZ_LOG(sApzChromeLog, LogLevel::Debug, ("HandleTap redispatching\n"));
    168    mUIThread->Dispatch(
    169        NewRunnableMethod<TapType, mozilla::LayoutDevicePoint, Modifiers,
    170                          ScrollableLayerGuid, uint64_t,
    171                          Maybe<DoubleTapToZoomMetrics>>(
    172            "layers::ChromeProcessController::HandleTap", this,
    173            &ChromeProcessController::HandleTap, aType, aPoint, aModifiers,
    174            aGuid, aInputBlockId, aDoubleTapToZoomMetrics));
    175    return;
    176  }
    177 
    178  if (!mAPZEventState) {
    179    return;
    180  }
    181 
    182  RefPtr<PresShell> presShell = GetPresShell();
    183  if (!presShell) {
    184    return;
    185  }
    186  if (!presShell->GetPresContext()) {
    187    return;
    188  }
    189  CSSToLayoutDeviceScale scale(
    190      presShell->GetPresContext()->CSSToDevPixelScale());
    191 
    192  CSSPoint point = aPoint / scale;
    193 
    194  // Stash the guid in InputAPZContext so that when the visual-to-layout
    195  // transform is applied to the event's coordinates, we use the right transform
    196  // based on the scroll frame being targeted.
    197  // The other values don't really matter.
    198  InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel);
    199 
    200  switch (aType) {
    201    case TapType::eSingleTap: {
    202      RefPtr<APZEventState> eventState(mAPZEventState);
    203      eventState->ProcessSingleTap(point, scale, aModifiers, 1, aInputBlockId);
    204      break;
    205    }
    206    case TapType::eDoubleTap:
    207      MOZ_ASSERT(aDoubleTapToZoomMetrics);
    208      HandleDoubleTap(point, aModifiers, aGuid, *aDoubleTapToZoomMetrics);
    209      break;
    210    case TapType::eSecondTap: {
    211      RefPtr<APZEventState> eventState(mAPZEventState);
    212      eventState->ProcessSingleTap(point, scale, aModifiers, 2, aInputBlockId);
    213      break;
    214    }
    215    case TapType::eLongTap: {
    216      RefPtr<APZEventState> eventState(mAPZEventState);
    217      eventState->ProcessLongTap(presShell, point, scale, aModifiers,
    218                                 aInputBlockId);
    219      break;
    220    }
    221    case TapType::eLongTapUp: {
    222      RefPtr<APZEventState> eventState(mAPZEventState);
    223      eventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
    224      break;
    225    }
    226  }
    227 
    228  // mAPZEventState may not dispatch the compatibility mouse events.  Therefore,
    229  // we should release the pointer capturing element at the last ePointerUp
    230  // here.
    231  PointerEventHandler::ReleasePointerCapturingElementAtLastPointerUp();
    232 }
    233 
    234 void ChromeProcessController::NotifyPinchGesture(
    235    PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid,
    236    const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange,
    237    Modifiers aModifiers) {
    238  if (!mUIThread->IsOnCurrentThread()) {
    239    mUIThread->Dispatch(
    240        NewRunnableMethod<PinchGestureInput::PinchGestureType,
    241                          ScrollableLayerGuid, LayoutDevicePoint,
    242                          LayoutDeviceCoord, Modifiers>(
    243            "layers::ChromeProcessController::NotifyPinchGesture", this,
    244            &ChromeProcessController::NotifyPinchGesture, aType, aGuid,
    245            aFocusPoint, aSpanChange, aModifiers));
    246    return;
    247  }
    248 
    249  if (mWidget) {
    250    // Dispatch the call to APZCCallbackHelper::NotifyPinchGesture to the main
    251    // thread so that it runs asynchronously from the current call. This is
    252    // because the call can run arbitrary JS code, which can also spin the event
    253    // loop and cause undesirable re-entrancy in APZ.
    254    mUIThread->Dispatch(NewRunnableFunction(
    255        "layers::ChromeProcessController::NotifyPinchGestureAsync",
    256        &APZCCallbackHelper::NotifyPinchGesture, aType, aFocusPoint,
    257        aSpanChange, aModifiers, mWidget));
    258  }
    259 }
    260 
    261 void ChromeProcessController::NotifyAPZStateChange(
    262    const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg,
    263    Maybe<uint64_t> aInputBlockId) {
    264  if (!mUIThread->IsOnCurrentThread()) {
    265    mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, APZStateChange,
    266                                          int, Maybe<uint64_t>>(
    267        "layers::ChromeProcessController::NotifyAPZStateChange", this,
    268        &ChromeProcessController::NotifyAPZStateChange, aGuid, aChange, aArg,
    269        aInputBlockId));
    270    return;
    271  }
    272 
    273  if (!mAPZEventState) {
    274    return;
    275  }
    276 
    277  mAPZEventState->ProcessAPZStateChange(aGuid.mScrollId, aChange, aArg,
    278                                        aInputBlockId);
    279 }
    280 
    281 void ChromeProcessController::NotifyMozMouseScrollEvent(
    282    const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent) {
    283  if (!mUIThread->IsOnCurrentThread()) {
    284    mUIThread->Dispatch(
    285        NewRunnableMethod<ScrollableLayerGuid::ViewID, nsString>(
    286            "layers::ChromeProcessController::NotifyMozMouseScrollEvent", this,
    287            &ChromeProcessController::NotifyMozMouseScrollEvent, aScrollId,
    288            aEvent));
    289    return;
    290  }
    291 
    292  APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
    293 }
    294 
    295 void ChromeProcessController::NotifyFlushComplete() {
    296  MOZ_ASSERT(IsRepaintThread());
    297 
    298  APZCCallbackHelper::NotifyFlushComplete(GetPresShell());
    299 }
    300 
    301 void ChromeProcessController::NotifyAsyncScrollbarDragInitiated(
    302    uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId,
    303    ScrollDirection aDirection) {
    304  if (!mUIThread->IsOnCurrentThread()) {
    305    mUIThread->Dispatch(NewRunnableMethod<uint64_t, ScrollableLayerGuid::ViewID,
    306                                          ScrollDirection>(
    307        "layers::ChromeProcessController::NotifyAsyncScrollbarDragInitiated",
    308        this, &ChromeProcessController::NotifyAsyncScrollbarDragInitiated,
    309        aDragBlockId, aScrollId, aDirection));
    310    return;
    311  }
    312 
    313  APZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId,
    314                                                        aDirection);
    315 }
    316 
    317 void ChromeProcessController::NotifyAsyncScrollbarDragRejected(
    318    const ScrollableLayerGuid::ViewID& aScrollId) {
    319  if (!mUIThread->IsOnCurrentThread()) {
    320    mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
    321        "layers::ChromeProcessController::NotifyAsyncScrollbarDragRejected",
    322        this, &ChromeProcessController::NotifyAsyncScrollbarDragRejected,
    323        aScrollId));
    324    return;
    325  }
    326 
    327  APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId);
    328 }
    329 
    330 void ChromeProcessController::NotifyAsyncAutoscrollRejected(
    331    const ScrollableLayerGuid::ViewID& aScrollId) {
    332  if (!mUIThread->IsOnCurrentThread()) {
    333    mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
    334        "layers::ChromeProcessController::NotifyAsyncAutoscrollRejected", this,
    335        &ChromeProcessController::NotifyAsyncAutoscrollRejected, aScrollId));
    336    return;
    337  }
    338 
    339  APZCCallbackHelper::NotifyAsyncAutoscrollRejected(aScrollId);
    340 }
    341 
    342 void ChromeProcessController::CancelAutoscroll(
    343    const ScrollableLayerGuid& aGuid) {
    344  mUIThread->Dispatch(NewRunnableFunction("layers::CancelAutoscroll",
    345                                          &APZCCallbackHelper::CancelAutoscroll,
    346                                          aGuid.mScrollId));
    347 }
    348 
    349 void ChromeProcessController::NotifyScaleGestureComplete(
    350    const ScrollableLayerGuid& aGuid, float aScale) {
    351  if (!mUIThread->IsOnCurrentThread()) {
    352    mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, float>(
    353        "layers::ChromeProcessController::NotifyScaleGestureComplete", this,
    354        &ChromeProcessController::NotifyScaleGestureComplete, aGuid, aScale));
    355    return;
    356  }
    357 
    358  if (mWidget) {
    359    // Dispatch the call to APZCCallbackHelper::NotifyScaleGestureComplete
    360    // to the main thread so that it runs asynchronously from the current call.
    361    // This is because the call can run arbitrary JS code, which can also spin
    362    // the event loop and cause undesirable re-entrancy in APZ.
    363    mUIThread->Dispatch(NewRunnableFunction(
    364        "layers::ChromeProcessController::NotifyScaleGestureComplete",
    365        &APZCCallbackHelper::NotifyScaleGestureComplete, mWidget, aScale));
    366  }
    367 }
    368 
    369 nsIFrame* ChromeProcessController::GetWidgetFrame() const {
    370  return mWidget ? mWidget->GetFrame() : nullptr;
    371 }