diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 5856f68cf35d..c23b04a385c9 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -46,6 +46,7 @@ import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.PlaceholderLayerClient; import org.mozilla.gecko.gfx.RectUtils; +import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.Tab.HistoryEntry; import java.io.*; @@ -514,6 +515,10 @@ abstract public class GeckoApp return file.toString(); } + public SharedPreferences getPlaceholderPrefs() { + return getSharedPreferences("GeckoApp", MODE_PRIVATE); + } + private void rememberLastScreen(boolean sync) { if (mUserDefinedProfile) return; @@ -526,7 +531,14 @@ abstract public class GeckoApp if (lastHistoryEntry == null) return; - SharedPreferences prefs = getSharedPreferences("GeckoApp", 0); + if (getLayerController().getLayerClient() != mSoftwareLayerClient) + return; + + IntSize pageSize = getLayerController().getPageSize(); + Rect visibleArea = getLayerController().getViewport(); + Point offset = getLayerController().getViewportMetrics().getViewportOffset(); + + SharedPreferences prefs = getPlaceholderPrefs(); Editor editor = prefs.edit(); String uri = lastHistoryEntry.mUri; @@ -535,7 +547,6 @@ abstract public class GeckoApp String lastUri = prefs.getString("last-uri", ""); String lastTitle = prefs.getString("last-title", uri); - // see if we can bail. if (uri.equals(lastUri) && title.equals(lastTitle)) return; @@ -543,6 +554,23 @@ abstract public class GeckoApp editor.putString("last-uri", uri); editor.putString("last-title", title); + if (pageSize != null) { + editor.putInt("page-width", pageSize.width); + editor.putInt("page-height", pageSize.height); + } + + if (visibleArea != null) { + editor.putInt("viewport-left", visibleArea.left); + editor.putInt("viewport-top", visibleArea.top); + editor.putInt("viewport-right", visibleArea.right); + editor.putInt("viewport-bottom", visibleArea.bottom); + } + + if (offset != null) { + editor.putInt("viewport-offset-x", offset.x); + editor.putInt("viewport-offset-y", offset.y); + } + Log.i(LOGTAG, "Saving:: " + uri + " " + title); editor.commit(); @@ -820,16 +848,6 @@ abstract public class GeckoApp GeckoAppShell.sendEventToGecko(getPrefsEvent); connectGeckoLayerClient(); - } else if (event.equals("PanZoom:Resize")) { - IntSize size = new IntSize(message.getJSONObject("size")); - int layoutWidth = mGeckoLayout.getMeasuredWidth(); - int layoutHeight = mGeckoLayout.getMeasuredHeight(); - // increase page size if smaller than layout dimensions - if (size.width < layoutWidth || size.height < layoutHeight) { - size = new IntSize(Math.max(size.width, layoutWidth), - Math.max(size.height, layoutHeight)); - } - mSoftwareLayerClient.setPageSize(size); } else if (event.equals("ToggleChrome:Hide")) { mMainHandler.post(new Runnable() { public void run() { @@ -843,8 +861,8 @@ abstract public class GeckoApp } }); } - } catch (Exception e) { - Log.i(LOGTAG, "handleMessage throws " + e + " for message: " + event); + } catch (Exception e) { + Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); } } @@ -887,6 +905,7 @@ abstract public class GeckoApp final Tab tab = Tabs.getInstance().addTab(tabId, uri); if (selected) { Tabs.getInstance().selectTab(tabId); + mSoftwareLayerClient.geckoLoadedNewContent(); } mMainHandler.post(new Runnable() { @@ -919,6 +938,8 @@ abstract public class GeckoApp if (tab == null) return; + mSoftwareLayerClient.geckoLoadedNewContent(); + mMainHandler.post(new Runnable() { public void run() { if (Tabs.getInstance().isSelectedTab(tab)) { @@ -994,6 +1015,8 @@ abstract public class GeckoApp if (Tabs.getInstance().isSelectedTab(tab)) mBrowserToolbar.setTitle(tab.getDisplayTitle()); onTabsChanged(tab); + + mSoftwareLayerClient.geckoLoadedNewContent(); } }); } @@ -1044,7 +1067,7 @@ abstract public class GeckoApp if (mGeckoLayout.indexOfChild(view) == -1) { lp = new PluginLayoutParams((int) w, (int) h, (int)x, (int)y); - lp.repositionFromVisibleRect(mLayerController.getVisibleRect(), mLayerController.getZoomFactor(), true); + lp.repositionFromVisibleRect(mLayerController.getViewport(), 1.0f/*mLayerController.getZoomFactor()*/, true); view.setWillNotDraw(false); if (view instanceof SurfaceView) { @@ -1059,7 +1082,7 @@ abstract public class GeckoApp } else { lp = (PluginLayoutParams)view.getLayoutParams(); lp.reset((int)x, (int)y, (int)w, (int)h); - lp.repositionFromVisibleRect(mLayerController.getVisibleRect(), mLayerController.getZoomFactor(), true); + lp.repositionFromVisibleRect(mLayerController.getViewport(), 1.0f/*mLayerController.getZoomFactor()*/, true); try { mGeckoLayout.updateViewLayout(view, lp); } catch (IllegalArgumentException e) { @@ -1101,7 +1124,7 @@ abstract public class GeckoApp public void repositionPluginViews(boolean resize, boolean setVisible) { for (View view : mPluginViews) { PluginLayoutParams lp = (PluginLayoutParams)view.getLayoutParams(); - lp.repositionFromVisibleRect(mLayerController.getVisibleRect(), mLayerController.getZoomFactor(), resize); + lp.repositionFromVisibleRect(mLayerController.getViewport(), 1.0f/*mLayerController.getZoomFactor()*/, resize); if (setVisible) { view.setVisibility(View.VISIBLE); @@ -1241,7 +1264,6 @@ abstract public class GeckoApp GeckoAppShell.registerGeckoEventListener("Preferences:Data", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("Gecko:Ready", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("Toast:Show", GeckoApp.mAppContext); - GeckoAppShell.registerGeckoEventListener("PanZoom:Resize", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext); @@ -1909,7 +1931,7 @@ abstract public class GeckoApp height = originalHeight = aHeight; } - public void repositionFromVisibleRect(RectF rect, float zoomFactor, boolean resize) { + public void repositionFromVisibleRect(Rect rect, float zoomFactor, boolean resize) { x = (int)((originalX - rect.left) * zoomFactor); y = (int)((originalY - rect.top) * zoomFactor); diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 1e7d6927d413..eb7afcfdc7ee 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -477,10 +477,7 @@ public class GeckoAppShell } }); - GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED, - LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT, - LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT); - GeckoAppShell.sendEventToGecko(event); + layerController.notifyLayerClientOfGeometryChange(); } private static void sendPendingEventsToGecko() { try { diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index eb1ced4760d2..1e85921b4e2d 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -94,8 +94,8 @@ JAVAFILES = \ gfx/TextureReaper.java \ gfx/TextLayer.java \ gfx/TileLayer.java \ + gfx/ViewportMetrics.java \ ui/PanZoomController.java \ - ui/ViewportController.java \ $(NULL) PROCESSEDJAVAFILES = \ diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index ca6e8c4209a0..921212503c90 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -20,6 +20,7 @@ * * Contributor(s): * Patrick Walton + * Chris Lord * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -43,7 +44,6 @@ import org.mozilla.gecko.gfx.LayerClient; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerRenderer; import org.mozilla.gecko.gfx.SingleTileLayer; -import org.mozilla.gecko.ui.ViewportController; import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; @@ -52,6 +52,7 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; +import android.util.DisplayMetrics; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; @@ -66,14 +67,21 @@ import java.util.TimerTask; * TODO: Throttle down Gecko's priority when we pan and zoom. */ public class GeckoSoftwareLayerClient extends LayerClient { + private static final String LOGTAG = "GeckoSoftwareLayerClient"; + private Context mContext; private int mWidth, mHeight, mFormat; + private IntSize mScreenSize, mViewportSize; private ByteBuffer mBuffer; private final SingleTileLayer mTileLayer; - private ViewportController mViewportController; /* The viewport rect that Gecko is currently displaying. */ - private RectF mGeckoVisibleRect; + private ViewportMetrics mGeckoViewport; + + /* Gecko has loaded new content, let its viewport metrics completely + * override the LayerController on the next draw. + */ + private boolean mNewContent; private CairoImage mCairoImage; @@ -81,20 +89,16 @@ public class GeckoSoftwareLayerClient extends LayerClient { private long mLastViewportChangeTime; private Timer mViewportRedrawTimer; - /* The initial page width and height that we use before a page is loaded. */ - private static final int PAGE_WIDTH = 980; /* Matches MobileSafari. */ - private static final int PAGE_HEIGHT = 1500; - public GeckoSoftwareLayerClient(Context context) { mContext = context; - mViewportController = new ViewportController(new IntSize(PAGE_WIDTH, PAGE_HEIGHT), - new RectF(0, 0, 1, 1)); - mWidth = LayerController.TILE_WIDTH; mHeight = LayerController.TILE_HEIGHT; mFormat = CairoImage.FORMAT_RGB16_565; + mScreenSize = new IntSize(1, 1); + mNewContent = true; + mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2); mCairoImage = new CairoImage() { @@ -113,9 +117,13 @@ public class GeckoSoftwareLayerClient extends LayerClient { /** Attaches the root layer to the layer controller so that Gecko appears. */ @Override - public void init() { - getLayerController().setRoot(mTileLayer); - render(); + public void setLayerController(LayerController layerController) { + super.setLayerController(layerController); + + layerController.setRoot(mTileLayer); + if (mGeckoViewport != null) + layerController.setViewportMetrics(mGeckoViewport); + geometryChanged(); } public void beginDrawing() { @@ -131,13 +139,22 @@ public class GeckoSoftwareLayerClient extends LayerClient { LayerController controller = getLayerController(); if (controller == null) return; - controller.notifyViewOfGeometryChange(); try { JSONObject metadataObject = new JSONObject(metadata); - float originX = (float)metadataObject.getDouble("x"); - float originY = (float)metadataObject.getDouble("y"); - mTileLayer.setOrigin(new PointF(originX, originY)); + mGeckoViewport = new ViewportMetrics(metadataObject); + + mTileLayer.setOrigin(mGeckoViewport.getDisplayportOrigin()); + + if (controller != null) { + if (mNewContent) { + mNewContent = false; + controller.setViewportMetrics(mGeckoViewport); + } else { + controller.setPageSize(mGeckoViewport.getPageSize()); + } + + } } catch (JSONException e) { throw new RuntimeException(e); } @@ -149,6 +166,10 @@ public class GeckoSoftwareLayerClient extends LayerClient { } } + public void geckoLoadedNewContent() { + mNewContent = true; + } + /** Returns the back buffer. This function is for Gecko to use. */ public ByteBuffer lockBuffer() { return mBuffer; @@ -162,21 +183,25 @@ public class GeckoSoftwareLayerClient extends LayerClient { /* no-op */ } - /** Called whenever the page changes size. */ - public void setPageSize(IntSize pageSize) { - mViewportController.setPageSize(pageSize); - getLayerController().setPageSize(pageSize); - } - @Override public void geometryChanged() { - mViewportController.setVisibleRect(getTransformedVisibleRect()); + /* Let Gecko know if the screensize has changed */ + DisplayMetrics metrics = new DisplayMetrics(); + GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); + + if (metrics.widthPixels != mScreenSize.width || + metrics.heightPixels != mScreenSize.height) { + mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels); + Log.i(LOGTAG, "Screen-size changed to " + mScreenSize); + GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED, + LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT, + metrics.widthPixels, metrics.heightPixels); + GeckoAppShell.sendEventToGecko(event); + } + render(); } - @Override - public IntSize getPageSize() { return mViewportController.getPageSize(); } - @Override public void render() { adjustViewportWithThrottling(); @@ -211,24 +236,14 @@ public class GeckoSoftwareLayerClient extends LayerClient { } private void adjustViewport() { - LayerController layerController = getLayerController(); - RectF visibleRect = layerController.getVisibleRect(); - RectF tileRect = mViewportController.widenRect(visibleRect); - tileRect = mViewportController.clampRect(tileRect); + ViewportMetrics viewportMetrics = getLayerController().getViewportMetrics(); + Point viewportOffset = viewportMetrics.getOptimumViewportOffset(); + viewportMetrics.setViewportOffset(viewportOffset); - int x = (int)Math.round(tileRect.left), y = (int)Math.round(tileRect.top); - GeckoEvent event = new GeckoEvent("Viewport:Change", "{\"x\": " + x + - ", \"y\": " + y + "}"); + GeckoEvent event = new GeckoEvent("Viewport:Change", viewportMetrics.toJSON()); GeckoAppShell.sendEventToGecko(event); mLastViewportChangeTime = System.currentTimeMillis(); } - - /* Returns the dimensions of the box in page coordinates that the user is viewing. */ - private RectF getTransformedVisibleRect() { - LayerController layerController = getLayerController(); - return mViewportController.transformVisibleRect(layerController.getVisibleRect(), - layerController.getPageSize()); - } } diff --git a/mobile/android/base/gfx/IntSize.java b/mobile/android/base/gfx/IntSize.java index d84788b40841..1367b4de10e0 100644 --- a/mobile/android/base/gfx/IntSize.java +++ b/mobile/android/base/gfx/IntSize.java @@ -43,6 +43,7 @@ import org.json.JSONObject; public class IntSize { public final int width, height; + public IntSize(IntSize size) { width = size.width; height = size.height; } public IntSize(int inWidth, int inHeight) { width = inWidth; height = inHeight; } public IntSize(JSONObject json) { @@ -54,6 +55,10 @@ public class IntSize { } } + public boolean equals(IntSize size) { + return ((size.width == width) && (size.height == height)); + } + @Override public String toString() { return "(" + width + "," + height + ")"; } diff --git a/mobile/android/base/gfx/Layer.java b/mobile/android/base/gfx/Layer.java index f604d4f4bc12..04ea9c4512dd 100644 --- a/mobile/android/base/gfx/Layer.java +++ b/mobile/android/base/gfx/Layer.java @@ -37,7 +37,7 @@ package org.mozilla.gecko.gfx; -import android.graphics.PointF; +import android.graphics.Point; import android.util.Log; import java.util.concurrent.locks.ReentrantLock; import javax.microedition.khronos.opengles.GL10; @@ -45,12 +45,12 @@ import javax.microedition.khronos.opengles.GL10; public abstract class Layer { private final ReentrantLock mTransactionLock; private boolean mInTransaction; - private PointF mOrigin; - private PointF mNewOrigin; + private Point mOrigin; + private Point mNewOrigin; public Layer() { mTransactionLock = new ReentrantLock(); - mOrigin = new PointF(0.0f, 0.0f); + mOrigin = new Point(0, 0); } /** Draws the layer. Automatically applies the translation. */ @@ -104,12 +104,12 @@ public abstract class Layer { } /** Returns the current layer origin. */ - public PointF getOrigin() { + public Point getOrigin() { return mOrigin; } /** Sets the origin. Only valid inside a transaction. */ - public void setOrigin(PointF newOrigin) { + public void setOrigin(Point newOrigin) { if (!mInTransaction) throw new RuntimeException("setOrigin() is only valid inside a transaction"); mNewOrigin = newOrigin; diff --git a/mobile/android/base/gfx/LayerClient.java b/mobile/android/base/gfx/LayerClient.java index 73d13202b0d5..2f191d570a76 100644 --- a/mobile/android/base/gfx/LayerClient.java +++ b/mobile/android/base/gfx/LayerClient.java @@ -37,9 +37,6 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.gfx.IntSize; -import org.mozilla.gecko.gfx.LayerController; - /** * A layer client provides tiles and manages other information used by the layer controller. */ @@ -47,12 +44,6 @@ public abstract class LayerClient { private LayerController mLayerController; public abstract void geometryChanged(); - public abstract IntSize getPageSize(); - - /** Called whenever the page changes size. */ - public abstract void setPageSize(IntSize pageSize); - - public abstract void init(); protected abstract void render(); public LayerController getLayerController() { return mLayerController; } diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index a582d2cc44bc..c36fddf23a10 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -20,6 +20,7 @@ * * Contributor(s): * Patrick Walton + * Chris Lord * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -47,6 +48,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -55,6 +57,7 @@ import android.view.MotionEvent; import android.view.GestureDetector; import android.view.ScaleGestureDetector; import android.view.View.OnTouchListener; +import java.lang.Math; import java.util.ArrayList; /** @@ -66,9 +69,7 @@ public class LayerController { private Layer mRootLayer; /* The root layer. */ private LayerView mView; /* The main rendering view. */ private Context mContext; /* The current context. */ - private RectF mVisibleRect; /* The current visible region. */ - private IntSize mScreenSize; /* The screen size of the viewport. */ - private IntSize mPageSize; /* The current page size. */ + private ViewportMetrics mViewportMetrics; /* The current viewport metrics. */ private PanZoomController mPanZoomController; /* @@ -79,24 +80,19 @@ public class LayerController { private OnTouchListener mOnTouchListener; /* The touch listener. */ private LayerClient mLayerClient; /* The layer client. */ + /* NB: These must be powers of two due to the OpenGL ES 1.x restriction on NPOT textures. */ public static final int TILE_WIDTH = 1024; public static final int TILE_HEIGHT = 2048; - /* NB: These must be powers of two due to the OpenGL ES 1.x restriction on NPOT textures. */ - private static final int DANGER_ZONE_X = 150; - private static final int DANGER_ZONE_Y = 300; /* If the visible rect is within the danger zone (measured in pixels from each edge of a tile), * we start aggressively redrawing to minimize checkerboarding. */ + private static final int DANGER_ZONE_X = 150; + private static final int DANGER_ZONE_Y = 300; public LayerController(Context context) { mContext = context; - mVisibleRect = new RectF(0.0f, 0.0f, 1.0f, 1.0f); - /* Gets filled in when the surface changes. */ - - mScreenSize = new IntSize(1, 1); - mPageSize = new IntSize(LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT); - + mViewportMetrics = new ViewportMetrics(); mPanZoomController = new PanZoomController(this); mView = new LayerView(context, this); } @@ -105,17 +101,27 @@ public class LayerController { public void setLayerClient(LayerClient layerClient) { mLayerClient = layerClient; - mPageSize = layerClient.getPageSize(); layerClient.setLayerController(this); - layerClient.init(); } - public Layer getRoot() { return mRootLayer; } - public LayerView getView() { return mView; } - public Context getContext() { return mContext; } - public RectF getVisibleRect() { return mVisibleRect; } - public IntSize getScreenSize() { return mScreenSize; } - public IntSize getPageSize() { return mPageSize; } + public LayerClient getLayerClient() { return mLayerClient; } + public Layer getRoot() { return mRootLayer; } + public LayerView getView() { return mView; } + public Context getContext() { return mContext; } + public ViewportMetrics getViewportMetrics() { return mViewportMetrics; } + + public Rect getViewport() { + return mViewportMetrics.getViewport(); + } + + public IntSize getViewportSize() { + Rect viewport = getViewport(); + return new IntSize(viewport.width(), viewport.height()); + } + + public IntSize getPageSize() { + return mViewportMetrics.getPageSize(); + } public Bitmap getCheckerboardPattern() { return getDrawable("checkerboard"); } public Bitmap getShadowPattern() { return getDrawable("shadow"); } @@ -131,60 +137,51 @@ public class LayerController { return BitmapFactory.decodeResource(mContext.getResources(), resourceID, options); } - /* - * Note that the zoom factor of the layer controller differs from the zoom factor of the layer - * client (i.e. the page). - */ - public float getZoomFactor() { return (float)mScreenSize.width / mVisibleRect.width(); } - /** - * The view calls this to indicate that the screen changed size. + * The view calls this to indicate that the viewport changed size. * * TODO: Refactor this to use an interface. Expose that interface only to the view and not * to the layer client. That way, the layer client won't be tempted to call this, which might * result in an infinite loop. */ - public void setScreenSize(int width, int height) { - float zoomFactor = getZoomFactor(); /* Must come first. */ - - mScreenSize = new IntSize(width, height); - setVisibleRect(mVisibleRect.left, mVisibleRect.top, width / zoomFactor, - height / zoomFactor); + public void setViewportSize(IntSize size) { + mViewportMetrics.setSize(size); notifyLayerClientOfGeometryChange(); + mPanZoomController.geometryChanged(); } - public void setNeedsDisplay() { - // TODO + public void scrollTo(PointF point) { + mViewportMetrics.setOrigin(new Point(Math.round(point.x), + Math.round(point.y))); + + notifyLayerClientOfGeometryChange(); + mPanZoomController.geometryChanged(); } - public void scrollTo(float x, float y) { - setVisibleRect(x, y, mVisibleRect.width(), mVisibleRect.height()); - } - - public void setVisibleRect(float x, float y, float width, float height) { - mVisibleRect = new RectF(x, y, x + width, y + height); - setNeedsDisplay(); - GeckoApp.mAppContext.repositionPluginViews(false); - } - - /** - * Sets the zoom factor to 1, adjusting the visible rect accordingly. The Gecko layer client - * calls this function after a zoom has completed and Gecko is done rendering the new visible - * region. - */ - public void unzoom() { - float zoomFactor = getZoomFactor(); - float x = Math.round(mVisibleRect.left * zoomFactor); - float y = Math.round(mVisibleRect.top * zoomFactor); - mVisibleRect = new RectF(x, y, x + mScreenSize.width, y + mScreenSize.height); - mPageSize = mPageSize.scale(zoomFactor); - setNeedsDisplay(); + public void setViewport(Rect viewport) { + mViewportMetrics.setViewport(viewport); + notifyLayerClientOfGeometryChange(); + mPanZoomController.geometryChanged(); } public void setPageSize(IntSize size) { - mPageSize = size.scale(getZoomFactor()); - mView.notifyRendererOfPageSizeChange(); + if (mViewportMetrics.getPageSize().equals(size)) + return; + + mViewportMetrics.setPageSize(size); + + // Page size is owned by the LayerClient, so no need to notify it of + // this change. + mPanZoomController.geometryChanged(); + } + + public void setViewportMetrics(ViewportMetrics viewport) { + mViewportMetrics = new ViewportMetrics(viewport); + + // We assume this was called by the LayerClient (as it includes page + // size), so no need to notify it of this change. + mPanZoomController.geometryChanged(); } public boolean post(Runnable action) { return mView.post(action); } @@ -202,12 +199,6 @@ public class LayerController { mLayerClient.geometryChanged(); } - // Informs the view and the panning and zooming controller that the geometry changed. - public void notifyViewOfGeometryChange() { - mView.geometryChanged(); - mPanZoomController.geometryChanged(); - } - /** * Returns true if this controller is fine with performing a redraw operation or false if it * would prefer that the action didn't take place. @@ -223,38 +214,9 @@ public class LayerController { // Returns true if a checkerboard is about to be visible. private boolean aboutToCheckerboard() { - Rect pageRect = new Rect(0, 0, mPageSize.width, mPageSize.height); - Rect adjustedPageRect = RectUtils.contract(pageRect, DANGER_ZONE_X, DANGER_ZONE_Y); - RectF visiblePageRect = RectUtils.intersect(mVisibleRect, new RectF(adjustedPageRect)); - RectF adjustedTileRect = RectUtils.contract(getTileRect(), DANGER_ZONE_X, DANGER_ZONE_Y); - return !adjustedTileRect.contains(visiblePageRect); - } - - /** Returns the given rect, clamped to the boundaries of a tile. */ - public RectF clampRect(RectF rect) { - float x = clamp(0, rect.left, mPageSize.width - LayerController.TILE_WIDTH); - float y = clamp(0, rect.top, mPageSize.height - LayerController.TILE_HEIGHT); - return new RectF(x, y, x + rect.width(), y + rect.height()); - } - - private float clamp(float min, float value, float max) { - if (max < min) - return min; - return (value < min) ? min : (value > max) ? max : value; - } - - // Returns the coordinates of a tile, scaled by the given factor, centered on the given rect. - private static RectF widenRect(RectF rect, float scaleFactor) { - PointF center = new PointF(rect.centerX(), rect.centerY()); - float halfTileWidth = TILE_WIDTH * scaleFactor / 2.0f; - float halfTileHeight = TILE_HEIGHT * scaleFactor / 2.0f; - return new RectF(rect.centerX() - halfTileWidth, rect.centerY() - halfTileHeight, - rect.centerX() + halfTileWidth, rect.centerY() + halfTileHeight); - } - - /** Returns the coordinates of a tile centered on the given rect. */ - public static RectF widenRect(RectF rect) { - return widenRect(rect, 1.0f); + RectF adjustedTileRect = + RectUtils.contract(getTileRect(), DANGER_ZONE_X, DANGER_ZONE_Y); + return !adjustedTileRect.contains(new RectF(mViewportMetrics.getViewport())); } /** @@ -269,17 +231,19 @@ public class LayerController { return null; // Undo the transforms. - PointF scaledPoint = PointUtils.scale(viewPoint, 1.0f / getZoomFactor()); - return PointUtils.subtract(PointUtils.add(new PointF(mVisibleRect.left, mVisibleRect.top), - scaledPoint), - mRootLayer.getOrigin()); + PointF newPoint = new PointF(mViewportMetrics.getOrigin()); + newPoint.offset(viewPoint.x, viewPoint.y); + + Point rootOrigin = mRootLayer.getOrigin(); + newPoint.offset(-rootOrigin.x, -rootOrigin.y); + + return newPoint; } /* * Gesture detection. This is handled only at a high level in this class; we dispatch to the * pan/zoom controller to do the dirty work. */ - public boolean onTouchEvent(MotionEvent event) { if (mPanZoomController.onTouchEvent(event)) return true; diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index 71b8f3bf806d..5d0a90a6021b 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -20,6 +20,7 @@ * * Contributor(s): * Patrick Walton + * Chris Lord * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -119,7 +120,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { /* Draw the checkerboard. */ Rect clampedPageRect = clampToScreen(pageRect); - IntSize screenSize = controller.getScreenSize(); + IntSize screenSize = controller.getViewportSize(); gl.glEnable(GL10.GL_SCISSOR_TEST); gl.glScissor(clampedPageRect.left, screenSize.height - clampedPageRect.bottom, clampedPageRect.width(), clampedPageRect.height()); @@ -155,28 +156,24 @@ public class LayerRenderer implements GLSurfaceView.Renderer { gl.glDisable(GL10.GL_BLEND); } - public void pageSizeChanged() { - mShadowLayer.recreateVertexBuffers(); - } - private void setupPageTransform(GL10 gl) { LayerController controller = mView.getController(); - RectF visibleRect = controller.getVisibleRect(); - float zoomFactor = controller.getZoomFactor(); + Rect viewport = controller.getViewport(); + //float zoomFactor = controller.getZoomFactor(); gl.glLoadIdentity(); - gl.glScalef(zoomFactor, zoomFactor, 1.0f); - gl.glTranslatef(-visibleRect.left, -visibleRect.top, 0.0f); + //gl.glScalef(zoomFactor, zoomFactor, 1.0f); + gl.glTranslatef(-viewport.left, -viewport.top, 0.0f); } private Rect getPageRect() { LayerController controller = mView.getController(); - float zoomFactor = controller.getZoomFactor(); - RectF visibleRect = controller.getVisibleRect(); + float zoomFactor = 1.0f;//controller.getZoomFactor(); + Rect viewport = controller.getViewport(); IntSize pageSize = controller.getPageSize(); - int x = (int)Math.round(-zoomFactor * visibleRect.left); - int y = (int)Math.round(-zoomFactor * visibleRect.top); + int x = (int)Math.round(-zoomFactor * viewport.left); + int y = (int)Math.round(-zoomFactor * viewport.top); return new Rect(x, y, x + (int)Math.round(zoomFactor * pageSize.width), y + (int)Math.round(zoomFactor * pageSize.height)); @@ -184,7 +181,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private Rect clampToScreen(Rect rect) { LayerController controller = mView.getController(); - IntSize screenSize = controller.getScreenSize(); + IntSize screenSize = controller.getViewportSize(); int left = Math.max(0, rect.left); int top = Math.max(0, rect.top); @@ -205,7 +202,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { // done on the main UI thread, not the GL renderer thread mView.post(new Runnable() { public void run() { - mView.setScreenSize(width, height); + mView.setViewportSize(new IntSize(width, height)); } }); diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java index 0c479f22f5f5..48066d300839 100644 --- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -88,15 +88,10 @@ public class LayerView extends GLSurfaceView { } public LayerController getController() { return mController; } - public void geometryChanged() { /* TODO: Schedule a redraw. */ } - - public void notifyRendererOfPageSizeChange() { - mRenderer.pageSizeChanged(); - } /** The LayerRenderer calls this to indicate that the window has changed size. */ - public void setScreenSize(int width, int height) { - mController.setScreenSize(width, height); + public void setViewportSize(IntSize size) { + mController.setViewportSize(size); } public void setInputConnectionHandler(InputConnectionHandler handler) { diff --git a/mobile/android/base/gfx/NinePatchTileLayer.java b/mobile/android/base/gfx/NinePatchTileLayer.java index cc5303bfc6ae..cc89f52667a5 100644 --- a/mobile/android/base/gfx/NinePatchTileLayer.java +++ b/mobile/android/base/gfx/NinePatchTileLayer.java @@ -50,6 +50,7 @@ public class NinePatchTileLayer extends TileLayer { private FloatBuffer mSideTexCoordBuffer, mSideVertexBuffer; private FloatBuffer mTopTexCoordBuffer, mTopVertexBuffer; private LayerController mLayerController; + private IntSize mPageSize; private static final int PATCH_SIZE = 16; private static final int TEXTURE_SIZE = 48; @@ -90,6 +91,7 @@ public class NinePatchTileLayer extends TileLayer { public NinePatchTileLayer(LayerController layerController, CairoImage image) { super(false, image); + mPageSize = new IntSize(1, 1); mLayerController = layerController; mSideTexCoordBuffer = createBuffer(SIDE_TEX_COORDS); @@ -99,24 +101,22 @@ public class NinePatchTileLayer extends TileLayer { } public void recreateVertexBuffers() { - IntSize pageSize = mLayerController.getPageSize(); - float[] sideVertices = { -PATCH_SIZE, -PATCH_SIZE, 0.0f, 0.0f, -PATCH_SIZE, 0.0f, -PATCH_SIZE, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - -PATCH_SIZE, pageSize.height, 0.0f, - 0.0f, pageSize.height, 0.0f, - -PATCH_SIZE, PATCH_SIZE + pageSize.height, 0.0f, - 0.0f, PATCH_SIZE + pageSize.height, 0.0f + -PATCH_SIZE, mPageSize.height, 0.0f, + 0.0f, mPageSize.height, 0.0f, + -PATCH_SIZE, PATCH_SIZE + mPageSize.height, 0.0f, + 0.0f, PATCH_SIZE + mPageSize.height, 0.0f }; float[] topVertices = { - 0.0f, -PATCH_SIZE, 0.0f, - pageSize.width, -PATCH_SIZE, 0.0f, - 0.0f, 0.0f, 0.0f, - pageSize.width, 0.0f, 0.0f + 0.0f, -PATCH_SIZE, 0.0f, + mPageSize.width, -PATCH_SIZE, 0.0f, + 0.0f, 0.0f, 0.0f, + mPageSize.width, 0.0f, 0.0f }; mSideVertexBuffer = createBuffer(sideVertices); @@ -126,6 +126,10 @@ public class NinePatchTileLayer extends TileLayer { @Override protected void onTileDraw(GL10 gl) { IntSize pageSize = mLayerController.getPageSize(); + if (!pageSize.equals(mPageSize)) { + mPageSize = pageSize; + recreateVertexBuffers(); + } gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); gl.glEnable(GL10.GL_BLEND); diff --git a/mobile/android/base/gfx/PlaceholderLayerClient.java b/mobile/android/base/gfx/PlaceholderLayerClient.java index 674e888f357d..2af5aff962d7 100644 --- a/mobile/android/base/gfx/PlaceholderLayerClient.java +++ b/mobile/android/base/gfx/PlaceholderLayerClient.java @@ -44,8 +44,11 @@ import org.mozilla.gecko.gfx.LayerClient; import org.mozilla.gecko.gfx.SingleTileLayer; import org.mozilla.gecko.GeckoApp; import android.content.Context; +import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Point; +import android.graphics.Rect; import android.os.AsyncTask; import android.os.Environment; import android.util.Log; @@ -58,27 +61,33 @@ import java.nio.ByteBuffer; */ public class PlaceholderLayerClient extends LayerClient { private Context mContext; - private IntSize mPageSize; + private ViewportMetrics mViewport; private int mWidth, mHeight, mFormat; private ByteBuffer mBuffer; private FetchImageTask mTask; private PlaceholderLayerClient(Context context) { mContext = context; - mPageSize = new IntSize(995, 1250); /* TODO */ + SharedPreferences prefs = GeckoApp.mAppContext.getPlaceholderPrefs(); + IntSize pageSize = new IntSize(prefs.getInt("page-width", 995), + prefs.getInt("page-height", 1250)); + Rect viewport = new Rect(prefs.getInt("viewport-left", 0), + prefs.getInt("viewport-top", 0), + prefs.getInt("viewport-right", 1), + prefs.getInt("viewport-bottom", 1)); + Point offset = new Point(prefs.getInt("viewport-offset-x", 0), + prefs.getInt("viewport-offset-y", 0)); + + mViewport = new ViewportMetrics(); + mViewport.setPageSize(pageSize); + mViewport.setViewport(viewport); + mViewport.setViewportOffset(offset); } public static PlaceholderLayerClient createInstance(Context context) { return new PlaceholderLayerClient(context); } - public void init() { - // Until http://code.google.com/p/android/issues/detail?id=16941 - // is worked around, do not load the last screenshot as it will OOM - // mTask = new FetchImageTask(); - // mTask.execute(); - } - public void destroy() { if (mTask != null) { mTask.cancel(false); @@ -109,11 +118,16 @@ public class PlaceholderLayerClient extends LayerClient { bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer()); return null; } - + @Override protected void onPostExecute(Void unused) { BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat); SingleTileLayer tileLayer = new SingleTileLayer(image); + + tileLayer.beginTransaction(); + tileLayer.setOrigin(mViewport.getDisplayportOrigin()); + tileLayer.endTransaction(); + getLayerController().setRoot(tileLayer); } } @@ -121,12 +135,17 @@ public class PlaceholderLayerClient extends LayerClient { @Override public void geometryChanged() { /* no-op */ } @Override - public IntSize getPageSize() { return mPageSize; } - @Override public void render() { /* no-op */ } - /** Called whenever the page changes size. */ @Override - public void setPageSize(IntSize pageSize) { mPageSize = pageSize; } + public void setLayerController(LayerController layerController) { + super.setLayerController(layerController); + + layerController.setViewportMetrics(mViewport); + + BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat); + SingleTileLayer tileLayer = new SingleTileLayer(image); + layerController.setRoot(tileLayer); + } } diff --git a/mobile/android/base/gfx/ViewportMetrics.java b/mobile/android/base/gfx/ViewportMetrics.java new file mode 100644 index 000000000000..912ca5759993 --- /dev/null +++ b/mobile/android/base/gfx/ViewportMetrics.java @@ -0,0 +1,202 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009-2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Patrick Walton + * Chris Lord + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.gecko.gfx; + +import android.graphics.Point; +import android.graphics.Rect; +import org.mozilla.gecko.gfx.IntSize; +import org.mozilla.gecko.gfx.LayerController; +import org.json.JSONException; +import org.json.JSONObject; +import android.util.Log; + +/** + * ViewportMetrics manages state and contains some utility functions related to + * the page viewport for the Gecko layer client to use. + */ +public class ViewportMetrics { + private IntSize mPageSize; + private Rect mViewportRect; + private Point mViewportOffset; + + public ViewportMetrics() { + mPageSize = new IntSize(LayerController.TILE_WIDTH, + LayerController.TILE_HEIGHT); + mViewportRect = new Rect(0, 0, 1, 1); + mViewportOffset = new Point(0, 0); + } + + public ViewportMetrics(ViewportMetrics viewport) { + mPageSize = new IntSize(viewport.getPageSize()); + mViewportRect = new Rect(viewport.getViewport()); + mViewportOffset = new Point(viewport.getViewportOffset()); + } + + public ViewportMetrics(JSONObject json) throws JSONException { + int x = json.getInt("x"); + int y = json.getInt("y"); + int width = json.getInt("width"); + int height = json.getInt("height"); + int pageWidth = json.getInt("pageWidth"); + int pageHeight = json.getInt("pageHeight"); + int offsetX = json.getInt("offsetX"); + int offsetY = json.getInt("offsetY"); + + mPageSize = new IntSize(pageWidth, pageHeight); + mViewportRect = new Rect(x, y, x + width, y + height); + mViewportOffset = new Point(offsetX, offsetY); + } + + public Point getOptimumViewportOffset() { + // XXX We currently always position the viewport in the centre of the + // displayport, but we might want to optimise this during panning + // to minimise checkerboarding. + Point optimumOffset = + new Point((LayerController.TILE_WIDTH - mViewportRect.width()) / 2, + (LayerController.TILE_HEIGHT - mViewportRect.height()) / 2); + + /* XXX Until bug #524925 is fixed, changing the viewport origin will + * probably cause things to be slower than just having a smaller usable + * displayport. + */ + Rect viewport = getClampedViewport(); + + // Make sure this offset won't cause wasted pixels in the displayport + // (i.e. make sure the resultant displayport intersects with the page + // as much as possible) + if (viewport.left - optimumOffset.x < 0) + optimumOffset.x = viewport.left; + else if (optimumOffset.x + viewport.right > mPageSize.width) + optimumOffset.x -= (mPageSize.width - (optimumOffset.x + viewport.right)); + + if (viewport.top - optimumOffset.y < 0) + optimumOffset.y = viewport.top; + else if (optimumOffset.y + viewport.bottom > mPageSize.height) + optimumOffset.y -= (mPageSize.height - (optimumOffset.y + viewport.bottom)); + + return optimumOffset; + } + + public Point getOrigin() { + return new Point(mViewportRect.left, mViewportRect.top); + } + + public Point getDisplayportOrigin() { + return new Point(mViewportRect.left - mViewportOffset.x, + mViewportRect.top - mViewportOffset.y); + } + + public IntSize getSize() { + return new IntSize(mViewportRect.width(), mViewportRect.height()); + } + + public Rect getViewport() { + return mViewportRect; + } + + /** Returns the viewport rectangle, clamped within the page-size. */ + public Rect getClampedViewport() { + Rect clampedViewport = new Rect(mViewportRect); + + // While the viewport size ought to never exceed the page size, we + // do the clamping in this order to make sure that the origin is + // never negative. + if (clampedViewport.right > mPageSize.width) + clampedViewport.offset(mPageSize.width - clampedViewport.right, 0); + if (clampedViewport.left < 0) + clampedViewport.offset(-clampedViewport.left, 0); + + if (clampedViewport.bottom > mPageSize.height) + clampedViewport.offset(0, mPageSize.height - clampedViewport.bottom); + if (clampedViewport.top < 0) + clampedViewport.offset(0, -clampedViewport.top); + + return clampedViewport; + } + + public Point getViewportOffset() { + return mViewportOffset; + } + + public IntSize getPageSize() { + return mPageSize; + } + + public void setPageSize(IntSize pageSize) { + mPageSize = pageSize; + } + + public void setViewport(Rect viewport) { + mViewportRect = viewport; + } + + public void setOrigin(Point origin) { + mViewportRect.set(origin.x, origin.y, + origin.x + mViewportRect.width(), + origin.y + mViewportRect.height()); + } + + public void setSize(IntSize size) { + mViewportRect.right = mViewportRect.left + size.width; + mViewportRect.bottom = mViewportRect.top + size.height; + } + + public void setViewportOffset(Point offset) { + mViewportOffset = offset; + } + + public boolean equals(ViewportMetrics viewport) { + return mViewportRect.equals(viewport.getViewport()) && + mPageSize.equals(viewport.getPageSize()) && + mViewportOffset.equals(viewport.getViewportOffset()); + } + + public String toJSON() { + return "{ \"x\" : " + mViewportRect.left + + ", \"y\" : " + mViewportRect.top + + ", \"width\" : " + mViewportRect.width() + + ", \"height\" : " + mViewportRect.height() + + ", \"pageWidth\" : " + mPageSize.width + + ", \"pageHeight\" : " + mPageSize.height + + ", \"offsetX\" : " + mViewportOffset.x + + ", \"offsetY\" : " + mViewportOffset.y + + "}"; + } +} + diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index 09723b32ae05..995662cd5192 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -44,11 +44,13 @@ import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; import android.graphics.PointF; +import android.graphics.Rect; import android.graphics.RectF; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import java.lang.Math; import java.util.Timer; import java.util.TimerTask; @@ -266,7 +268,7 @@ public class PanZoomController } } - float zoomFactor = mController.getZoomFactor(); + float zoomFactor = 1.0f;//mController.getZoomFactor(); mX.velocity = (mX.touchPos - x) / zoomFactor; mY.velocity = (mY.touchPos - y) / zoomFactor; mX.touchPos = x; @@ -315,15 +317,13 @@ public class PanZoomController } private void updatePosition() { - mController.scrollTo(mX.viewportPos, mY.viewportPos); - mController.notifyLayerClientOfGeometryChange(); + mController.scrollTo(new PointF(mX.viewportPos, mY.viewportPos)); } // Populates the viewport info and length in the axes. private void populatePositionAndLength() { IntSize pageSize = mController.getPageSize(); - RectF visibleRect = mController.getVisibleRect(); - IntSize screenSize = mController.getScreenSize(); + RectF visibleRect = new RectF(mController.getViewport()); mX.setPageLength(pageSize.width); mX.viewportPos = visibleRect.left; @@ -603,6 +603,7 @@ public class PanZoomController */ @Override public boolean onScale(ScaleGestureDetector detector) { + /* mState = PanZoomState.PINCHING; float newZoom = detector.getCurrentSpan() / mInitialZoomSpan; @@ -614,11 +615,13 @@ public class PanZoomController mController.setVisibleRect(x, y, width, height); mController.notifyLayerClientOfGeometryChange(); populatePositionAndLength(); + */ return true; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { + /* mState = PanZoomState.PINCHING; RectF initialZoomRect = mController.getVisibleRect(); float initialZoom = mController.getZoomFactor(); @@ -628,16 +631,19 @@ public class PanZoomController mInitialZoomSpan = detector.getCurrentSpan() / initialZoom; GeckoApp.mAppContext.hidePluginViews(); + */ return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { + /* mState = PanZoomState.PANNING_HOLD_LOCKED; mX.firstTouchPos = mX.touchPos = detector.getFocusX(); mY.firstTouchPos = mY.touchPos = detector.getFocusY(); GeckoApp.mAppContext.showPluginViews(); + */ } @Override diff --git a/mobile/android/base/ui/ViewportController.java b/mobile/android/base/ui/ViewportController.java deleted file mode 100644 index 75b352a1e1e0..000000000000 --- a/mobile/android/base/ui/ViewportController.java +++ /dev/null @@ -1,110 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009-2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Patrick Walton - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko.ui; - -import android.graphics.PointF; -import android.graphics.RectF; -import org.mozilla.gecko.gfx.IntSize; -import org.mozilla.gecko.gfx.LayerController; -import org.mozilla.gecko.gfx.RectUtils; - -/** Manages the dimensions of the page viewport. */ -public class ViewportController { - private IntSize mPageSize; - private RectF mVisibleRect; - - public ViewportController(IntSize pageSize, RectF visibleRect) { - mPageSize = pageSize; - mVisibleRect = visibleRect; - } - - private float clamp(float min, float value, float max) { - if (max < min) - return min; - return (value < min) ? min : (value > max) ? max : value; - } - - /** Returns the given rect, clamped to the boundaries of a tile. */ - public RectF clampRect(RectF rect) { - float x = clamp(0, rect.left, mPageSize.width - LayerController.TILE_WIDTH); - float y = clamp(0, rect.top, mPageSize.height - LayerController.TILE_HEIGHT); - return new RectF(x, y, x + rect.width(), x + rect.height()); - } - - /** Returns the coordinates of a tile centered on the given rect. */ - public static RectF widenRect(RectF rect) { - return new RectF(rect.centerX() - LayerController.TILE_WIDTH / 2, - rect.centerY() - LayerController.TILE_HEIGHT / 2, - rect.centerX() + LayerController.TILE_WIDTH / 2, - rect.centerY() + LayerController.TILE_HEIGHT / 2); - } - - /** - * Given the layer controller's visible rect, page size, and screen size, returns the zoom - * factor. - */ - public float getZoomFactor(RectF layerVisibleRect, IntSize layerPageSize, - IntSize screenSize) { - RectF transformed = transformVisibleRect(layerVisibleRect, layerPageSize); - return (float)screenSize.width / transformed.width(); - } - - /** - * Given the visible rectangle that the user is viewing and the layer controller's page size, - * returns the dimensions of the box that this corresponds to on the page. - */ - public RectF transformVisibleRect(RectF layerVisibleRect, IntSize layerPageSize) { - float zoomFactor = (float)layerPageSize.width / (float)mPageSize.width; - return RectUtils.scale(layerVisibleRect, 1.0f / zoomFactor); - } - - /** - * Given the visible rectangle that the user is viewing and the layer controller's page size, - * returns the dimensions in layer coordinates that this corresponds to. - */ - public RectF untransformVisibleRect(RectF viewportVisibleRect, IntSize layerPageSize) { - float zoomFactor = (float)layerPageSize.width / (float)mPageSize.width; - return RectUtils.scale(viewportVisibleRect, zoomFactor); - } - - public IntSize getPageSize() { return mPageSize; } - public void setPageSize(IntSize pageSize) { mPageSize = pageSize; } - public RectF getVisibleRect() { return mVisibleRect; } - public void setVisibleRect(RectF visibleRect) { mVisibleRect = visibleRect; } -} - diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 40f8db640af7..e6a97b2565e1 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -265,7 +265,7 @@ var BrowserApp = { if (!aTab) return; - this.deck.selectedPanel = aTab.browser; + this.deck.selectedPanel = aTab.vbox; }, get selectedBrowser() { @@ -507,15 +507,7 @@ var BrowserApp = { }, getDrawMetadata: function getDrawMetadata() { - return JSON.stringify(this.selectedTab.viewportMetrics); - }, - - setViewport: function setViewport(aNewViewport) { - this.selectedTab.viewportMetrics = aNewViewport; - - /* TODO: Translate at edges. */ - /* TODO: Zoom. */ - this.selectedBrowser.contentWindow.scrollTo(aNewViewport.x, aNewViewport.y); + return JSON.stringify(this.selectedTab.viewport); }, observe: function(aSubject, aTopic, aData) { @@ -557,7 +549,7 @@ var BrowserApp = { } else if (aTopic == "FullScreen:Exit") { browser.contentDocument.mozCancelFullScreen(); } else if (aTopic == "Viewport:Change") { - this.setViewport(JSON.parse(aData)); + this.selectedTab.viewport = JSON.parse(aData); } } } @@ -905,9 +897,11 @@ let gTabIDFactory = 0; function Tab(aURL, aParams) { this.browser = null; + this.vbox = null; this.id = 0; this.create(aURL, aParams); - this.viewportMetrics = { x: 0, y: 0 }; + this._viewport = { x: 0, y: 0, width: 1, height: 1, offsetX: 0, offsetY: 0, + pageWidth: 1, pageHeight: 1 }; } Tab.prototype = { @@ -915,12 +909,17 @@ Tab.prototype = { if (this.browser) return; + this.vbox = document.createElement("vbox"); + this.vbox.align = "start"; + BrowserApp.deck.appendChild(this.vbox); + this.browser = document.createElement("browser"); this.browser.setAttribute("type", "content"); - this.browser.setAttribute("width", "980"); - this.browser.setAttribute("height", "480"); - BrowserApp.deck.appendChild(this.browser); + this.browser.style.width = "980px"; + this.browser.style.height = "480px"; + this.vbox.appendChild(this.browser); + // Turn off clipping so we can buffer areas outside of the browser element. let frameLoader = this.browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; frameLoader.clipSubdocument = false; @@ -951,8 +950,9 @@ Tab.prototype = { return; this.browser.removeProgressListener(this); - BrowserApp.deck.removeChild(this.browser); + BrowserApp.deck.removeChild(this.vbox); this.browser = null; + this.vbox = null; let message = { gecko: { type: "Tab:Closed", @@ -982,6 +982,58 @@ Tab.prototype = { return this.browser.getAttribute("type") == "content-primary"; }, + set viewport(aViewport) { + // TODO: Zoom? + + // Set scroll position + this.browser.contentWindow.scrollTo(aViewport.x, aViewport.y); + + // Check if the viewport size/position has changed and set the necessary + // attributes on the browser element. + if (aViewport.width != this._viewport.width) { + this._viewport.width = aViewport.width; + this.browser.style.width = this._viewport.width.toString() + "px"; + } + if (aViewport.height != this._viewport.height) { + this._viewport.height = aViewport.height; + this.browser.style.height = this._viewport.height.toString() + "px"; + } + + let transformChanged = false; + + if (aViewport.offsetX != this._viewport.offsetX) { + this._viewport.offsetX = aViewport.offsetX; + transformChanged = true; + } + if (aViewport.offsetY != this._viewport.offsetY) { + this._viewport.offsetY = aViewport.offsetY; + transformChanged = true; + } + + if (transformChanged) + this.browser.style.MozTransform = + "translate(" + this._viewport.offsetX + "px, " + + this._viewport.offsetY + "px)"; + + }, + + get viewport() { + // Update the viewport to current dimensions + this._viewport.x = this.browser.contentWindow.scrollX; + this._viewport.y = this.browser.contentWindow.scrollY; + + let doc = this.browser.contentDocument.documentElement; + if (doc != null) { + this._viewport.pageWidth = doc.scrollWidth; + this._viewport.pageHeight = doc.scrollHeight; + } else { + this._viewport.pageWidth = this._viewport.width; + this._viewport.pageHeight = this._viewport.height; + } + + return this._viewport; + }, + onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) { if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) { // Filter optimization: Only really send DOCUMENT state changes to Java listener @@ -1111,7 +1163,6 @@ var BrowserEventHandler = { BrowserApp.deck.addEventListener("DOMLinkAdded", this, true); BrowserApp.deck.addEventListener("DOMTitleChanged", this, true); BrowserApp.deck.addEventListener("DOMUpdatePageReport", PopupBlockerObserver.onUpdatePageReport, false); - BrowserApp.deck.addEventListener("MozScrolledAreaChanged", this, true); }, handleEvent: function(aEvent) { @@ -1122,13 +1173,20 @@ var BrowserEventHandler = { return; let tabID = BrowserApp.getTabForBrowser(browser).id; + let zoom = browser._zoom || 1; + let html = browser.contentDocument.documentElement; + let size = { width: html.scrollWidth * zoom, height: html.scrollHeight * zoom }; + + dump("### Sending pageSize: " + size.toSource()); + sendMessageToJava({ gecko: { type: "DOMContentLoaded", tabID: tabID, windowID: 0, uri: browser.currentURI.spec, - title: browser.contentTitle + title: browser.contentTitle, + pageSize: size } }); @@ -1489,19 +1547,6 @@ var BrowserEventHandler = { window.mozRequestAnimationFrame(callback); } break; - - case "MozScrolledAreaChanged": - let browser = BrowserApp.getBrowserForDocument(aEvent.target); - if (browser != BrowserApp.selectedBrowser) - return; - - sendMessageToJava({ - gecko: { - type: "PanZoom:Resize", - size: { width: aEvent.width, height: aEvent.height } - } - }); - break; } },