Avoid potential negative array index access to cached text.
[LibreOffice.git] / android / source / src / java / org / mozilla / gecko / gfx / GeckoLayerClient.java
blob72a96f0bb00faf46b2284efcb29387e21c2677b7
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
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/. */
6 package org.mozilla.gecko.gfx;
8 import org.libreoffice.LibreOfficeMainActivity;
9 import android.graphics.PointF;
10 import android.graphics.RectF;
11 import android.util.DisplayMetrics;
13 import org.libreoffice.LOKitShell;
14 import org.mozilla.gecko.ZoomConstraints;
16 import java.util.List;
18 public class GeckoLayerClient implements PanZoomTarget {
19 private static final String LOGTAG = GeckoLayerClient.class.getSimpleName();
21 private LayerRenderer mLayerRenderer;
23 private LibreOfficeMainActivity mContext;
24 private IntSize mScreenSize;
25 private DisplayPortMetrics mDisplayPort;
27 private ComposedTileLayer mLowResLayer;
28 private ComposedTileLayer mRootLayer;
30 private boolean mForceRedraw;
32 /* The current viewport metrics.
33 * This is volatile so that we can read and write to it from different threads.
34 * We avoid synchronization to make getting the viewport metrics from
35 * the compositor as cheap as possible. The viewport is immutable so
36 * we don't need to worry about anyone mutating it while we're reading from it.
37 * Specifically:
38 * 1) reading mViewportMetrics from any thread is fine without synchronization
39 * 2) writing to mViewportMetrics requires synchronizing on the layer controller object
40 * 3) whenever reading multiple fields from mViewportMetrics without synchronization (i.e. in
41 * case 1 above) you should always first grab a local copy of the reference, and then use
42 * that because mViewportMetrics might get reassigned in between reading the different
43 * fields. */
44 private volatile ImmutableViewportMetrics mViewportMetrics;
46 private ZoomConstraints mZoomConstraints;
48 private boolean mIsReady;
50 private PanZoomController mPanZoomController;
51 private LayerView mView;
52 private final DisplayPortCalculator mDisplayPortCalculator;
54 public GeckoLayerClient(LibreOfficeMainActivity context) {
55 // we can fill these in with dummy values because they are always written
56 // to before being read
57 mContext = context;
58 mScreenSize = new IntSize(0, 0);
59 mDisplayPort = new DisplayPortMetrics();
60 mDisplayPortCalculator = new DisplayPortCalculator(mContext);
62 mForceRedraw = true;
63 DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
64 mViewportMetrics = new ImmutableViewportMetrics(displayMetrics);
67 public void setView(LayerView view) {
68 mView = view;
69 mPanZoomController = PanZoomController.Factory.create(mContext, this, view);
70 mView.connect(this);
73 public void notifyReady() {
74 mIsReady = true;
76 mRootLayer = new DynamicTileLayer(mContext);
77 mLowResLayer = new FixedZoomTileLayer(mContext);
79 mLayerRenderer = new LayerRenderer(mView);
81 mView.setLayerRenderer(mLayerRenderer);
83 sendResizeEventIfNecessary(false);
84 mView.requestRender();
87 public void destroy() {
88 mPanZoomController.destroy();
91 Layer getRoot() {
92 return mIsReady ? mRootLayer : null;
95 Layer getLowResLayer() {
96 return mIsReady ? mLowResLayer : null;
99 public LayerView getView() {
100 return mView;
104 * Returns true if this controller is fine with performing a redraw operation or false if it
105 * would prefer that the action didn't take place.
107 private boolean getRedrawHint() {
108 if (mForceRedraw) {
109 mForceRedraw = false;
110 return true;
113 if (!mPanZoomController.getRedrawHint()) {
114 return false;
116 return mDisplayPortCalculator.aboutToCheckerboard(mViewportMetrics, mPanZoomController.getVelocityVector(), getDisplayPort());
120 * The view calls this function to indicate that the viewport changed size. It must hold the
121 * monitor while calling it.
123 * TODO: Refactor this to use an interface. Expose that interface only to the view and not
124 * to the layer client. That way, the layer client won't be tempted to call this, which might
125 * result in an infinite loop.
127 void setViewportSize(FloatSize size, boolean forceResizeEvent) {
128 mViewportMetrics = mViewportMetrics.setViewportSize(size.width, size.height);
129 sendResizeEventIfNecessary(forceResizeEvent);
132 PanZoomController getPanZoomController() {
133 return mPanZoomController;
136 /* Informs Gecko that the screen size has changed.
137 * @param force: If true, a resize event will always be sent, otherwise
138 * it is only sent if size has changed. */
139 private void sendResizeEventIfNecessary(boolean force) {
140 DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
141 IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
143 if (!force && mScreenSize.equals(newScreenSize)) {
144 return;
147 mScreenSize = newScreenSize;
149 LOKitShell.sendSizeChangedEvent(mScreenSize.width, mScreenSize.height);
153 * Sets the current page rect. You must hold the monitor while calling this.
155 private void setPageRect(RectF rect, RectF cssRect) {
156 // Since the "rect" is always just a multiple of "cssRect" we don't need to
157 // check both; this function assumes that both "rect" and "cssRect" are relative
158 // the zoom factor in mViewportMetrics.
159 if (mViewportMetrics.getCssPageRect().equals(cssRect))
160 return;
162 mViewportMetrics = mViewportMetrics.setPageRect(rect, cssRect);
164 // Page size is owned by the layer client, so no need to notify it of
165 // this change.
167 post(new Runnable() {
168 public void run() {
169 mPanZoomController.pageRectUpdated();
170 mView.requestRender();
175 private void adjustViewport(DisplayPortMetrics displayPort) {
176 ImmutableViewportMetrics metrics = getViewportMetrics();
178 ImmutableViewportMetrics clampedMetrics = metrics.clamp();
180 if (displayPort == null) {
181 displayPort = mDisplayPortCalculator.calculate(metrics, mPanZoomController.getVelocityVector());
184 mDisplayPort = displayPort;
186 reevaluateTiles();
190 * Aborts any pan/zoom animation that is currently in progress.
192 public void abortPanZoomAnimation() {
193 if (mPanZoomController != null) {
194 mView.post(new Runnable() {
195 public void run() {
196 mPanZoomController.abortAnimation();
202 public void setZoomConstraints(ZoomConstraints constraints) {
203 mZoomConstraints = constraints;
206 /** The compositor invokes this function whenever it determines that the page rect
207 * has changed (based on the information it gets from layout). If setFirstPaintViewport
208 * is invoked on a frame, then this function will not be. For any given frame, this
209 * function will be invoked before syncViewportInfo.
211 public void setPageRect(float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
212 synchronized (getLock()) {
213 RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
214 float ourZoom = getViewportMetrics().zoomFactor;
215 setPageRect(RectUtils.scale(cssPageRect, ourZoom), cssPageRect);
216 // Here the page size of the document has changed, but the document being displayed
217 // is still the same. Therefore, we don't need to send anything to browser.js; any
218 // changes we need to make to the display port will get sent the next time we call
219 // adjustViewport().
223 private DisplayPortMetrics getDisplayPort() {
224 return mDisplayPort;
227 public void beginDrawing() {
228 mLowResLayer.beginTransaction();
229 mRootLayer.beginTransaction();
232 public void endDrawing() {
233 mLowResLayer.endTransaction();
234 mRootLayer.endTransaction();
237 private void geometryChanged() {
238 sendResizeEventIfNecessary(false);
239 if (getRedrawHint()) {
240 adjustViewport(null);
244 /** Implementation of PanZoomTarget */
245 @Override
246 public ImmutableViewportMetrics getViewportMetrics() {
247 return mViewportMetrics;
250 /** Implementation of PanZoomTarget */
251 @Override
252 public ZoomConstraints getZoomConstraints() {
253 return mZoomConstraints;
256 /** Implementation of PanZoomTarget */
257 @Override
258 public void setAnimationTarget(ImmutableViewportMetrics viewport) {
259 if (mIsReady) {
260 // We know what the final viewport of the animation is going to be, so
261 // immediately request a draw of that area by setting the display port
262 // accordingly. This way we should have the content pre-rendered by the
263 // time the animation is done.
264 DisplayPortMetrics displayPort = mDisplayPortCalculator.calculate(viewport, null);
265 adjustViewport(displayPort);
269 /** Implementation of PanZoomTarget
270 * You must hold the monitor while calling this.
272 @Override
273 public void setViewportMetrics(ImmutableViewportMetrics viewport) {
274 mViewportMetrics = viewport;
275 mView.requestRender();
276 if (mIsReady) {
277 geometryChanged();
281 /** Implementation of PanZoomTarget */
282 @Override
283 public void forceRedraw() {
284 mForceRedraw = true;
285 if (mIsReady) {
286 geometryChanged();
290 /** Implementation of PanZoomTarget */
291 @Override
292 public boolean post(Runnable action) {
293 return mView.post(action);
296 /** Implementation of PanZoomTarget */
297 @Override
298 public Object getLock() {
299 return this;
302 public PointF convertViewPointToLayerPoint(PointF viewPoint) {
303 ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
304 PointF origin = viewportMetrics.getOrigin();
305 float zoom = viewportMetrics.zoomFactor;
307 return new PointF(
308 ((viewPoint.x + origin.x) / zoom),
309 ((viewPoint.y + origin.y) / zoom));
312 /** Implementation of PanZoomTarget */
313 @Override
314 public boolean isFullScreen() {
315 return false;
318 public void zoomTo(RectF rect) {
319 if (mPanZoomController instanceof JavaPanZoomController) {
320 ((JavaPanZoomController) mPanZoomController).animatedZoomTo(rect);
325 * Move the viewport to the desired point, and change the zoom level.
327 public void moveTo(PointF point, Float zoom) {
328 if (mPanZoomController instanceof JavaPanZoomController) {
329 ((JavaPanZoomController) mPanZoomController).animatedMove(point, zoom);
333 public void zoomTo(float pageWidth, float pageHeight) {
334 zoomTo(new RectF(0, 0, pageWidth, pageHeight));
337 public void forceRender() {
338 mView.requestRender();
341 /* Root Layer Access */
342 private void reevaluateTiles() {
343 mLowResLayer.reevaluateTiles(mViewportMetrics, mDisplayPort);
344 mRootLayer.reevaluateTiles(mViewportMetrics, mDisplayPort);
347 public void clearAndResetlayers() {
348 mLowResLayer.clearAndReset();
349 mRootLayer.clearAndReset();
352 public void invalidateTiles(List<SubTile> tilesToInvalidate, RectF rect) {
353 mLowResLayer.invalidateTiles(tilesToInvalidate, rect);
354 mRootLayer.invalidateTiles(tilesToInvalidate, rect);