summaryrefslogtreecommitdiffstats
path: root/widget/android/AndroidContentController.cpp
blob: 1df053afb2f61b4e85cfaa661a1deb0a723db7ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "AndroidContentController.h"

#include "AndroidBridge.h"
#include "base/message_loop.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/IAPZCTreeManager.h"
#include "nsIObserverService.h"
#include "nsLayoutUtils.h"
#include "nsWindow.h"

using mozilla::layers::IAPZCTreeManager;

namespace mozilla {
namespace widget {

void
AndroidContentController::Destroy()
{
    mAndroidWindow = nullptr;
    ChromeProcessController::Destroy();
}

void
AndroidContentController::NotifyDefaultPrevented(IAPZCTreeManager* aManager,
                                                 uint64_t aInputBlockId,
                                                 bool aDefaultPrevented)
{
    if (!AndroidBridge::IsJavaUiThread()) {
        // The notification must reach the APZ on the Java UI thread (aka the
        // APZ "controller" thread) but we get it from the Gecko thread, so we
        // have to throw it onto the other thread.
        AndroidBridge::Bridge()->PostTaskToUiThread(NewRunnableMethod<uint64_t, bool>(
            aManager, &IAPZCTreeManager::ContentReceivedInputBlock,
            aInputBlockId, aDefaultPrevented), 0);
        return;
    }

    aManager->ContentReceivedInputBlock(aInputBlockId, aDefaultPrevented);
}

void
AndroidContentController::DispatchSingleTapToObservers(const LayoutDevicePoint& aPoint,
                                                       const ScrollableLayerGuid& aGuid) const
{
    nsIContent* content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
    nsPresContext* context = content
        ? mozilla::layers::APZCCallbackHelper::GetPresContextForContent(content)
        : nullptr;

    if (!context) {
      return;
    }

    CSSPoint point = mozilla::layers::APZCCallbackHelper::ApplyCallbackTransform(
        aPoint / context->CSSToDevPixelScale(), aGuid);

    nsPresContext* rcdContext = context->GetToplevelContentDocumentPresContext();
    if (rcdContext && rcdContext->PresShell()->ScaleToResolution()) {
        // We need to convert from the root document to the root content document,
        // by unapplying the resolution that's on the content document.
        const float resolution = rcdContext->PresShell()->GetResolution();
        point.x /= resolution;
        point.y /= resolution;
    }

    CSSIntPoint rounded = RoundedToInt(point);
    nsAppShell::PostEvent([rounded] {
        nsCOMPtr<nsIObserverService> obsServ =
            mozilla::services::GetObserverService();
        if (!obsServ) {
            return;
        }

        nsPrintfCString data("{\"x\":%d,\"y\":%d}", rounded.x, rounded.y);
        obsServ->NotifyObservers(nullptr, "Gesture:SingleTap",
                                 NS_ConvertASCIItoUTF16(data).get());
    });
}

void
AndroidContentController::HandleTap(TapType aType, const LayoutDevicePoint& aPoint,
                                    Modifiers aModifiers,
                                    const ScrollableLayerGuid& aGuid,
                                    uint64_t aInputBlockId)
{
    // This function will get invoked first on the Java UI thread, and then
    // again on the main thread (because of the code in ChromeProcessController::
    // HandleTap). We want to post the SingleTap message once; it can be
    // done from either thread but we need access to the callback transform
    // so we do it from the main thread.
    if (NS_IsMainThread() &&
        (aType == TapType::eSingleTap || aType == TapType::eSecondTap)) {
        DispatchSingleTapToObservers(aPoint, aGuid);
    }

    ChromeProcessController::HandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId);
}

void
AndroidContentController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
{
    AndroidBridge::Bridge()->PostTaskToUiThread(Move(aTask), aDelayMs);
}
void
AndroidContentController::UpdateOverscrollVelocity(const float aX, const float aY, const bool aIsRootContent)
{
  if (aIsRootContent && mAndroidWindow) {
    mAndroidWindow->UpdateOverscrollVelocity(aX, aY);
  }
}

void
AndroidContentController::UpdateOverscrollOffset(const float aX, const float aY, const bool aIsRootContent)
{
  if (aIsRootContent && mAndroidWindow) {
    mAndroidWindow->UpdateOverscrollOffset(aX, aY);
  }
}

void
AndroidContentController::SetScrollingRootContent(const bool isRootContent)
{
  if (mAndroidWindow) {
    mAndroidWindow->SetScrollingRootContent(isRootContent);
  }
}

void
AndroidContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                               APZStateChange aChange,
                                               int aArg)
{
  // This function may get invoked twice, if the first invocation is not on
  // the main thread then the ChromeProcessController version of this function
  // will redispatch to the main thread. We want to make sure that our handling
  // only happens on the main thread.
  ChromeProcessController::NotifyAPZStateChange(aGuid, aChange, aArg);
  if (NS_IsMainThread()) {
    nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
    if (aChange == layers::GeckoContentController::APZStateChange::eTransformEnd) {
      // This is used by tests to determine when the APZ is done doing whatever
      // it's doing. XXX generify this as needed when writing additional tests.
      observerService->NotifyObservers(nullptr, "APZ:TransformEnd", nullptr);
      observerService->NotifyObservers(nullptr, "PanZoom:StateChange", u"NOTHING");
    } else if (aChange == layers::GeckoContentController::APZStateChange::eTransformBegin) {
      observerService->NotifyObservers(nullptr, "PanZoom:StateChange", u"PANNING");
    }
  }
}

} // namespace widget
} // namespace mozilla