Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "ZoomConstraintsClient.h"
7 :
8 : #include <inttypes.h>
9 : #include "FrameMetrics.h"
10 : #include "gfxPrefs.h"
11 : #include "LayersLogging.h"
12 : #include "mozilla/layers/APZCCallbackHelper.h"
13 : #include "mozilla/Preferences.h"
14 : #include "mozilla/PresShell.h"
15 : #include "mozilla/dom/Event.h"
16 : #include "nsDocument.h"
17 : #include "nsIFrame.h"
18 : #include "nsLayoutUtils.h"
19 : #include "nsPoint.h"
20 : #include "nsView.h"
21 : #include "nsViewportInfo.h"
22 : #include "Units.h"
23 : #include "UnitTransforms.h"
24 :
25 : #define ZCC_LOG(...)
26 : // #define ZCC_LOG(...) printf_stderr("ZCC: " __VA_ARGS__)
27 :
28 110 : NS_IMPL_ISUPPORTS(ZoomConstraintsClient, nsIDOMEventListener, nsIObserver)
29 :
30 3 : static const nsLiteralString DOM_META_ADDED = NS_LITERAL_STRING("DOMMetaAdded");
31 3 : static const nsLiteralString DOM_META_CHANGED = NS_LITERAL_STRING("DOMMetaChanged");
32 3 : static const nsLiteralString FULLSCREEN_CHANGED = NS_LITERAL_STRING("fullscreenchange");
33 3 : static const nsLiteralCString BEFORE_FIRST_PAINT = NS_LITERAL_CSTRING("before-first-paint");
34 3 : static const nsLiteralCString NS_PREF_CHANGED = NS_LITERAL_CSTRING("nsPref:changed");
35 :
36 : using namespace mozilla;
37 : using namespace mozilla::layers;
38 :
39 3 : ZoomConstraintsClient::ZoomConstraintsClient() :
40 : mDocument(nullptr),
41 3 : mPresShell(nullptr)
42 : {
43 3 : }
44 :
45 2 : ZoomConstraintsClient::~ZoomConstraintsClient()
46 : {
47 2 : }
48 :
49 : static nsIWidget*
50 5 : GetWidget(nsIPresShell* aShell)
51 : {
52 5 : if (!aShell) {
53 0 : return nullptr;
54 : }
55 5 : if (nsIFrame* rootFrame = aShell->GetRootFrame()) {
56 : #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
57 : return rootFrame->GetNearestWidget();
58 : #else
59 1 : if (nsView* view = rootFrame->GetView()) {
60 1 : return view->GetWidget();
61 : }
62 : #endif
63 : }
64 4 : return nullptr;
65 : }
66 :
67 : void
68 2 : ZoomConstraintsClient::Destroy()
69 : {
70 2 : if (!(mPresShell && mDocument)) {
71 0 : return;
72 : }
73 :
74 : ZCC_LOG("Destroying %p\n", this);
75 :
76 2 : if (mEventTarget) {
77 2 : mEventTarget->RemoveEventListener(DOM_META_ADDED, this, false);
78 2 : mEventTarget->RemoveEventListener(DOM_META_CHANGED, this, false);
79 2 : mEventTarget->RemoveSystemEventListener(FULLSCREEN_CHANGED, this, false);
80 2 : mEventTarget = nullptr;
81 : }
82 :
83 4 : nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
84 2 : if (observerService) {
85 2 : observerService->RemoveObserver(this, BEFORE_FIRST_PAINT.Data());
86 : }
87 :
88 2 : Preferences::RemoveObserver(this, "browser.ui.zoom.force-user-scalable");
89 :
90 2 : if (mGuid) {
91 0 : if (nsIWidget* widget = GetWidget(mPresShell)) {
92 : ZCC_LOG("Sending null constraints in %p for { %u, %" PRIu64 " }\n",
93 : this, mGuid->mPresShellId, mGuid->mScrollId);
94 0 : widget->UpdateZoomConstraints(mGuid->mPresShellId, mGuid->mScrollId, Nothing());
95 0 : mGuid = Nothing();
96 : }
97 : }
98 :
99 2 : mDocument = nullptr;
100 2 : mPresShell = nullptr;
101 : }
102 :
103 : void
104 3 : ZoomConstraintsClient::Init(nsIPresShell* aPresShell, nsIDocument* aDocument)
105 : {
106 3 : if (!(aPresShell && aDocument)) {
107 0 : return;
108 : }
109 :
110 3 : mPresShell = aPresShell;
111 3 : mDocument = aDocument;
112 :
113 6 : if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow()) {
114 3 : mEventTarget = window->GetParentTarget();
115 : }
116 3 : if (mEventTarget) {
117 3 : mEventTarget->AddEventListener(DOM_META_ADDED, this, false);
118 3 : mEventTarget->AddEventListener(DOM_META_CHANGED, this, false);
119 3 : mEventTarget->AddSystemEventListener(FULLSCREEN_CHANGED, this, false);
120 : }
121 :
122 6 : nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
123 3 : if (observerService) {
124 3 : observerService->AddObserver(this, BEFORE_FIRST_PAINT.Data(), false);
125 : }
126 :
127 3 : Preferences::AddStrongObserver(this, "browser.ui.zoom.force-user-scalable");
128 : }
129 :
130 : NS_IMETHODIMP
131 2 : ZoomConstraintsClient::HandleEvent(nsIDOMEvent* event)
132 : {
133 4 : nsAutoString type;
134 2 : event->GetType(type);
135 :
136 2 : if (type.Equals(DOM_META_ADDED)) {
137 : ZCC_LOG("Got a dom-meta-added event in %p\n", this);
138 2 : RefreshZoomConstraints();
139 0 : } else if (type.Equals(DOM_META_CHANGED)) {
140 : ZCC_LOG("Got a dom-meta-changed event in %p\n", this);
141 0 : RefreshZoomConstraints();
142 0 : } else if (type.Equals(FULLSCREEN_CHANGED)) {
143 : ZCC_LOG("Got a fullscreen-change event in %p\n", this);
144 0 : RefreshZoomConstraints();
145 : }
146 :
147 4 : return NS_OK;
148 : }
149 :
150 : NS_IMETHODIMP
151 1 : ZoomConstraintsClient::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
152 : {
153 1 : if (SameCOMIdentity(aSubject, mDocument) && BEFORE_FIRST_PAINT.EqualsASCII(aTopic)) {
154 : ZCC_LOG("Got a before-first-paint event in %p\n", this);
155 1 : RefreshZoomConstraints();
156 0 : } else if (NS_PREF_CHANGED.EqualsASCII(aTopic)) {
157 : ZCC_LOG("Got a pref-change event in %p\n", this);
158 : // We need to run this later because all the pref change listeners need
159 : // to execute before we can be guaranteed that gfxPrefs::ForceUserScalable()
160 : // returns the updated value.
161 :
162 : RefPtr<nsRunnableMethod<ZoomConstraintsClient>> event =
163 0 : NewRunnableMethod("ZoomConstraintsClient::RefreshZoomConstraints",
164 : this,
165 0 : &ZoomConstraintsClient::RefreshZoomConstraints);
166 0 : mDocument->Dispatch("ZoomConstraintsClient::RefreshZoomConstraints",
167 : TaskCategory::Other,
168 0 : event.forget());
169 : }
170 1 : return NS_OK;
171 : }
172 :
173 : void
174 2 : ZoomConstraintsClient::ScreenSizeChanged()
175 : {
176 : ZCC_LOG("Got a screen-size change notification in %p\n", this);
177 2 : RefreshZoomConstraints();
178 2 : }
179 :
180 : mozilla::layers::ZoomConstraints
181 1 : ComputeZoomConstraintsFromViewportInfo(const nsViewportInfo& aViewportInfo)
182 : {
183 1 : mozilla::layers::ZoomConstraints constraints;
184 1 : constraints.mAllowZoom = aViewportInfo.IsZoomAllowed() && gfxPrefs::APZAllowZooming();
185 1 : constraints.mAllowDoubleTapZoom = constraints.mAllowZoom;
186 1 : if (constraints.mAllowZoom) {
187 0 : constraints.mMinZoom.scale = aViewportInfo.GetMinZoom().scale;
188 0 : constraints.mMaxZoom.scale = aViewportInfo.GetMaxZoom().scale;
189 : } else {
190 1 : constraints.mMinZoom.scale = aViewportInfo.GetDefaultZoom().scale;
191 1 : constraints.mMaxZoom.scale = aViewportInfo.GetDefaultZoom().scale;
192 : }
193 1 : return constraints;
194 : }
195 :
196 : void
197 5 : ZoomConstraintsClient::RefreshZoomConstraints()
198 : {
199 5 : nsIWidget* widget = GetWidget(mPresShell);
200 5 : if (!widget) {
201 8 : return;
202 : }
203 :
204 1 : uint32_t presShellId = 0;
205 1 : FrameMetrics::ViewID viewId = FrameMetrics::NULL_SCROLL_ID;
206 : bool scrollIdentifiersValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
207 1 : mDocument->GetDocumentElement(),
208 1 : &presShellId, &viewId);
209 1 : if (!scrollIdentifiersValid) {
210 0 : return;
211 : }
212 :
213 1 : LayoutDeviceIntSize screenSize;
214 1 : if (!nsLayoutUtils::GetContentViewerSize(mPresShell->GetPresContext(), screenSize)) {
215 0 : return;
216 : }
217 :
218 1 : nsViewportInfo viewportInfo = mDocument->GetViewportInfo(
219 1 : ViewAs<ScreenPixel>(screenSize, PixelCastJustification::LayoutDeviceIsScreenForBounds));
220 :
221 : mozilla::layers::ZoomConstraints zoomConstraints =
222 2 : ComputeZoomConstraintsFromViewportInfo(viewportInfo);
223 :
224 1 : if (mDocument->Fullscreen()) {
225 : ZCC_LOG("%p is in fullscreen, disallowing zooming\n", this);
226 0 : zoomConstraints.mAllowZoom = false;
227 0 : zoomConstraints.mAllowDoubleTapZoom = false;
228 : }
229 :
230 1 : if (zoomConstraints.mAllowDoubleTapZoom) {
231 : // If the CSS viewport is narrower than the screen (i.e. width <= device-width)
232 : // then we disable double-tap-to-zoom behaviour.
233 : CSSToLayoutDeviceScale scale =
234 0 : mPresShell->GetPresContext()->CSSToDevPixelScale();
235 0 : if ((viewportInfo.GetSize() * scale).width <= screenSize.width) {
236 0 : zoomConstraints.mAllowDoubleTapZoom = false;
237 : }
238 : }
239 :
240 : // We only ever create a ZoomConstraintsClient for an RCD, so the RSF of
241 : // the presShell must be the RCD-RSF (if it exists).
242 1 : MOZ_ASSERT(mPresShell->GetPresContext()->IsRootContentDocument());
243 1 : if (nsIScrollableFrame* rcdrsf = mPresShell->GetRootScrollFrameAsScrollable()) {
244 : ZCC_LOG("Notifying RCD-RSF that it is zoomable: %d\n", zoomConstraints.mAllowZoom);
245 1 : rcdrsf->SetZoomableByAPZ(zoomConstraints.mAllowZoom);
246 : }
247 :
248 2 : ScrollableLayerGuid newGuid(0, presShellId, viewId);
249 1 : if (mGuid && mGuid.value() != newGuid) {
250 : ZCC_LOG("Clearing old constraints in %p for { %u, %" PRIu64 " }\n",
251 : this, mGuid->mPresShellId, mGuid->mScrollId);
252 : // If the guid changes, send a message to clear the old one
253 0 : widget->UpdateZoomConstraints(mGuid->mPresShellId, mGuid->mScrollId, Nothing());
254 : }
255 1 : mGuid = Some(newGuid);
256 : ZCC_LOG("Sending constraints %s in %p for { %u, %" PRIu64 " }\n",
257 : Stringify(zoomConstraints).c_str(), this, presShellId, viewId);
258 1 : widget->UpdateZoomConstraints(presShellId, viewId, Some(zoomConstraints));
259 : }
|