[Libreoffice-commits] core.git: Branch 'distro/collabora/viewer' - 2 commits - android/Bootstrap android/experimental solenv/bin

Jan Holesovsky kendy at collabora.com
Sat Feb 21 16:21:33 PST 2015


 android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java                           |    2 
 android/Bootstrap/src/org/libreoffice/kit/Document.java                                        |   24 
 android/Bootstrap/src/org/libreoffice/kit/Office.java                                          |    5 
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java                          |   25 
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java                          |   38 -
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOEventFactory.java                   |   44 -
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java                       |   52 +
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java                      |  152 +++--
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java                |  191 +++++-
 android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java          |   69 +-
 android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java                 |   14 
 android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java                     |   31 -
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java                |    3 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java          |  188 +++---
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java      |    2 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java         |    1 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java                  |    3 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java           |  252 ++------
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java     |    2 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java      |  289 +++++-----
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java              |  212 -------
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java                  |  143 +++-
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PanZoomController.java          |   10 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PanZoomTarget.java              |    2 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/RenderControllerThread.java     |   37 -
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScreenshotLayer.java            |  200 ------
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java |    1 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/SubTile.java                    |  238 ++++++++
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TouchEventHandler.java          |   58 --
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/util/FloatUtils.java                |    2 
 solenv/bin/native-code.py                                                                      |    9 
 31 files changed, 1128 insertions(+), 1171 deletions(-)

New commits:
commit 2f7ea81c59d214f17492af2afc114bd5a2a529ea
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Sun Feb 22 01:15:41 2015 +0100

    Backported Tomaž's rendering improvements from the tiled-editing branch.
    
    Much improves the rendering experience, and decreases the amount of oom's too.
    
    It is up to the 'android: support selection invalidation, show selection
    handles' commit (including).
    
    Change-Id: Ife926aabbb83128f221f500eab0e6acdbe589306

diff --git a/android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java b/android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java
index 670e397..de90303 100644
--- a/android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java
+++ b/android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java
@@ -11,8 +11,6 @@ package org.libreoffice.kit;
 // https://code.google.com/p/android/issues/detail?id=16941
 //
 
-import android.util.Log;
-
 import java.nio.ByteBuffer;
 
 public final class DirectBufferAllocator {
diff --git a/android/Bootstrap/src/org/libreoffice/kit/Document.java b/android/Bootstrap/src/org/libreoffice/kit/Document.java
index 3cd9b0e..aee11f7 100644
--- a/android/Bootstrap/src/org/libreoffice/kit/Document.java
+++ b/android/Bootstrap/src/org/libreoffice/kit/Document.java
@@ -9,8 +9,6 @@
 
 package org.libreoffice.kit;
 
-import android.util.Log;
-
 import java.nio.ByteBuffer;
 
 public class Document {
@@ -26,10 +24,29 @@ public class Document {
     public static final int DOCTYPE_DRAWING = 3;
     public static final int DOCTYPE_OTHER = 4;
 
+    /**
+     * Mouse event types
+     */
     public static final int MOUSE_BUTTON_DOWN = 0;
     public static final int MOUSE_BUTTON_UP = 1;
     public static final int MOUSE_MOVE = 2;
 
+    /**
+     * Callback message types
+     */
+    public static final int CALLBACK_INVALIDATE_TILES = 0;
+    public static final int CALLBACK_INVALIDATE_VISIBLE_CURSOR = 1;
+    public static final int CALLBACK_INVALIDATE_TEXT_SELECTION = 2;
+    public static final int CALLBACK_INVALIDATE_TEXT_SELECTION_START = 3;
+    public static final int CALLBACK_INVALIDATE_TEXT_SELECTION_END = 4;
+
+    /**
+     * Text selection types
+     */
+    public static final int TEXT_SELECTION_START = 0;
+    public static final int TEXT_SELECTION_END = 1;
+    public static final int TEXT_SELECTION_RESET = 2;
+
     private final ByteBuffer handle;
     private MessageCallback messageCallback = null;
 
@@ -94,8 +111,9 @@ public class Document {
      * @param type - mouse event type
      * @param x - x coordinate
      * @param y - y coordinate
+     * @param count - number of events
      */
-    public native void postMouseEvent(int type, int x, int y);
+    public native void postMouseEvent(int type, int x, int y, int count);
 
     /**
      * Callback to retrieve messages from LOK
diff --git a/android/Bootstrap/src/org/libreoffice/kit/Office.java b/android/Bootstrap/src/org/libreoffice/kit/Office.java
index 25861c9..8bd780b 100644
--- a/android/Bootstrap/src/org/libreoffice/kit/Office.java
+++ b/android/Bootstrap/src/org/libreoffice/kit/Office.java
@@ -37,9 +37,10 @@ public class Office {
     /**
      * Post a key event to LibreOffice.
      * @param type - type of key event
-     * @param code - key event code
+     * @param charCode - the Unicode character generated by this event or 0.
+     * @param keyCode - the integer code representing the key of the event (non-zero for control keys).
      */
-    public native void postKeyEvent(int type, int code);
+    public native void postKeyEvent(int type, int charCode, int keyCode);
 
     public native void destroy();
     public native void destroyAndExit();
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java
index 13c436f..47ecddf 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java
@@ -2,27 +2,14 @@ package org.libreoffice;
 
 import android.app.Activity;
 import android.app.AlertDialog;
-import android.content.DialogInterface;
 import android.content.ComponentName;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.widget.DrawerLayout;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ListView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
+import android.content.DialogInterface;
 import android.content.Intent;
-import android.net.Uri;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.view.View;
+import android.widget.TextView;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.io.File;
 
 public abstract class LOAbout extends Activity {
@@ -44,8 +31,8 @@ public abstract class LOAbout extends Activity {
             i.setComponent(componentName);
             startActivity(i);
         } else {
-            LOKitShell.sendEvent(LOEventFactory.close());
-            LOKitShell.sendEvent(LOEventFactory.load(input));
+            LOKitShell.sendCloseEvent();
+            LOKitShell.sendLoadEvent(input);
         }
     }
 
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
index 809988d..807d0d0 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
@@ -1,5 +1,7 @@
 package org.libreoffice;
 
+import android.graphics.PointF;
+import android.graphics.RectF;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
@@ -14,11 +16,11 @@ public class LOEvent implements Comparable<LOEvent> {
     public static final int LOAD = 4;
     public static final int CLOSE = 5;
     public static final int REDRAW = 6;
-    public static final int TILE_REQUEST = 7;
+    public static final int TILE_REEVALUATION_REQUEST = 7;
     public static final int THUMBNAIL = 8;
-    public static final int TILE_RERENDER = 9;
+    public static final int TILE_INVALIDATION = 9;
     public static final int TOUCH = 10;
-    public static final int KEY_PRESS = 11;
+    public static final int KEY_EVENT = 11;
 
     public final int mType;
     public int mPriority = 0;
@@ -27,13 +29,13 @@ public class LOEvent implements Comparable<LOEvent> {
     public String mTypeString;
     public int mPartIndex;
     public String mFilename;
-    public TileIdentifier mTileId;
     public ComposedTileLayer mComposedTileLayer;
-    public boolean mForceRedraw;
-    public SubTile mTile;
     public String mTouchType;
     public MotionEvent mMotionEvent;
+    public PointF mDocumentTouchCoordinate;
+    public String mKeyEventType;
     public KeyEvent mKeyEvent;
+    public RectF mInvalidationRect;
 
     public LOEvent(int type) {
         mType = type;
@@ -44,12 +46,10 @@ public class LOEvent implements Comparable<LOEvent> {
         mTypeString = "Size Changed: " + widthPixels + " " + heightPixels;
     }
 
-    public LOEvent(int type, ComposedTileLayer composedTileLayer, TileIdentifier tileId, boolean forceRedraw) {
+    public LOEvent(int type, ComposedTileLayer composedTileLayer) {
         mType = type;
-        mTypeString = "Tile Request";
+        mTypeString = "Tile Reevaluation";
         mComposedTileLayer = composedTileLayer;
-        mTileId = tileId;
-        mForceRedraw = forceRedraw;
     }
 
     public LOEvent(int type, String filename) {
@@ -72,27 +72,29 @@ public class LOEvent implements Comparable<LOEvent> {
     public LOEvent(int type, ThumbnailCreator.ThumbnailCreationTask task) {
         mType = type;
         mTask = task;
+        mTypeString = "Thumbnail";
     }
 
-    public LOEvent(int type, ComposedTileLayer composedTileLayer, SubTile tile) {
-        mType = type;
-        mTypeString = "Tile Rerender";
-        mComposedTileLayer = composedTileLayer;
-        mTile = tile;
-    }
-
-    public LOEvent(int type, String touchType, MotionEvent motionEvent) {
+    public LOEvent(int type, String touchType, MotionEvent motionEvent, PointF documentTouchCoordinate) {
         mType = type;
         mTypeString = "Touch";
         mTouchType = touchType;
         mMotionEvent = motionEvent;
+        mDocumentTouchCoordinate = documentTouchCoordinate;
     }
 
     public LOEvent(int type, KeyEvent keyEvent) {
         mType = type;
+        mTypeString = "Key Event";
         mKeyEvent = keyEvent;
     }
 
+    public LOEvent(int type, RectF rect) {
+        mType = type;
+        mTypeString = "Tile Invalidation";
+        mInvalidationRect = rect;
+    }
+
     public String getTypeString() {
         if (mTypeString == null) {
             return "Event type: " + mType;
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEventFactory.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEventFactory.java
deleted file mode 100644
index b866850..0000000
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEventFactory.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.libreoffice;
-
-import org.mozilla.gecko.gfx.ComposedTileLayer;
-import org.mozilla.gecko.gfx.IntSize;
-import org.mozilla.gecko.gfx.SubTile;
-
-
-public class LOEventFactory {
-    public static LOEvent sizeChanged(int widthPixels, int heightPixels) {
-        return new LOEvent(LOEvent.SIZE_CHANGED, widthPixels, heightPixels);
-    }
-
-    public static LOEvent tileSize(IntSize tileSize) {
-        return new LOEvent(LOEvent.TILE_SIZE, tileSize);
-    }
-
-    public static LOEvent changePart(int part) {
-        return new LOEvent(LOEvent.CHANGE_PART, part);
-    }
-
-    public static LOEvent load(String inputFile) {
-        return new LOEvent(LOEvent.LOAD, inputFile);
-    }
-
-    public static LOEvent close() {
-        return new LOEvent(LOEvent.CLOSE);
-    }
-
-    public static LOEvent redraw() {
-        return new LOEvent(LOEvent.REDRAW);
-    }
-
-    public static LOEvent tileRequest(ComposedTileLayer composedTileLayer, TileIdentifier tileID, boolean forceRedraw) {
-        return new LOEvent(LOEvent.TILE_REQUEST, composedTileLayer, tileID, forceRedraw);
-    }
-
-    public static LOEvent thumbnail(ThumbnailCreator.ThumbnailCreationTask task) {
-        return new LOEvent(LOEvent.THUMBNAIL, task);
-    }
-
-    public static LOEvent tileRerender(ComposedTileLayer composedTileLayer, SubTile tile) {
-        return new LOEvent(LOEvent.TILE_RERENDER, composedTileLayer, tile);
-    }
-}
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
index 49da6b4..2037e9d 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
@@ -3,11 +3,17 @@ package org.libreoffice;
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.RectF;
 import android.os.Handler;
 import android.util.DisplayMetrics;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import org.mozilla.gecko.gfx.ComposedTileLayer;
+import org.mozilla.gecko.gfx.LayerView;
+import org.mozilla.gecko.gfx.SubTile;
+
 
 public class LOKitShell {
     private static final String LOGTAG = LOKitShell.class.getSimpleName();
@@ -54,6 +60,14 @@ public class LOKitShell {
         return metrics;
     }
 
+    public static boolean isEditingEnabled() {
+        return false;
+    }
+
+    public static LayerView getLayerView() {
+        return LibreOfficeMainActivity.mAppContext.getLayerClient().getView();
+    }
+
     // EVENTS
 
     /**
@@ -72,18 +86,42 @@ public class LOKitShell {
     /**
      * Send touch event to LOKitThread.
      */
-    public static void sentTouchEvent(String touchType, MotionEvent motionEvent) {
-        LOKitShell.sendEvent(new LOEvent(LOEvent.TOUCH, "SingleTap", motionEvent));
+    public static void sentTouchEvent(String touchType, MotionEvent motionEvent, PointF pointF) {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.TOUCH, touchType, motionEvent, pointF));
     }
 
     /**
-     * Send key press event to LOKitThread.
+     * Send key event to LOKitThread.
      */
-    public static void sendKeyPressEvent(KeyEvent event) {
-        LOKitShell.sendEvent(new LOEvent(LOEvent.KEY_PRESS, event));
+    public static void sendKeyEvent(KeyEvent event) {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.KEY_EVENT, event));
     }
 
-    public static boolean isEditingEnabled() {
-        return false;
+    public static void sendSizeChangedEvent(int width, int height) {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.SIZE_CHANGED, width, height));
+    }
+
+    public static void sendChangePartEvent(int part) {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.CHANGE_PART, part));
+    }
+
+    public static void sendLoadEvent(String inputFile) {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.LOAD, inputFile));
+    }
+
+    public static void sendCloseEvent() {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.CLOSE));
+    }
+
+    public static void sendRedrawEvent() {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.REDRAW));
+    }
+
+    public static void sendTileReevaluationRequest(ComposedTileLayer composedTileLayer) {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.TILE_REEVALUATION_REQUEST, composedTileLayer));
+    }
+
+    public static void sendTileInvalidationRequest(RectF rect) {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.TILE_INVALIDATION, rect));
     }
 }
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index afc9028..9ec76be 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -4,6 +4,7 @@ import android.graphics.Bitmap;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 
 import org.mozilla.gecko.gfx.CairoImage;
@@ -12,12 +13,15 @@ import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.SubTile;
 
-import java.util.concurrent.PriorityBlockingQueue;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
 
-public class LOKitThread extends Thread implements TileProvider.TileInvalidationCallback {
+public class LOKitThread extends Thread {
     private static final String LOGTAG = LOKitThread.class.getSimpleName();
 
-    private PriorityBlockingQueue<LOEvent> mEventQueue = new PriorityBlockingQueue<LOEvent>();
+    private LinkedBlockingQueue<LOEvent> mEventQueue = new LinkedBlockingQueue<LOEvent>();
+
     private LibreOfficeMainActivity mApplication;
     private TileProvider mTileProvider;
     private ImmutableViewportMetrics mViewportMetrics;
@@ -27,35 +31,66 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
         TileProviderFactory.initialize();
     }
 
-    private void tileRequest(ComposedTileLayer composedTileLayer, TileIdentifier tileId, boolean forceRedraw) {
-        if (mTileProvider == null)
+    @Override
+    public void run() {
+        while (true) {
+            LOEvent event;
+            try {
+                event = mEventQueue.take();
+                processEvent(event);
+            } catch (InterruptedException exception) {
+                throw new RuntimeException(exception);
+            }
+        }
+    }
+
+    /* Viewport changed, recheck if tiles need to be added / removed */
+    private void tileReevaluationRequest(ComposedTileLayer composedTileLayer) {
+        if (mTileProvider == null) {
             return;
+        }
+        List<SubTile> tiles = new ArrayList<SubTile>();
+
+        mLayerClient.beginDrawing();
+        composedTileLayer.addNewTiles(tiles);
+        mLayerClient.endDrawing();
 
-        if (composedTileLayer.isStillValid(tileId)) {
+        for (SubTile tile : tiles) {
+            TileIdentifier tileId = tile.id;
             CairoImage image = mTileProvider.createTile(tileId.x, tileId.y, tileId.size, tileId.zoom);
+            mLayerClient.beginDrawing();
             if (image != null) {
-                mLayerClient.beginDrawing();
-                SubTile tile = new SubTile(image, tileId);
-                composedTileLayer.addTile(tile);
-                mLayerClient.endDrawing(mViewportMetrics);
-                mLayerClient.forceRender();
+                tile.setImage(image);
             }
-        } else {
-            composedTileLayer.cleanupInvalidTile(tileId);
+            mLayerClient.endDrawing();
+            mLayerClient.forceRender();
         }
+
+        mLayerClient.beginDrawing();
+        composedTileLayer.markTiles();
+        composedTileLayer.clearMarkedTiles();
+        mLayerClient.endDrawing();
+        mLayerClient.forceRender();
     }
 
-    private void tileRerender(ComposedTileLayer composedTileLayer, SubTile tile) {
-        if (mTileProvider == null)
+    /* Invalidate tiles that intersect the input rect */
+    private void tileInvalidation(RectF rect) {
+        if (mLayerClient == null || mTileProvider == null) {
             return;
+        }
 
-        if (composedTileLayer.isStillValid(tile.id) && !tile.markedForRemoval) {
-            mLayerClient.beginDrawing();
-            mTileProvider.rerenderTile(tile.getImage(), tile.id.x, tile.id.y, tile.id.size, tile.id.zoom);
+        mLayerClient.beginDrawing();
+
+        List<SubTile> tiles = new ArrayList<SubTile>();
+        mLayerClient.invalidateTiles(tiles, rect);
+
+        for (SubTile tile : tiles) {
+            CairoImage image = mTileProvider.createTile(tile.id.x, tile.id.y, tile.id.size, tile.id.zoom);
+            tile.setImage(image);
             tile.invalidate();
-            mLayerClient.endDrawing(mViewportMetrics);
-            mLayerClient.forceRender();
         }
+        mLayerClient.endDrawing();
+        mLayerClient.forceRender();
     }
 
     /** Handle the geometry change + draw. */
@@ -72,6 +107,7 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
         zoomAndRepositionTheDocument();
 
         mLayerClient.forceRedraw();
+        mLayerClient.forceRender();
     }
 
     private void zoomAndRepositionTheDocument() {
@@ -118,7 +154,6 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
 
         if (mTileProvider.isReady()) {
             LOKitShell.showProgressSpinner();
-            mTileProvider.registerInvalidationCallback(this);
             refresh();
             LOKitShell.hideProgressSpinner();
         } else {
@@ -133,15 +168,6 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
         }
     }
 
-    public void run() {
-        try {
-            while (true) {
-                processEvent(mEventQueue.take());
-            }
-        } catch (InterruptedException ex) {
-        }
-    }
-
     private void processEvent(LOEvent event) {
         switch (event.mType) {
             case LOEvent.LOAD:
@@ -157,32 +183,54 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
             case LOEvent.CHANGE_PART:
                 changePart(event.mPartIndex);
                 break;
-            case LOEvent.TILE_REQUEST:
-                tileRequest(event.mComposedTileLayer, event.mTileId, event.mForceRedraw);
-                break;
-            case LOEvent.TILE_RERENDER:
-                tileRerender(event.mComposedTileLayer, event.mTile);
+            case LOEvent.TILE_INVALIDATION:
+                tileInvalidation(event.mInvalidationRect);
                 break;
             case LOEvent.THUMBNAIL:
                 createThumbnail(event.mTask);
                 break;
             case LOEvent.TOUCH:
-                if (!LOKitShell.isEditingEnabled()) {
-                    return;
-                }
-                touch(event.mTouchType, event.mMotionEvent);
+                touch(event.mTouchType, event.mMotionEvent, event.mDocumentTouchCoordinate);
+                break;
+            case LOEvent.KEY_EVENT:
+                keyEvent(event.mKeyEvent);
                 break;
-            case LOEvent.KEY_PRESS:
-                if (!LOKitShell.isEditingEnabled()) {
-                    return;
-                }
-                mTileProvider.keyPress(event.mKeyEvent);
+            case LOEvent.TILE_REEVALUATION_REQUEST:
+                tileReevaluationRequest(event.mComposedTileLayer);
                 break;
         }
     }
 
-    private void touch(String touchType, MotionEvent motionEvent) {
-        LibreOfficeMainActivity.mAppContext.showSoftKeyboard();
+    /**
+     * Processes key events.
+     */
+    private void keyEvent(KeyEvent keyEvent) {
+        if (!LOKitShell.isEditingEnabled()) {
+            return;
+        }
+        if (mTileProvider == null) {
+            return;
+        }
+        mTileProvider.sendKeyEvent(keyEvent);
+    }
+
+    /**
+     * Processes touch events.
+     */
+    private void touch(String touchType, MotionEvent motionEvent, PointF mDocumentTouchCoordinate) {
+        if (!LOKitShell.isEditingEnabled()) {
+            return;
+        }
+        if (mTileProvider == null) {
+            return;
+        }
+        if (touchType.equals("LongPress")) {
+            LibreOfficeMainActivity.mAppContext.hideSoftKeyboard();
+            mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 2);
+        } else { // "SingleTap"
+            LibreOfficeMainActivity.mAppContext.showSoftKeyboard();
+            mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 1);
+        }
     }
 
     private void createThumbnail(final ThumbnailCreator.ThumbnailCreationTask task) {
@@ -197,18 +245,6 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
     public void clearQueue() {
         mEventQueue.clear();
     }
-
-    @Override
-    public void invalidate(RectF rect) {
-        if (!LOKitShell.isEditingEnabled()) {
-            return;
-        }
-
-        Log.i(LOGTAG, "Invalidate request: " + rect);
-
-        mLayerClient = mApplication.getLayerClient();
-        mLayerClient.invalidateTiles(rect);
-    }
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
index 2caf748..679f8b4 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
@@ -1,6 +1,7 @@
 package org.libreoffice;
 
 import android.graphics.Bitmap;
+import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -9,6 +10,8 @@ import org.libreoffice.kit.DirectBufferAllocator;
 import org.libreoffice.kit.Document;
 import org.libreoffice.kit.LibreOfficeKit;
 import org.libreoffice.kit.Office;
+//import org.mozilla.gecko.TextSelection;
+//import org.mozilla.gecko.TextSelectionHandle;
 import org.mozilla.gecko.gfx.BufferedCairoImage;
 import org.mozilla.gecko.gfx.CairoImage;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
@@ -29,7 +32,6 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
     private Office mOffice;
     private Document mDocument;
     private boolean mIsReady = false;
-    private TileInvalidationCallback tileInvalidationCallback = null;
 
     private float mDPI;
     private float mWidthTwip;
@@ -72,8 +74,7 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
         if (checkDocument()) {
             postLoad();
             mIsReady = true;
-        }
-        else {
+        } else {
             mIsReady = false;
         }
     }
@@ -277,18 +278,63 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
     }
 
     /**
-     * Register the tile invalidation callback.
+     * Returns the Unicode character generated by this event or 0.
+     */
+    private int getCharCode(KeyEvent keyEvent) {
+        switch (keyEvent.getKeyCode())
+        {
+            case KeyEvent.KEYCODE_DEL:
+            case KeyEvent.KEYCODE_ENTER:
+                return 0;
+        }
+        return keyEvent.getUnicodeChar();
+    }
+
+    /**
+     * Returns the integer code representing the key of the event (non-zero for
+     * control keys).
      */
+    private int getKeyCode(KeyEvent keyEvent) {
+        switch (keyEvent.getKeyCode()) {
+            case KeyEvent.KEYCODE_DEL:
+                return com.sun.star.awt.Key.BACKSPACE;
+            case KeyEvent.KEYCODE_ENTER:
+                return com.sun.star.awt.Key.RETURN;
+        }
+        return 0;
+    }
+
     @Override
-    public void registerInvalidationCallback(TileInvalidationCallback tileInvalidationCallback) {
-        this.tileInvalidationCallback = tileInvalidationCallback;
+    public void sendKeyEvent(KeyEvent keyEvent) {
+        if (keyEvent.getAction() == KeyEvent.ACTION_MULTIPLE) {
+            String keyString = keyEvent.getCharacters();
+            for (int i = 0; i < keyString.length(); i++) {
+                int codePoint = keyString.codePointAt(i);
+                mOffice.postKeyEvent(Office.KEY_PRESS, codePoint, getKeyCode(keyEvent));
+            }
+        } else if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+            mOffice.postKeyEvent(Office.KEY_PRESS, getCharCode(keyEvent), getKeyCode(keyEvent));
+        } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
+            mOffice.postKeyEvent(Office.KEY_RELEASE, getCharCode(keyEvent), getKeyCode(keyEvent));
+        }
+    }
+
+    private void mouseButton(int type, PointF inDocument, int numberOfClicks) {
+        int x = (int) pixelToTwip(inDocument.x, mDPI);
+        int y = (int) pixelToTwip(inDocument.y, mDPI);
+
+        mDocument.postMouseEvent(type, x, y, numberOfClicks);
     }
 
     @Override
-    public void keyPress(KeyEvent keyEvent) {
-        mOffice.postKeyEvent(Office.KEY_PRESS, keyEvent.getKeyCode());
+    public void mouseButtonDown(PointF documentCoordinate, int numberOfClicks) {
+        mouseButton(Document.MOUSE_BUTTON_DOWN, documentCoordinate, numberOfClicks);
     }
 
+    @Override
+    public void mouseButtonUp(PointF documentCoordinate, int numberOfClicks) {
+        mouseButton(Document.MOUSE_BUTTON_UP, documentCoordinate, numberOfClicks);
+    }
 
     @Override
     protected void finalize() throws Throwable {
@@ -313,33 +359,126 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
         return mDocument.getPart();
     }
 
+    private RectF convertCallbackMessageStringToRectF(String text) {
+        if (text.equals("EMPTY")) {
+            return null;
+        }
+
+        String[] coordinates = text.split(",");
+
+        if (coordinates.length != 4) {
+            return null;
+        }
+        int width = Integer.decode(coordinates[0].trim());
+        int height = Integer.decode(coordinates[1].trim());
+        int x = Integer.decode(coordinates[2].trim());
+        int y = Integer.decode(coordinates[3].trim());
+        RectF rect = new RectF(
+                twipToPixel(x, mDPI),
+                twipToPixel(y, mDPI),
+                twipToPixel(x + width, mDPI),
+                twipToPixel(y + height, mDPI)
+        );
+        return rect;
+    }
+
     /**
      * Process the retrieved messages from LOK
      */
     @Override
     public void messageRetrieved(int signalNumber, String payload) {
+        if (!LOKitShell.isEditingEnabled()) {
+            return;
+        }
+
         switch (signalNumber) {
-            case 0:
-                if (!payload.equals("EMPTY")) {
-                    String[] coordinates = payload.split(",");
-
-                    if (coordinates.length == 4) {
-                        int width = Integer.decode(coordinates[0].trim());
-                        int height = Integer.decode(coordinates[1].trim());
-                        int x = Integer.decode(coordinates[2].trim());
-                        int y = Integer.decode(coordinates[3].trim());
-                        RectF rect = new RectF(
-                                twipToPixel(x, mDPI),
-                                twipToPixel(y, mDPI),
-                                twipToPixel(x + width, mDPI),
-                                twipToPixel(y + height, mDPI)
-                        );
-                        tileInvalidationCallback.invalidate(rect);
-                    }
-                }
+            case Document.CALLBACK_INVALIDATE_TILES:
+                invalidateTiles(payload);
+                break;
+            case Document.CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+                invalidateCursor(payload);
+                break;
+            case Document.CALLBACK_INVALIDATE_TEXT_SELECTION:
+                Log.i(LOGTAG, "Selection: " + payload);
+                invalidateSelection(payload);
+                break;
+            case Document.CALLBACK_INVALIDATE_TEXT_SELECTION_START:
+                Log.i(LOGTAG, "Selection start: " + payload);
+                invalidateSelectionStart(payload);
+                break;
+            case Document.CALLBACK_INVALIDATE_TEXT_SELECTION_END:
+                Log.i(LOGTAG, "Selection end: " + payload);
+                invalidateSelectionEnd(payload);
                 break;
         }
     }
+
+    private void invalidateCursor(String payload) {
+        /*
+        RectF rect = convertCallbackMessageStringToRectF(payload);
+        if (rect != null) {
+            RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom);
+            TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
+            textSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, underSelection);
+            textSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE);
+
+            TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer();
+            textCursorLayer.positionCursor(rect);
+            textCursorLayer.showCursor();
+        }
+        */
+    }
+
+    private void invalidateTiles(String payload) {
+        RectF rect = convertCallbackMessageStringToRectF(payload);
+        if (rect != null) {
+            LOKitShell.sendTileInvalidationRequest(rect);
+        }
+    }
+
+    private void invalidateSelectionStart(String payload) {
+        /*
+        RectF rect = convertCallbackMessageStringToRectF(payload);
+        if (rect != null) {
+            RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom);
+            TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
+            textSelection.positionHandle(TextSelectionHandle.HandleType.START, underSelection);
+            textSelection.showHandle(TextSelectionHandle.HandleType.START);
+
+            textSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE);
+
+            TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer();
+            textCursorLayer.hideCursor();
+        }
+        */
+    }
+
+    private void invalidateSelectionEnd(String payload) {
+        /*
+        RectF rect = convertCallbackMessageStringToRectF(payload);
+        if (rect != null) {
+            RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom);
+            TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
+            textSelection.positionHandle(TextSelectionHandle.HandleType.END, underSelection);
+            textSelection.showHandle(TextSelectionHandle.HandleType.END);
+
+            textSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE);
+
+            TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer();
+            textCursorLayer.hideCursor();
+        }
+        */
+    }
+
+    private void invalidateSelection(String payload) {
+        /*
+        if (payload.isEmpty()) {
+            TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
+            textSelection.hideHandle(TextSelectionHandle.HandleType.START);
+            textSelection.hideHandle(TextSelectionHandle.HandleType.END);
+        }
+        */
+    }
 }
 
 // vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
index 7fa45f5..13225c3 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
@@ -1,6 +1,5 @@
 package org.libreoffice;
 
-import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -8,10 +7,7 @@ import android.content.DialogInterface;
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v4.widget.DrawerLayout;
-import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -19,7 +15,6 @@ import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView;
 import android.widget.ListView;
 import android.widget.RelativeLayout;
-import android.widget.TextView;
 
 import org.mozilla.gecko.ZoomConstraints;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
@@ -28,7 +23,6 @@ import org.mozilla.gecko.gfx.LayerView;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -53,6 +47,8 @@ public class LibreOfficeMainActivity extends LOAbout {
     private List<DocumentPartView> mDocumentPartView = new ArrayList<DocumentPartView>();
     private DocumentPartViewListAdapter mDocumentPartViewListAdapter;
     private String mInputFile;
+    //private TextSelection mTextSelection;
+    //private TextCursorLayer mTextCursorLayer;
     private File mTempFile = null;
 
     public LibreOfficeMainActivity() {
@@ -102,8 +98,6 @@ public class LibreOfficeMainActivity extends LOAbout {
 
         mMainHandler = new Handler();
 
-        LayoutInflater.from(this).setFactory(ViewFactory.getInstance());
-
         if (getIntent().getData() != null) {
             if (getIntent().getData().getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
                 if (copyFileToTemp() && mTempFile != null) {
@@ -141,10 +135,16 @@ public class LibreOfficeMainActivity extends LOAbout {
             sLOKitThread.clearQueue();
         }
 
+        /*
+        mTextSelection = new TextSelection(mAppContext);
+        mTextCursorLayer = new TextCursorLayer(mAppContext);
+        */
+
         mLayerClient = new GeckoLayerClient(this);
         mLayerClient.setZoomConstraints(new ZoomConstraints(true));
         LayerView layerView = (LayerView) findViewById(R.id.layer_view);
         mLayerClient.setView(layerView);
+        layerView.setInputConnectionHandler(new LOKitInputConnectionHandler());
         mLayerClient.notifyReady();
     }
 
@@ -193,13 +193,14 @@ public class LibreOfficeMainActivity extends LOAbout {
     protected void onStart() {
         Log.i(LOGTAG, "onStart..");
         super.onStart();
-        LOKitShell.sendEvent(LOEventFactory.load(mInputFile));
+        LOKitShell.sendLoadEvent(mInputFile);
     }
 
     @Override
     protected void onStop() {
         Log.i(LOGTAG, "onStop..");
-        LOKitShell.sendEvent(LOEventFactory.close());
+        hideSoftKeyboardDirect();
+        LOKitShell.sendCloseEvent();
         super.onStop();
     }
 
@@ -243,8 +244,6 @@ public class LibreOfficeMainActivity extends LOAbout {
      * Force the request on main thread.
      */
     public void showSoftKeyboard() {
-        Log.i(LOGTAG, "SoftKeyboard show request..");
-
         LOKitShell.getMainHandler().post(new Runnable() {
             @Override
             public void run() {
@@ -258,6 +257,30 @@ public class LibreOfficeMainActivity extends LOAbout {
         });
     }
 
+    /**
+     * Hides software keyboard on UI thread.
+     */
+    public void hideSoftKeyboard() {
+        LOKitShell.getMainHandler().post(new Runnable() {
+            @Override
+            public void run() {
+                hideSoftKeyboardDirect();
+            }
+        });
+    }
+
+    /**
+     * Hides software keyboard.
+     */
+    private void hideSoftKeyboardDirect() {
+        LayerView layerView = (LayerView) findViewById(R.id.layer_view);
+
+        if (getCurrentFocus() != null) {
+            InputMethodManager inputMethodManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+            inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
+        }
+    }
+
     public void showProgressSpinner() {
         findViewById(R.id.loadingPanel).setVisibility(View.VISIBLE);
     }
@@ -282,24 +305,24 @@ public class LibreOfficeMainActivity extends LOAbout {
         alertDialog.show();
     }
 
+    /*
+    public TextSelection getTextSelection() {
+        return mTextSelection;
+    }
+
+    public TextCursorLayer getTextCursorLayer() {
+        return mTextCursorLayer;
+    }
+    */
+
     private class DocumentPartClickListener implements android.widget.AdapterView.OnItemClickListener {
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             DocumentPartView partView = mDocumentPartViewListAdapter.getItem(position);
-            LOKitShell.sendEvent(LOEventFactory.changePart(partView.partIndex));
+            LOKitShell.sendChangePartEvent(partView.partIndex);
             mDrawerLayout.closeDrawer(mDrawerList);
         }
     }
-
-
-    /**
-     * Listen to key presses and send event to LOK.
-     */
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        LOKitShell.sendKeyPressEvent(event);
-        return super.onKeyDown(keyCode, event);
-    }
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
index 1f15870..fe934a1 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
@@ -1,6 +1,7 @@
 package org.libreoffice;
 
 import android.graphics.Bitmap;
+import android.graphics.PointF;
 import android.view.KeyEvent;
 
 import org.mozilla.gecko.gfx.BufferedCairoImage;
@@ -23,7 +24,7 @@ public class MockTileProvider implements TileProvider {
             LibreOfficeMainActivity.mAppContext.mMainHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                LibreOfficeMainActivity.mAppContext.getDocumentPartViewListAdapter().add(partView);
+                    LibreOfficeMainActivity.mAppContext.getDocumentPartViewListAdapter().add(partView);
                 }
             });
         }
@@ -89,11 +90,18 @@ public class MockTileProvider implements TileProvider {
     }
 
     @Override
-    public void registerInvalidationCallback(TileInvalidationCallback tileInvalidationCallback) {
+    public void sendKeyEvent(KeyEvent keyEvent) {
+
+    }
+
+    @Override
+    public void mouseButtonDown(PointF documentCoordinate, int numberOfClicks) {
+
     }
 
     @Override
-    public void keyPress(KeyEvent keyEvent) {
+    public void mouseButtonUp(PointF documentCoordinate, int numberOfClicks) {
+
     }
 
     @Override
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
index 48bca1b..ea868bd 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
@@ -2,6 +2,7 @@ package org.libreoffice;
 
 
 import android.graphics.Bitmap;
+import android.graphics.PointF;
 import android.graphics.RectF;
 import android.view.KeyEvent;
 
@@ -30,12 +31,14 @@ public interface TileProvider {
 
     /**
      * Change the document part to the one specified by the partIndex input parameter.
+     *
      * @param partIndex - part index to change to
      */
     void changePart(int partIndex);
 
     /**
      * Get the current document part number.
+     *
      * @return
      */
     int getCurrentPartNumber();
@@ -58,27 +61,25 @@ public interface TileProvider {
     boolean isSpreadsheet();
 
     /**
-     * Register a callback that is invoked when a tile invalidation is
-     * required.
+     * Trigger a key event.
      *
-     * @param tileInvalidationCallback - the tile invalidation callback
+     * @param keyEvent - contains information about key event
      */
-    void registerInvalidationCallback(TileProvider.TileInvalidationCallback tileInvalidationCallback);
+    void sendKeyEvent(KeyEvent keyEvent);
 
     /**
-     * Trigger a key press.
-     * @param keyEvent - contains the
+     * Trigger a mouse button down event.
+     *
+     * @param documentCoordinate - coordinate relative to the document where the mouse button should be triggered
+     * @param numberOfClicks     - number of clicks (1 - single click, 2 - double click)
      */
-    void keyPress(KeyEvent keyEvent);
+    void mouseButtonDown(PointF documentCoordinate, int numberOfClicks);
 
     /**
-     * Callback to retrieve invalidation calls
+     * Trigger a mouse button up event.
+     *
+     * @param documentCoordinate - coordinate relative to the document where the mouse button should be triggered
+     * @param numberOfClicks     - number of clicks (1 - single click, 2 - double click)
      */
-    public interface TileInvalidationCallback {
-        /**
-         * Invoked when a region is invalidated.
-         * @param rect area in pixels which was invalidated and needs to be redrawn
-         */
-        void invalidate(RectF rect);
-    }
+    void mouseButtonUp(PointF documentCoordinate, int numberOfClicks);
 }
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java
index 4ac49f7..eba0dfe 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java
@@ -5,9 +5,6 @@
 
 package org.mozilla.gecko;
 
-import org.json.JSONException;
-import org.json.JSONObject;
-
 public final class ZoomConstraints {
     private final boolean mAllowZoom;
     private final boolean mAllowDoubleTapZoom;
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java
index ca25438..e11c91c 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java
@@ -1,6 +1,5 @@
 package org.mozilla.gecko.gfx;
 
-import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -8,136 +7,164 @@ import android.graphics.RectF;
 import android.graphics.Region;
 import android.util.Log;
 
-import org.libreoffice.LOEvent;
-import org.libreoffice.LOEventFactory;
 import org.libreoffice.LOKitShell;
 import org.libreoffice.TileIdentifier;
 import org.mozilla.gecko.util.FloatUtils;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 public abstract class ComposedTileLayer extends Layer implements ComponentCallbacks2 {
     private static final String LOGTAG = ComposedTileLayer.class.getSimpleName();
 
-    protected final List<SubTile> tiles = new CopyOnWriteArrayList<SubTile>();
+    protected final List<SubTile> tiles = new ArrayList<SubTile>();
 
     protected final IntSize tileSize;
+    private final ReadWriteLock tilesReadWriteLock = new ReentrantReadWriteLock();
+    private final Lock tilesReadLock = tilesReadWriteLock.readLock();
+    private final Lock tilesWriteLock = tilesReadWriteLock.writeLock();
+
     protected RectF currentViewport = new RectF();
-    protected float currentZoom;
+    protected float currentZoom = 1.0f;
+    protected RectF currentPageRect = new RectF();
+
+    private long reevaluationNanoTime = 0;
 
     public ComposedTileLayer(Context context) {
         context.registerComponentCallbacks(this);
         this.tileSize = new IntSize(256, 256);
     }
 
+    protected static RectF roundToTileSize(RectF input, IntSize tileSize) {
+        float minX = ((int) (input.left / tileSize.width)) * tileSize.width;
+        float minY = ((int) (input.top / tileSize.height)) * tileSize.height;
+        float maxX = ((int) (input.right / tileSize.width) + 1) * tileSize.width;
+        float maxY = ((int) (input.bottom / tileSize.height) + 1) * tileSize.height;
+        return new RectF(minX, minY, maxX, maxY);
+    }
+
+    protected static RectF inflate(RectF rect, IntSize inflateSize) {
+        RectF newRect = new RectF(rect);
+        newRect.left -= inflateSize.width;
+        newRect.left = newRect.left < 0.0f ? 0.0f : newRect.left;
+
+        newRect.top -= inflateSize.height;
+        newRect.top = newRect.top < 0.0f ? 0.0f : newRect.top;
+
+        newRect.right += inflateSize.width;
+        newRect.bottom += inflateSize.height;
+
+        return newRect;
+    }
+
+    protected static RectF normalizeRect(RectF rect, float sourceFactor, float targetFactor) {
+        RectF normalizedRect = new RectF(
+                (rect.left / sourceFactor) * targetFactor,
+                (rect.top / sourceFactor) * targetFactor,
+                (rect.right / sourceFactor) * targetFactor,
+                (rect.bottom / sourceFactor) * targetFactor);
+
+        return normalizedRect;
+    }
+
     public void invalidate() {
+        tilesReadLock.lock();
         for (SubTile tile : tiles) {
             tile.invalidate();
         }
+        tilesReadLock.unlock();
     }
 
     @Override
     public void beginTransaction() {
         super.beginTransaction();
+        tilesReadLock.lock();
         for (SubTile tile : tiles) {
             tile.beginTransaction();
         }
+        tilesReadLock.unlock();
     }
 
     @Override
     public void endTransaction() {
+        tilesReadLock.lock();
         for (SubTile tile : tiles) {
             tile.endTransaction();
         }
+        tilesReadLock.unlock();
         super.endTransaction();
     }
 
     @Override
     public void draw(RenderContext context) {
+        tilesReadLock.lock();
         for (SubTile tile : tiles) {
             if (RectF.intersects(tile.getBounds(context), context.viewport)) {
                 tile.draw(context);
             }
         }
+        tilesReadLock.unlock();
     }
 
     @Override
     protected void performUpdates(RenderContext context) {
         super.performUpdates(context);
 
+        tilesReadLock.lock();
         for (SubTile tile : tiles) {
             tile.beginTransaction();
             tile.refreshTileMetrics();
             tile.endTransaction();
             tile.performUpdates(context);
         }
+        tilesReadLock.unlock();
     }
 
     @Override
     public Region getValidRegion(RenderContext context) {
         Region validRegion = new Region();
+        tilesReadLock.lock();
         for (SubTile tile : tiles) {
             validRegion.op(tile.getValidRegion(context), Region.Op.UNION);
         }
-
+        tilesReadLock.unlock();
         return validRegion;
     }
 
     @Override
     public void setResolution(float newResolution) {
         super.setResolution(newResolution);
+        tilesReadLock.lock();
         for (SubTile tile : tiles) {
             tile.setResolution(newResolution);
         }
-    }
-
-    protected RectF roundToTileSize(RectF input, IntSize tileSize) {
-        float minX = ((int) (input.left / tileSize.width)) * tileSize.width;
-        float minY = ((int) (input.top / tileSize.height)) * tileSize.height;
-        float maxX = ((int) (input.right / tileSize.width) + 1) * tileSize.width;
-        float maxY = ((int) (input.bottom / tileSize.height) + 1) * tileSize.height;
-        return new RectF(minX, minY, maxX, maxY);
-    }
-
-    protected RectF inflate(RectF rect, IntSize inflateSize) {
-        RectF newRect = new RectF(rect);
-        newRect.left -= inflateSize.width;
-        newRect.left = newRect.left < 0.0f ? 0.0f : newRect.left;
-
-        newRect.top -= inflateSize.height;
-        newRect.top = newRect.top < 0.0f ? 0.0f : newRect.top;
-
-        newRect.right += inflateSize.width;
-        newRect.bottom += inflateSize.height;
-
-        return newRect;
-    }
-
-    protected RectF normalizeRect(RectF rect, float sourceFactor, float targetFactor) {
-        RectF normalizedRect = new RectF(
-                (rect.left / sourceFactor) * targetFactor,
-                (rect.top / sourceFactor) * targetFactor,
-                (rect.right / sourceFactor) * targetFactor,
-                (rect.bottom / sourceFactor) * targetFactor);
-
-        return normalizedRect;
+        tilesReadLock.unlock();
     }
 
     public void reevaluateTiles(ImmutableViewportMetrics viewportMetrics, DisplayPortMetrics mDisplayPort) {
         RectF newViewPort = getViewPort(viewportMetrics);
         float newZoom = getZoom(viewportMetrics);
 
-        if (!currentViewport.equals(newViewPort) || currentZoom != newZoom) {
-            currentViewport = newViewPort;
-            currentZoom = newZoom;
-            RectF pageRect = viewportMetrics.getPageRect();
+        if (currentViewport.equals(newViewPort) && FloatUtils.fuzzyEquals(currentZoom, newZoom)) {
+            return;
+        }
 
-            clearMarkedTiles();
-            addNewTiles(pageRect);
-            markTiles();
+        long currentReevaluationNanoTime = System.nanoTime();
+        if ((currentReevaluationNanoTime - reevaluationNanoTime) < 25 * 1000000) {
+            return;
         }
+
+        reevaluationNanoTime = currentReevaluationNanoTime;
+
+        currentViewport = newViewPort;
+        currentZoom = newZoom;
+        currentPageRect = viewportMetrics.getPageRect();
+
+        LOKitShell.sendTileReevaluationRequest(this);
     }
 
     protected abstract RectF getViewPort(ImmutableViewportMetrics viewportMetrics);
@@ -146,43 +173,53 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
 
     protected abstract int getTilePriority();
 
-    private void addNewTiles(RectF pageRect) {
+    private boolean containsTilesMatching(float x, float y, float currentZoom) {
+        tilesReadLock.lock();
+        try {
+            for (SubTile tile : tiles) {
+                if (tile.id.x == x && tile.id.y == y && tile.id.zoom == currentZoom) {
+                    return true;
+                }
+            }
+            return false;
+        } finally {
+            tilesReadLock.unlock();
+        }
+    }
+
+    public void addNewTiles(List<SubTile> newTiles) {
         for (float y = currentViewport.top; y < currentViewport.bottom; y += tileSize.height) {
-            if (y > pageRect.height()) {
+            if (y > currentPageRect.height()) {
                 continue;
             }
             for (float x = currentViewport.left; x < currentViewport.right; x += tileSize.width) {
-                if (x > pageRect.width()) {
+                if (x > currentPageRect.width()) {
                     continue;
                 }
-                boolean contains = false;
-                for (SubTile tile : tiles) {
-                    if (tile.id.x == x && tile.id.y == y && tile.id.zoom == currentZoom) {
-                        contains = true;
-                    }
-                }
-                if (!contains) {
+                if (!containsTilesMatching(x, y, currentZoom)) {
                     TileIdentifier tileId = new TileIdentifier((int) x, (int) y, currentZoom, tileSize);
-                    LOEvent event = LOEventFactory.tileRequest(this, tileId, true);
-                    event.mPriority = getTilePriority();
-                    LOKitShell.sendEvent(event);
+                    SubTile tile = createNewTile(tileId);
+                    newTiles.add(tile);
                 }
             }
         }
     }
 
-    private void clearMarkedTiles() {
-        List<SubTile> tilesToRemove = new ArrayList<SubTile>();
-        for (SubTile tile : tiles) {
+    public void clearMarkedTiles() {
+        tilesWriteLock.lock();
+        Iterator<SubTile> iterator = tiles.iterator();
+        while (iterator.hasNext()) {
+            SubTile tile = iterator.next();
             if (tile.markedForRemoval) {
                 tile.destroy();
-                tilesToRemove.add(tile);
+                iterator.remove();
             }
         }
-        tiles.removeAll(tilesToRemove);
+        tilesWriteLock.unlock();
     }
 
-    private void markTiles() {
+    public void markTiles() {
+        tilesReadLock.lock();
         for (SubTile tile : tiles) {
             if (FloatUtils.fuzzyEquals(tile.id.zoom, currentZoom)) {
                 RectF tileRect = tile.id.getRectF();
@@ -193,16 +230,23 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
                 tile.markForRemoval();
             }
         }
+        tilesReadLock.unlock();
     }
 
     public void clearAndReset() {
+        tilesWriteLock.lock();
         tiles.clear();
+        tilesWriteLock.unlock();
         currentViewport = new RectF();
     }
 
-    public void addTile(SubTile tile) {
+    private SubTile createNewTile(TileIdentifier tileId) {
+        SubTile tile = new SubTile(tileId);
         tile.beginTransaction();
+        tilesWriteLock.lock();
         tiles.add(tile);
+        tilesWriteLock.unlock();
+        return tile;
     }
 
     public boolean isStillValid(TileIdentifier tileId) {
@@ -212,14 +256,15 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
     /**
      * Invalidate tiles which intersect the input rect
      */
-    public void invalidateTiles(RectF cssRect) {
+    public void invalidateTiles(List<SubTile> tilesToInvalidate, RectF cssRect) {
         RectF zoomedRect = RectUtils.scale(cssRect, currentZoom);
-
+        tilesReadLock.lock();
         for (SubTile tile : tiles) {
-            if (RectF.intersects(zoomedRect, tile.id.getRectF())) {
-                LOKitShell.sendEvent(LOEventFactory.tileRerender(this, tile));
+            if (!tile.markedForRemoval && RectF.intersects(zoomedRect, tile.id.getRectF())) {
+                tilesToInvalidate.add(tile);
             }
         }
+        tilesReadLock.unlock();
     }
 
     @Override
@@ -239,7 +284,4 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
             Log.i(LOGTAG, "Trimming memory - TRIM_MEMORY_RUNNING_LOW");
         }
     }
-
-    public void cleanupInvalidTile(TileIdentifier tileId) {
-    }
 }
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java
index 789d5b6..f860cd9 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java
@@ -17,7 +17,7 @@ import org.mozilla.gecko.util.FloatUtils;
 import java.util.Map;
 
 final class DisplayPortCalculator {
-    private static final String LOGTAG = "GeckoDisplayPortCalculator";
+    private static final String LOGTAG = DisplayPortCalculator.class.getSimpleName();
     private static final PointF ZERO_VELOCITY = new PointF(0, 0);
 
     // Keep this in sync with the TILEDLAYERBUFFER_TILE_SIZE defined in gfx/layers/TiledLayerBuffer.h
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java
index ab9a637..f622c44 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java
@@ -6,6 +6,7 @@
 package org.mozilla.gecko.gfx;
 
 import android.graphics.RectF;
+
 import org.mozilla.gecko.util.FloatUtils;
 
 /*
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java
index dd9c1b3..7b18373 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java
@@ -5,10 +5,9 @@
 
 package org.mozilla.gecko.gfx;
 
-import org.mozilla.gecko.util.FloatUtils;
-
 import org.json.JSONException;
 import org.json.JSONObject;
+import org.mozilla.gecko.util.FloatUtils;
 
 public class FloatSize {
     public final float width, height;
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
index 9ec1c98..40fb8bb 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -6,56 +6,27 @@
 package org.mozilla.gecko.gfx;
 
 import android.content.Context;
-import android.graphics.Color;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.DisplayMetrics;
-import android.util.Log;
 
-import org.libreoffice.LOEvent;
-import org.libreoffice.LOEventFactory;
 import org.libreoffice.LOKitShell;
 import org.mozilla.gecko.ZoomConstraints;
-import org.mozilla.gecko.util.FloatUtils;
 
-public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
+import java.util.List;
+
+public class GeckoLayerClient implements PanZoomTarget {
     private static final String LOGTAG = GeckoLayerClient.class.getSimpleName();
 
     private LayerRenderer mLayerRenderer;
-    private boolean mLayerRendererInitialized;
 
     private Context mContext;
     private IntSize mScreenSize;
-    private IntSize mWindowSize;
     private DisplayPortMetrics mDisplayPort;
-    private DisplayPortMetrics mReturnDisplayPort;
-
-    private boolean mRecordDrawTimes;
-    private final DrawTimingQueue mDrawTimingQueue;
 
     private ComposedTileLayer mLowResLayer;
     private ComposedTileLayer mRootLayer;
 
-    /* The Gecko viewport as per the UI thread. Must be touched only on the UI thread.
-     * If any events being sent to Gecko that are relative to the Gecko viewport position,
-     * they must (a) be relative to this viewport, and (b) be sent on the UI thread to
-     * avoid races. As long as these two conditions are satisfied, and the events being
-     * sent to Gecko are processed in FIFO order, the events will properly be relative
-     * to the Gecko viewport position. Note that if Gecko updates its viewport independently,
-     * we get notified synchronously and also update this on the UI thread.
-     */
-    private ImmutableViewportMetrics mGeckoViewport;
-
-    /*
-     * The viewport metrics being used to draw the current frame. This is only
-     * accessed by the compositor thread, and so needs no synchronisation.
-     */
-    private ImmutableViewportMetrics mFrameMetrics;
-
-    private ImmutableViewportMetrics mNewGeckoViewport;
-
-    private boolean mPendingViewportAdjust;
-
     private boolean mForceRedraw;
 
     /* The current viewport metrics.
@@ -74,12 +45,9 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
 
     private ZoomConstraints mZoomConstraints;
 
-    private boolean mGeckoIsReady;
-
-    /* The new color for the checkerboard. */
-    private int mCheckerboardColor;
+    private boolean mIsReady;
 
-    private final PanZoomController mPanZoomController;
+    private PanZoomController mPanZoomController;
     private LayerView mView;
 
     public GeckoLayerClient(Context context) {
@@ -87,35 +55,52 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
         // to before being read
         mContext = context;
         mScreenSize = new IntSize(0, 0);
-        mWindowSize = new IntSize(0, 0);
         mDisplayPort = new DisplayPortMetrics();
-        mRecordDrawTimes = false;
-        mDrawTimingQueue = new DrawTimingQueue();
 
         mForceRedraw = true;
         DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
         mViewportMetrics = new ImmutableViewportMetrics(displayMetrics);
         mZoomConstraints = new ZoomConstraints(false);
-
-        mPanZoomController = PanZoomController.Factory.create(this);
     }
 
-    public void setView(LayerView v) {
-        mView = v;
+    public void setView(LayerView view) {
+        mView = view;
+        mPanZoomController = PanZoomController.Factory.create(this, view);
         mView.connect(this);
     }
 
     public void notifyReady() {
-        mGeckoIsReady = true;
+        mIsReady = true;
 
         mRootLayer = new DynamicTileLayer(mContext);
         mLowResLayer = new FixedZoomTileLayer(mContext);
 
         mLayerRenderer = new LayerRenderer(mView);
 
-        mView.setListener(this);
         mView.setLayerRenderer(mLayerRenderer);
-        sendResizeEventIfNecessary(true);
+
+        sendResizeEventIfNecessary();
+        mView.requestRender();
+    }
+
+    public void destroy() {
+        mPanZoomController.destroy();
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    Layer getRoot() {
+        return mIsReady ? mRootLayer : null;
+    }
+
+    Layer getLowResLayer() {
+        return mIsReady ? mLowResLayer : null;
+    }
+
+    public LayerView getView() {
+        return mView;
     }
 
     /**
@@ -131,22 +116,9 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
         if (!mPanZoomController.getRedrawHint()) {
             return false;
         }
-
         return DisplayPortCalculator.aboutToCheckerboard(mViewportMetrics, mPanZoomController.getVelocityVector(), getDisplayPort());
     }
 
-    Layer getRoot() {
-        return mGeckoIsReady ? mRootLayer : null;
-    }
-
-    Layer getLowResLayer() {
-        return mGeckoIsReady ? mLowResLayer : null;
-    }
-
-    public LayerView getView() {
-        return mView;
-    }
-
     /**
      * The view calls this function to indicate that the viewport changed size. It must hold the
      * monitor while calling it.
@@ -157,7 +129,7 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
      */
     void setViewportSize(FloatSize size) {
         mViewportMetrics = mViewportMetrics.setViewportSize(size.width, size.height);
-        sendResizeEventIfNecessary(true);
+        sendResizeEventIfNecessary();
     }
 
     public PanZoomController getPanZoomController() {
@@ -165,26 +137,17 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
     }
 
     /* Informs Gecko that the screen size has changed. */
-    private void sendResizeEventIfNecessary(boolean force) {
+    private void sendResizeEventIfNecessary() {
         DisplayMetrics metrics = LOKitShell.getDisplayMetrics();
-
         IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
 
-        // Return immediately if the screen size hasn't changed or the viewport
-        // size is zero (which indicates that the rendering surface hasn't been
-        // allocated yet).
-        boolean screenSizeChanged = !mScreenSize.equals(newScreenSize);
-
-        if (!force && !screenSizeChanged) {
+        if (mScreenSize.equals(newScreenSize)) {
             return;
         }
 
-        Log.d(LOGTAG, "Screen-size changed to " + mScreenSize + " - > " + newScreenSize);
-
         mScreenSize = newScreenSize;
 
-        LOEvent event = LOEventFactory.sizeChanged(metrics.widthPixels, metrics.heightPixels);
-        LOKitShell.sendEvent(event);
+        LOKitShell.sendSizeChangedEvent(mScreenSize.width, mScreenSize.height);
     }
 
     /**
@@ -220,11 +183,6 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
         }
 
         mDisplayPort = displayPort;
-        mGeckoViewport = clampedMetrics;
-
-        if (mRecordDrawTimes) {
-            mDrawTimingQueue.add(displayPort);
-        }
 
         reevaluateTiles();
     }
@@ -272,94 +230,34 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
         mRootLayer.beginTransaction();
     }
 
-    public void endDrawing(ImmutableViewportMetrics viewportMetrics) {
-        synchronized (this) {
-            try {
-                mNewGeckoViewport = viewportMetrics;
-                updateViewport();
-                //mLowResLayer.invalidate();
-                //mRootLayer.invalidate();
-            } finally {
-                mLowResLayer.endTransaction();
-                mRootLayer.endTransaction();
-            }
-        }
-    }
-
-    protected void updateViewport() {
-        // save and restore the viewport size stored in java; never let the
-        // JS-side viewport dimensions override the java-side ones because
-        // java is the One True Source of this information, and allowing JS
-        // to override can lead to race conditions where this data gets clobbered.
-        FloatSize viewportSize = mViewportMetrics.getSize();
-        mGeckoViewport = mNewGeckoViewport.setViewportSize(viewportSize.width, viewportSize.height);
-
-        RectF position = mGeckoViewport.getViewport();
-        mRootLayer.setPosition(RectUtils.round(position));
-        mRootLayer.setResolution(mGeckoViewport.zoomFactor);
-
-        mLowResLayer.setPosition(RectUtils.round(position));
-        mLowResLayer.setResolution(mGeckoViewport.zoomFactor);
-
-        // Don't adjust page size when zooming unless zoom levels are
-        // approximately equal.
-        if (FloatUtils.fuzzyEquals(getViewportMetrics().zoomFactor, mGeckoViewport.zoomFactor)) {
-            setPageRect(mGeckoViewport.getPageRect(), mGeckoViewport.getCssPageRect());
-        }
+    public void endDrawing() {
+        mLowResLayer.endTransaction();
+        mRootLayer.endTransaction();
     }
 
     public void geometryChanged() {
-        sendResizeEventIfNecessary(false);
+        sendResizeEventIfNecessary();
         if (getRedrawHint()) {
             adjustViewport(null);
         }
     }
 
-    /** Implementation of LayerView.Listener */
-    @Override
-    public void renderRequested() {
-    }
-
-    /** Implementation of LayerView.Listener */
-    @Override
-    public void compositionPauseRequested() {
-    }
-
-    /** Implementation of LayerView.Listener */
-    @Override
-    public void compositionResumeRequested(int width, int height) {
-    }
-
-    /** Implementation of LayerView.Listener */
-    @Override
-    public void surfaceChanged(int width, int height) {
-        setViewportSize(new FloatSize(width, height));
-
-        // We need to make this call even when the compositor isn't currently
-        // paused (e.g. during an orientation change), to make the compositor
-        // aware of the changed surface.
-        compositionResumeRequested(width, height);
-        renderRequested();
-    }
-
-    /** Implementation of LayerView.Listener */
-    @Override
-    public void compositorCreated() {
-    }
-
     /** Implementation of PanZoomTarget */
+    @Override
     public ImmutableViewportMetrics getViewportMetrics() {
         return mViewportMetrics;
     }
 
     /** Implementation of PanZoomTarget */
+    @Override
     public ZoomConstraints getZoomConstraints() {
         return mZoomConstraints;
     }
 
     /** Implementation of PanZoomTarget */
+    @Override
     public void setAnimationTarget(ImmutableViewportMetrics viewport) {
-        if (mGeckoIsReady) {
+        if (mIsReady) {
             // We know what the final viewport of the animation is going to be, so
             // immediately request a draw of that area by setting the display port
             // accordingly. This way we should have the content pre-rendered by the
@@ -372,69 +270,52 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
     /** Implementation of PanZoomTarget
      * You must hold the monitor while calling this.
      */
+    @Override
     public void setViewportMetrics(ImmutableViewportMetrics viewport) {
         mViewportMetrics = viewport;
         mView.requestRender();
-        if (mGeckoIsReady) {
+        if (mIsReady) {
             geometryChanged();
         }
     }
 
     /** Implementation of PanZoomTarget */
+    @Override
     public void forceRedraw() {
         mForceRedraw = true;
-        if (mGeckoIsReady) {
+        if (mIsReady) {
             geometryChanged();
         }
     }
 
     /** Implementation of PanZoomTarget */
+    @Override
     public boolean post(Runnable action) {
         return mView.post(action);
     }
 
     /** Implementation of PanZoomTarget */
+    @Override
     public Object getLock() {
         return this;
     }
 
-    /** Implementation of PanZoomTarget
-     * Converts a point from layer view coordinates to layer coordinates. In other words, given a
-     * point measured in pixels from the top left corner of the layer view, returns the point in
-     * pixels measured from the last scroll position we sent to Gecko, in CSS pixels. Assuming the
-     * events being sent to Gecko are processed in FIFO order, this calculation should always be
-     * correct.
-     */
     public PointF convertViewPointToLayerPoint(PointF viewPoint) {
-        if (!mGeckoIsReady) {
-            return null;
-        }
-
         ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
         PointF origin = viewportMetrics.getOrigin();
         float zoom = viewportMetrics.zoomFactor;
-        ImmutableViewportMetrics geckoViewport = mGeckoViewport;
-        PointF geckoOrigin = geckoViewport.getOrigin();
-        float geckoZoom = geckoViewport.zoomFactor;
-
-        // viewPoint + origin gives the coordinate in device pixels from the top-left corner of the page.
-        // Divided by zoom, this gives us the coordinate in CSS pixels from the top-left corner of the page.
-        // geckoOrigin / geckoZoom is where Gecko thinks it is (scrollTo position) in CSS pixels from
-        // the top-left corner of the page. Subtracting the two gives us the offset of the viewPoint from
-        // the current Gecko coordinate in CSS pixels.
+
         PointF layerPoint = new PointF(
-                ((viewPoint.x + origin.x) / zoom) - (geckoOrigin.x / geckoZoom),
-                ((viewPoint.y + origin.y) / zoom) - (geckoOrigin.y / geckoZoom));
+                ((viewPoint.x + origin.x) / zoom),
+                ((viewPoint.y + origin.y) / zoom));
 
         return layerPoint;
     }
 
-    public void destroy() {
-        mPanZoomController.destroy();
-    }
-
-    public Context getContext() {
-        return mContext;
+    /** Implementation of PanZoomTarget */
+    @Override
+    public boolean isFullScreen() {
+        return false;
     }
 
     public void zoomTo(RectF rect) {
@@ -448,18 +329,7 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
     }
 
     public void forceRender() {
-        post(new Runnable() {
-            public void run() {
-                mView.requestRender();
-            }
-        });
-    }
-
-    private class AdjustRunnable implements Runnable {
-        public void run() {
-            mPendingViewportAdjust = false;
-            adjustViewport(null);
-        }
+        mView.requestRender();
     }
 
     /* Root Layer Access */
@@ -473,8 +343,8 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener {
         mRootLayer.clearAndReset();
     }
 
-    public void invalidateTiles(RectF rect) {
-        mLowResLayer.invalidateTiles(rect);
-        mRootLayer.invalidateTiles(rect);
+    public void invalidateTiles(List<SubTile> tilesToInvalidate, RectF rect) {
+        mLowResLayer.invalidateTiles(tilesToInvalidate, rect);
+        mRootLayer.invalidateTiles(tilesToInvalidate, rect);
     }
 }
\ No newline at end of file
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java
index 6fef53f..d460c19 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java
@@ -1,8 +1,8 @@
 package org.mozilla.gecko.gfx;
 
+import android.view.KeyEvent;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
-import android.view.KeyEvent;
 
 public interface InputConnectionHandler
 {
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
index 9440091..af3f11c 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
@@ -7,12 +7,14 @@ package org.mozilla.gecko.gfx;
 
 import android.graphics.PointF;
 import android.graphics.RectF;
+import android.os.Build;
 import android.util.FloatMath;
 import android.util.Log;
 import android.view.GestureDetector;
+import android.view.InputDevice;
 import android.view.MotionEvent;
+import android.view.View;
 
-import org.libreoffice.LOEventFactory;
 import org.libreoffice.LOKitShell;
 import org.libreoffice.LibreOfficeMainActivity;
 import org.mozilla.gecko.ZoomConstraints;
@@ -75,7 +77,7 @@ public class JavaPanZoomController
     private final SubdocumentScrollHelper mSubscroller;
     private final Axis mX;
     private final Axis mY;
-
+    private final TouchEventHandler mTouchEventHandler;
     private Thread mMainThread;
 
     /* The timer that handles flings or bounces. */
@@ -91,11 +93,12 @@ public class JavaPanZoomController
     /* Whether or not to wait for a double-tap before dispatching a single-tap */
     private boolean mWaitForDoubleTap;
 
-    public JavaPanZoomController(PanZoomTarget target) {
+    public JavaPanZoomController(PanZoomTarget target, View view) {
         mTarget = target;
         mSubscroller = new SubdocumentScrollHelper();
         mX = new AxisX(mSubscroller);
         mY = new AxisY(mSubscroller);
+        mTouchEventHandler = new TouchEventHandler(view.getContext(), view, this);
 
         mMainThread = LibreOfficeMainActivity.mAppContext.getMainLooper().getThread();
         checkMainThread();
@@ -105,6 +108,7 @@ public class JavaPanZoomController
 
     public void destroy() {
         mSubscroller.destroy();
+        mTouchEventHandler.destroy();
     }
 
     private final static float easeOut(float t) {
@@ -132,15 +136,40 @@ public class JavaPanZoomController
         }
     }
 
+    /** This function MUST be called on the UI thread */
+    public boolean onMotionEvent(MotionEvent event) {
+        if (Build.VERSION.SDK_INT <= 11) {
+            return false;
+        }
+
+        switch (event.getSource() & InputDevice.SOURCE_CLASS_MASK) {
+        case InputDevice.SOURCE_CLASS_POINTER:
+            switch (event.getAction() & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_SCROLL: return handlePointerScroll(event);
+            }
+            break;
+        }
+        return false;
+    }
+
+    /** This function MUST be called on the UI thread */
     public boolean onTouchEvent(MotionEvent event) {
+        return mTouchEventHandler.handleEvent(event);
+    }
+
+    boolean handleEvent(MotionEvent event) {
         switch (event.getAction() & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN:   return onTouchStart(event);
-            case MotionEvent.ACTION_MOVE:   return onTouchMove(event);
-            case MotionEvent.ACTION_UP:     return onTouchEnd(event);
-            case MotionEvent.ACTION_CANCEL: return onTouchCancel(event);
-            case MotionEvent.ACTION_SCROLL: return onScroll(event);
-            default:                        return false;
+        case MotionEvent.ACTION_DOWN:   return handleTouchStart(event);
+        case MotionEvent.ACTION_MOVE:   return handleTouchMove(event);
+        case MotionEvent.ACTION_UP:     return handleTouchEnd(event);
+        case MotionEvent.ACTION_CANCEL: return handleTouchCancel(event);
         }
+        return false;
+    }
+
+    /** This function MUST be called on the UI thread */
+    public void notifyDefaultActionPrevented(boolean prevented) {
+        mTouchEventHandler.handleEventListenerAction(!prevented);
     }
 
     /** This function must be called from the UI thread. */
@@ -151,24 +180,24 @@ public class JavaPanZoomController
         // snaps to edges. for other cases (where the user's finger(s) are down) don't do
         // anything special.
         switch (mState) {
-            case FLING:
-                mX.stopFling();
-                mY.stopFling();
-                // fall through
-            case BOUNCE:
-            case ANIMATED_ZOOM:
-                // the zoom that's in progress likely makes no sense any more (such as if
-                // the screen orientation changed) so abort it
-                setState(PanZoomState.NOTHING);
-                // fall through
-            case NOTHING:
-                // Don't do animations here; they're distracting and can cause flashes on page
-                // transitions.
-                synchronized (mTarget.getLock()) {
-                    mTarget.setViewportMetrics(getValidViewportMetrics());
-                    mTarget.forceRedraw();
-                }
-                break;
+        case FLING:
+            mX.stopFling();
+            mY.stopFling();
+            // fall through
+        case BOUNCE:
+        case ANIMATED_ZOOM:
+            // the zoom that's in progress likely makes no sense any more (such as if
+            // the screen orientation changed) so abort it
+            setState(PanZoomState.NOTHING);
+            // fall through
+        case NOTHING:
+            // Don't do animations here; they're distracting and can cause flashes on page
+            // transitions.
+            synchronized (mTarget.getLock()) {
+                mTarget.setViewportMetrics(getValidViewportMetrics());
+                mTarget.forceRedraw();
+            }
+            break;
         }
     }
 
@@ -213,122 +242,123 @@ public class JavaPanZoomController
      * Panning/scrolling
      */
 
-    private boolean onTouchStart(MotionEvent event) {
+    private boolean handleTouchStart(MotionEvent event) {
         // user is taking control of movement, so stop
         // any auto-movement we have going
         stopAnimationTimer();
 
         switch (mState) {
-            case ANIMATED_ZOOM:
-                // We just interrupted a double-tap animation, so force a redraw in
-                // case this touchstart is just a tap that doesn't end up triggering
-                // a redraw
-                mTarget.forceRedraw();
-                // fall through
-            case FLING:
-            case BOUNCE:
-            case NOTHING:
-            case WAITING_LISTENERS:
-                startTouch(event.getX(0), event.getY(0), event.getEventTime());
-                return false;
-            case TOUCHING:
-            case PANNING:
-            case PANNING_LOCKED:
-            case PANNING_HOLD:
-            case PANNING_HOLD_LOCKED:
-            case PINCHING:
-                Log.e(LOGTAG, "Received impossible touch down while in " + mState);
-                return false;
+        case ANIMATED_ZOOM:
+            // We just interrupted a double-tap animation, so force a redraw in
+            // case this touchstart is just a tap that doesn't end up triggering
+            // a redraw
+            mTarget.forceRedraw();
+            // fall through
+        case FLING:
+        case BOUNCE:
+        case NOTHING:
+        case WAITING_LISTENERS:
+            startTouch(event.getX(0), event.getY(0), event.getEventTime());
+            return false;
+        case TOUCHING:
+        case PANNING:
+        case PANNING_LOCKED:
+        case PANNING_HOLD:
+        case PANNING_HOLD_LOCKED:
+        case PINCHING:
+            Log.e(LOGTAG, "Received impossible touch down while in " + mState);
+            return false;
         }
-        Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchStart");
+        Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchStart");
         return false;
     }
 
-    private boolean onTouchMove(MotionEvent event) {
+    private boolean handleTouchMove(MotionEvent event) {
 
         switch (mState) {
-            case FLING:
-            case BOUNCE:
-            case WAITING_LISTENERS:
-                // should never happen
-                Log.e(LOGTAG, "Received impossible touch move while in " + mState);
-                // fall through
-            case ANIMATED_ZOOM:
-            case NOTHING:
-                // may happen if user double-taps and drags without lifting after the
-                // second tap. ignore the move if this happens.
-                return false;
+        case FLING:
+        case BOUNCE:
+        case WAITING_LISTENERS:
+            // should never happen
+            Log.e(LOGTAG, "Received impossible touch move while in " + mState);
+            // fall through
+        case ANIMATED_ZOOM:
+        case NOTHING:
+            // may happen if user double-taps and drags without lifting after the
+            // second tap. ignore the move if this happens.
+            return false;
 
-            case TOUCHING:
-                if (panDistance(event) < PAN_THRESHOLD) {
-                    return false;
-                }
-                cancelTouch();
-                startPanning(event.getX(0), event.getY(0), event.getEventTime());
-                track(event);
-                return true;
+        case TOUCHING:
+            // Don't allow panning if there is an element in full-screen mode. See bug 775511.
+            if (mTarget.isFullScreen() || panDistance(event) < PAN_THRESHOLD) {
+                return false;
+            }
+            cancelTouch();
+            startPanning(event.getX(0), event.getY(0), event.getEventTime());
+            track(event);
+            return true;
 
-            case PANNING_HOLD_LOCKED:
-                setState(PanZoomState.PANNING_LOCKED);
-                // fall through
-            case PANNING_LOCKED:
-                track(event);
-                return true;
+        case PANNING_HOLD_LOCKED:
+            setState(PanZoomState.PANNING_LOCKED);
+            // fall through
+        case PANNING_LOCKED:
+            track(event);
+            return true;
 
-            case PANNING_HOLD:
-                setState(PanZoomState.PANNING);
-                // fall through
-            case PANNING:
-                track(event);
-                return true;
+        case PANNING_HOLD:
+            setState(PanZoomState.PANNING);
+            // fall through
+        case PANNING:
+            track(event);
+            return true;
 
-            case PINCHING:
-                // scale gesture listener will handle this
-                return false;
+        case PINCHING:
+            // scale gesture listener will handle this
+            return false;
         }
-        Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchMove");
+        Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchMove");
         return false;
     }
 
-    private boolean onTouchEnd(MotionEvent event) {
+    private boolean handleTouchEnd(MotionEvent event) {
 
         switch (mState) {
-            case FLING:
-            case BOUNCE:
-            case WAITING_LISTENERS:
-                // should never happen
-                Log.e(LOGTAG, "Received impossible touch end while in " + mState);
-                // fall through
-            case ANIMATED_ZOOM:
-            case NOTHING:
-                // may happen if user double-taps and drags without lifting after the
-                // second tap. ignore if this happens.
-                return false;
+        case FLING:
+        case BOUNCE:
+        case WAITING_LISTENERS:
+            // should never happen
+            Log.e(LOGTAG, "Received impossible touch end while in " + mState);
+            // fall through
+        case ANIMATED_ZOOM:
+        case NOTHING:
+            // may happen if user double-taps and drags without lifting after the
+            // second tap. ignore if this happens.
+            return false;
 
-            case TOUCHING:
-                // the switch into TOUCHING might have happened while the page was
-                // snapping back after overscroll. we need to finish the snap if that
-                // was the case
-                bounce();
-                return false;
+        case TOUCHING:
+            // the switch into TOUCHING might have happened while the page was
+            // snapping back after overscroll. we need to finish the snap if that
+            // was the case
+            bounce();
+            return false;
 
-            case PANNING:
-            case PANNING_LOCKED:
-            case PANNING_HOLD:
-            case PANNING_HOLD_LOCKED:
-                setState(PanZoomState.FLING);
-                fling();
-                return true;
+        case PANNING:
+        case PANNING_LOCKED:
+        case PANNING_HOLD:
+        case PANNING_HOLD_LOCKED:
+            setState(PanZoomState.FLING);
+            fling();
+            return true;
 
-            case PINCHING:
-                setState(PanZoomState.NOTHING);
-                return true;
+        case PINCHING:
+            setState(PanZoomState.NOTHING);
+            return true;
         }
-        Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchEnd");
+        Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchEnd");
         return false;
     }
 
-    private boolean onTouchCancel(MotionEvent event) {
+    private boolean handleTouchCancel(MotionEvent event) {
         cancelTouch();
 
         if (mState == PanZoomState.WAITING_LISTENERS) {
@@ -346,7 +376,7 @@ public class JavaPanZoomController
         return false;
     }
 
-    private boolean onScroll(MotionEvent event) {
+    private boolean handlePointerScroll(MotionEvent event) {
         if (mState == PanZoomState.NOTHING || mState == PanZoomState.FLING) {
             float scrollX = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
             float scrollY = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
@@ -415,8 +445,8 @@ public class JavaPanZoomController
 
         for (int i = 0; i < event.getHistorySize(); i++) {
             track(event.getHistoricalX(0, i),
-                    event.getHistoricalY(0, i),
-                    event.getHistoricalEventTime(i));
+                  event.getHistoricalY(0, i),
+                  event.getHistoricalEventTime(i));
         }
         track(event.getX(0), event.getY(0), event.getEventTime());
 
@@ -792,6 +822,9 @@ public class JavaPanZoomController
 
     @Override
     public boolean onScale(SimpleScaleGestureDetector detector) {
+        if (mTarget.isFullScreen())
+            return false;
+
         if (mState != PanZoomState.PINCHING)
             return false;
 
@@ -835,7 +868,7 @@ public class JavaPanZoomController
             }
 
             scrollBy(mLastZoomFocus.x - detector.getFocusX(),
-                    mLastZoomFocus.y - detector.getFocusY());
+                     mLastZoomFocus.y - detector.getFocusY());
             PointF focus = new PointF(detector.getFocusX(), detector.getFocusY());
             scaleWithFocus(newZoomFactor, focus);
         }
@@ -868,7 +901,6 @@ public class JavaPanZoomController
         mTarget.setViewportMetrics(viewportMetrics);
     }
 
-    @Override
     public boolean getRedrawHint() {
         switch (mState) {
             case PINCHING:
@@ -901,9 +933,15 @@ public class JavaPanZoomController
         mWaitForDoubleTap = false;
     }
 
+    private PointF getMotionInDocumentCoordinates(MotionEvent motionEvent) {
+        RectF viewport = getValidViewportMetrics().getViewport();
+        PointF viewPoint = new PointF(motionEvent.getX(0), motionEvent.getY(0));
+        return mTarget.convertViewPointToLayerPoint(viewPoint);
+    }
+
     @Override
     public void onLongPress(MotionEvent motionEvent) {
-        LOKitShell.sentTouchEvent("LongPress", motionEvent);
+        LOKitShell.sentTouchEvent("LongPress", motionEvent, getMotionInDocumentCoordinates(motionEvent));
     }
 
     @Override
@@ -911,7 +949,7 @@ public class JavaPanZoomController
         // When double-tapping is allowed, we have to wait to see if this is
         // going to be a double-tap.
         if (!mWaitForDoubleTap) {
-            LOKitShell.sentTouchEvent("SingleTap", motionEvent);
+            LOKitShell.sentTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
         }
         // return false because we still want to get the ACTION_UP event that triggers this
         return false;
@@ -921,14 +959,14 @@ public class JavaPanZoomController
     public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
         // In cases where we don't wait for double-tap, we handle this in onSingleTapUp.
         if (mWaitForDoubleTap) {
-            LOKitShell.sentTouchEvent("SingleTap", motionEvent);
+            LOKitShell.sentTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
         }
         return true;
     }
 
     @Override
     public boolean onDoubleTap(MotionEvent motionEvent) {
-        LOKitShell.sentTouchEvent("DoubleTap", motionEvent);
+        LOKitShell.sentTouchEvent("DoubleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
         return true;
     }
 
@@ -984,19 +1022,16 @@ public class JavaPanZoomController
     }
 
     /** This function must be called from the UI thread. */
-    @Override
     public void abortPanning() {
         checkMainThread();
         bounce();
     }
 
-    @Override
     public void setOverScrollMode(int overscrollMode) {
         mX.setOverScrollMode(overscrollMode);
         mY.setOverScrollMode(overscrollMode);
     }
 
-    @Override
     public int getOverScrollMode() {
         return mX.getOverScrollMode();
     }
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
index 6fe97b8..b1aea36 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
@@ -5,15 +5,10 @@
 
 package org.mozilla.gecko.gfx;
 
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.RegionIterator;
 import android.opengl.GLES20;
 import android.opengl.GLSurfaceView;
 import android.os.SystemClock;
@@ -36,7 +31,6 @@ import javax.microedition.khronos.opengles.GL10;
  */
 public class LayerRenderer implements GLSurfaceView.Renderer {
     private static final String LOGTAG = "GeckoLayerRenderer";
-    private static final String PROFTAG = "GeckoLayerRendererProf";
 
     /*
      * The amount of time a frame is allowed to take to render before we declare it a dropped
@@ -44,14 +38,9 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
      */
     private static final int MAX_FRAME_TIME = 16;   /* 1000 ms / 60 FPS */
 
-    private static final int FRAME_RATE_METER_WIDTH = 128;
-    private static final int FRAME_RATE_METER_HEIGHT = 32;
-
     private final LayerView mView;
     private final SingleTileLayer mBackgroundLayer;
-    private final ScreenshotLayer mScreenshotLayer;
     private final NinePatchTileLayer mShadowLayer;
-    private TextLayer mFrameRateLayer;
     private final ScrollbarLayer mHorizScrollLayer;
     private final ScrollbarLayer mVertScrollLayer;
     private final FadeRunnable mFadeRunnable;
@@ -63,16 +52,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
 
     private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
 
-    // Dropped frames display
-    private int[] mFrameTimings;
-    private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames;
-
-    // Render profiling output
-    private int mFramesRendered;
-    private float mCompleteFramesRendered;
-    private boolean mProfileRender;
-    private long mProfileOutputTime;
-
     /* Used by robocop for testing purposes */
     private IntBuffer mPixelBuffer;
 
@@ -126,44 +105,12 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
         "    gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
         "}\n";
 
-    public void setCheckerboardBitmap(Bitmap bitmap, float pageWidth, float pageHeight) {
-        mScreenshotLayer.setBitmap(bitmap);
-        mScreenshotLayer.beginTransaction();
-        try {
-            mScreenshotLayer.setPosition(new Rect(0, 0, Math.round(pageWidth),
-                                                    Math.round(pageHeight)));
-            mScreenshotLayer.invalidate();
-        } finally {
-            mScreenshotLayer.endTransaction();
-        }
-    }
-
-    public void updateCheckerboardBitmap(Bitmap bitmap, float x, float y,
-                                         float width, float height,
-                                         float pageWidth, float pageHeight) {
-        mScreenshotLayer.updateBitmap(bitmap, x, y, width, height);
-        mScreenshotLayer.beginTransaction();
-        try {
-            mScreenshotLayer.setPosition(new Rect(0, 0, Math.round(pageWidth),
-                                                    Math.round(pageHeight)));
-            mScreenshotLayer.invalidate();
-        } finally {
-            mScreenshotLayer.endTransaction();
-        }
-    }
-
-    public void resetCheckerboard() {
-        mScreenshotLayer.reset();
-    }
-
     public LayerRenderer(LayerView view) {
         mView = view;
 
         CairoImage backgroundImage = new BufferedCairoImage(view.getBackgroundPattern());
         mBackgroundLayer = new SingleTileLayer(true, backgroundImage);
 
-        mScreenshotLayer = ScreenshotLayer.create();
-
         CairoImage shadowImage = new BufferedCairoImage(view.getShadowPattern());
         mShadowLayer = new NinePatchTileLayer(shadowImage);
 
@@ -171,9 +118,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
         mVertScrollLayer = ScrollbarLayer.create(this, true);
         mFadeRunnable = new FadeRunnable();
 
-        mFrameTimings = new int[60];
-        mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0;
-
         // Initialize the FloatBuffer that will be used to store all vertices and texture
         // coordinates in draw() commands.
         mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4);
@@ -196,18 +140,13 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
         DirectBufferAllocator.free(mCoordByteBuffer);
         mCoordByteBuffer = null;
         mCoordBuffer = null;
-        mScreenshotLayer.destroy();
         mBackgroundLayer.destroy();
         mShadowLayer.destroy();
         mHorizScrollLayer.destroy();
         mVertScrollLayer.destroy();
-        if (mFrameRateLayer != null) {
-            mFrameRateLayer.destroy();
-        }
     }
 
     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
-        checkMonitoringEnabled();
         createDefaultProgram();
         activateDefaultProgram();
     }
@@ -292,27 +231,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
         }
     }
 
-    private void printCheckerboardStats() {
-        Log.d(PROFTAG, "Frames rendered over last 1000ms: " + mCompleteFramesRendered + "/" + mFramesRendered);
-        mFramesRendered = 0;
-        mCompleteFramesRendered = 0;
-    }
-
-    /** Used by robocop for testing purposes. Not for production use! */
-    IntBuffer getPixels() {
-        IntBuffer pixelBuffer = IntBuffer.allocate(mView.getWidth() * mView.getHeight());
-        synchronized (pixelBuffer) {
-            mPixelBuffer = pixelBuffer;
-            mView.requestRender();
-            try {
-                pixelBuffer.wait();
-            } catch (InterruptedException ie) {
-            }
-            mPixelBuffer = null;
-        }
-        return pixelBuffer;
-    }
-
     private RenderContext createScreenContext(ImmutableViewportMetrics metrics) {
         RectF viewport = new RectF(0.0f, 0.0f, metrics.getWidth(), metrics.getHeight());
         RectF pageRect = new RectF(metrics.getPageRect());
@@ -333,64 +251,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
 
     public void onSurfaceChanged(GL10 gl, final int width, final int height) {
         GLES20.glViewport(0, 0, width, height);
-
-        if (mFrameRateLayer != null) {
-            moveFrameRateLayer(width, height);
-        }
-
-        /* TODO: Throw away tile images? */
-    }
-
-    private void updateDroppedFrames(long frameStartTime) {
-        int frameElapsedTime = (int)(SystemClock.uptimeMillis() - frameStartTime);
-
-        /* Update the running statistics. */
-        mFrameTimingsSum -= mFrameTimings[mCurrentFrame];
-        mFrameTimingsSum += frameElapsedTime;

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list