Index: content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java |
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java |
index ba0c70ebeaa44f086df6ca89bf046cf7f4b47a4d..e9cdf106c14156f9153785c012c30a53ddf08f0e 100644 |
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java |
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java |
@@ -87,8 +87,10 @@ import java.lang.reflect.Field; |
import java.util.ArrayList; |
import java.util.HashMap; |
import java.util.HashSet; |
+import java.util.LinkedHashMap; |
import java.util.List; |
import java.util.Map; |
+import java.util.Map.Entry; |
/** |
* Provides a Java-side 'wrapper' around a WebContent (native) instance. |
@@ -143,6 +145,136 @@ public class ContentViewCore |
private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>(); |
/** |
+ * A {@link ViewAndroidDelegate} that delegates to the current container view. |
+ * |
+ * <p>This delegate handles the replacement of container views transparently so |
+ * that clients can safely hold to instances of this class. |
+ */ |
+ private class ContentViewAndroidDelegate implements ViewAndroidDelegate { |
+ /** |
+ * Represents the position of an anchor view. |
+ */ |
+ @VisibleForTesting |
+ private class Position { |
+ private final float mX; |
+ private final float mY; |
+ private final float mWidth; |
+ private final float mHeight; |
+ |
+ public Position(float x, float y, float width, float height) { |
+ mX = x; |
+ mY = y; |
+ mWidth = width; |
+ mHeight = height; |
+ } |
+ } |
+ |
+ /** |
+ * The current container view. This view can be updated with |
+ * {@link #updateCurrentContainerView()}. |
+ */ |
+ private ViewGroup mCurrentContainerView; |
+ |
+ /** |
+ * List of anchor views stored in the order in which they were acquired mapped |
+ * to their position. |
+ */ |
+ private Map<View, Position> mAnchorViews = new LinkedHashMap<View, Position>(); |
+ |
+ @Override |
+ public View acquireAnchorView() { |
+ View anchorView = new View(mContext); |
+ mAnchorViews.put(anchorView, null); |
+ mCurrentContainerView.addView(anchorView); |
+ return anchorView; |
+ } |
+ |
+ @Override |
+ @SuppressWarnings("deprecation") // AbsoluteLayout |
+ public void setAnchorViewPosition( |
+ View view, float x, float y, float width, float height) { |
+ mAnchorViews.put(view, new Position(x, y, width, height)); |
+ doSetAnchorViewPosition(view, x, y, width, height); |
+ } |
+ |
+ private void doSetAnchorViewPosition( |
+ View view, float x, float y, float width, float height) { |
+ if (view.getParent() == null) { |
+ // Ignore. setAnchorViewPosition has been called after the anchor view has |
+ // already been released. |
+ return; |
+ } |
+ assert view.getParent() == mCurrentContainerView; |
+ |
+ float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale(); |
+ |
+ // The anchor view should not go outside the bounds of the ContainerView. |
+ int leftMargin = Math.round(x * scale); |
+ int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale); |
+ int scaledWidth = Math.round(width * scale); |
+ // ContentViewCore currently only supports these two container view types. |
+ if (mCurrentContainerView instanceof FrameLayout) { |
+ int startMargin; |
+ if (ApiCompatibilityUtils.isLayoutRtl(mCurrentContainerView)) { |
+ startMargin = mCurrentContainerView.getMeasuredWidth() |
+ - Math.round((width + x) * scale); |
+ } else { |
+ startMargin = leftMargin; |
+ } |
+ if (scaledWidth + startMargin > mCurrentContainerView.getWidth()) { |
+ scaledWidth = mCurrentContainerView.getWidth() - startMargin; |
+ } |
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( |
+ scaledWidth, Math.round(height * scale)); |
+ ApiCompatibilityUtils.setMarginStart(lp, startMargin); |
+ lp.topMargin = topMargin; |
+ view.setLayoutParams(lp); |
+ } else if (mCurrentContainerView instanceof android.widget.AbsoluteLayout) { |
+ // This fixes the offset due to a difference in |
+ // scrolling model of WebView vs. Chrome. |
+ // TODO(sgurun) fix this to use mContainerViewAtCreation.getScroll[X/Y]() |
+ // as it naturally accounts for scroll differences between |
+ // these models. |
+ leftMargin += mRenderCoordinates.getScrollXPixInt(); |
+ topMargin += mRenderCoordinates.getScrollYPixInt(); |
+ |
+ android.widget.AbsoluteLayout.LayoutParams lp = |
+ new android.widget.AbsoluteLayout.LayoutParams( |
+ scaledWidth, (int) (height * scale), leftMargin, topMargin); |
+ view.setLayoutParams(lp); |
+ } else { |
+ Log.e(TAG, "Unknown layout " + mCurrentContainerView.getClass().getName()); |
+ } |
+ } |
+ |
+ @Override |
+ public void releaseAnchorView(View anchorView) { |
+ mAnchorViews.remove(anchorView); |
+ mCurrentContainerView.removeView(anchorView); |
+ } |
+ |
+ /** |
+ * Updates (or sets for the first time) the current container view to which |
+ * this class delegates. Existing anchor views are transferred from the old to |
+ * the new container view. |
+ */ |
+ void updateCurrentContainerView() { |
+ ViewGroup oldContainerView = mCurrentContainerView; |
+ mCurrentContainerView = mContainerView; |
+ for (Entry<View, Position> entry : mAnchorViews.entrySet()) { |
+ View anchorView = entry.getKey(); |
+ Position position = entry.getValue(); |
+ oldContainerView.removeView(anchorView); |
+ mCurrentContainerView.addView(anchorView); |
+ if (position != null) { |
+ doSetAnchorViewPosition(anchorView, |
+ position.mX, position.mY, position.mWidth, position.mHeight); |
+ } |
+ } |
+ } |
+ } |
+ |
+ /** |
* Interface that consumers of {@link ContentViewCore} must implement to allow the proper |
* dispatching of view methods through the containing view. |
* |
@@ -361,6 +493,9 @@ public class ContentViewCore |
// screen orientation. |
private boolean mFullscreenRequiredForOrientationLock = true; |
+ // A ViewAndroidDelegate that delegates to the current container view. |
+ private ContentViewAndroidDelegate mViewAndroidDelegate; |
+ |
/** |
* Constructs a new ContentViewCore. Embedders must call initialize() after constructing |
* a ContentViewCore and before using it. |
@@ -429,9 +564,11 @@ public class ContentViewCore |
} |
/** |
- * Returns a delegate that can be used to add and remove views from the ContainerView. |
+ * Returns a delegate that can be used to add and remove views from the current |
+ * container view. Clients can safely hold to instances of this class as it handles the |
+ * replacement of container views transparently. |
* |
- * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same |
+ * NOTE: Use with care, as not all ContentViewCore users setup their container view in the same |
* way. In particular, the Android WebView has limitations on what implementation details can |
* be provided via a child view, as they are visible in the API and could introduce |
* compatibility breaks with existing applications. If in doubt, contact the |
@@ -439,77 +576,8 @@ public class ContentViewCore |
* |
* @return A ViewAndroidDelegate that can be used to add and remove views. |
*/ |
- @VisibleForTesting |
public ViewAndroidDelegate getViewAndroidDelegate() { |
- return new ViewAndroidDelegate() { |
- // mContainerView can change, but this ViewAndroidDelegate can only be used to |
- // add and remove views from the mContainerViewAtCreation. |
- private final ViewGroup mContainerViewAtCreation = mContainerView; |
- |
- @Override |
- public View acquireAnchorView() { |
- View anchorView = new View(mContext); |
- mContainerViewAtCreation.addView(anchorView); |
- return anchorView; |
- } |
- |
- @Override |
- @SuppressWarnings("deprecation") // AbsoluteLayout |
- public void setAnchorViewPosition( |
- View view, float x, float y, float width, float height) { |
- if (view.getParent() == null) { |
- // Ignore. setAnchorViewPosition has been called after the anchor view has |
- // already been released. |
- return; |
- } |
- assert view.getParent() == mContainerViewAtCreation; |
- |
- float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale(); |
- |
- // The anchor view should not go outside the bounds of the ContainerView. |
- int leftMargin = Math.round(x * scale); |
- int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale); |
- int scaledWidth = Math.round(width * scale); |
- // ContentViewCore currently only supports these two container view types. |
- if (mContainerViewAtCreation instanceof FrameLayout) { |
- int startMargin; |
- if (ApiCompatibilityUtils.isLayoutRtl(mContainerViewAtCreation)) { |
- startMargin = mContainerViewAtCreation.getMeasuredWidth() |
- - Math.round((width + x) * scale); |
- } else { |
- startMargin = leftMargin; |
- } |
- if (scaledWidth + startMargin > mContainerViewAtCreation.getWidth()) { |
- scaledWidth = mContainerViewAtCreation.getWidth() - startMargin; |
- } |
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( |
- scaledWidth, Math.round(height * scale)); |
- ApiCompatibilityUtils.setMarginStart(lp, startMargin); |
- lp.topMargin = topMargin; |
- view.setLayoutParams(lp); |
- } else if (mContainerViewAtCreation instanceof android.widget.AbsoluteLayout) { |
- // This fixes the offset due to a difference in |
- // scrolling model of WebView vs. Chrome. |
- // TODO(sgurun) fix this to use mContainerViewAtCreation.getScroll[X/Y]() |
- // as it naturally accounts for scroll differences between |
- // these models. |
- leftMargin += mRenderCoordinates.getScrollXPixInt(); |
- topMargin += mRenderCoordinates.getScrollYPixInt(); |
- |
- android.widget.AbsoluteLayout.LayoutParams lp = |
- new android.widget.AbsoluteLayout.LayoutParams( |
- scaledWidth, (int) (height * scale), leftMargin, topMargin); |
- view.setLayoutParams(lp); |
- } else { |
- Log.e(TAG, "Unknown layout " + mContainerViewAtCreation.getClass().getName()); |
- } |
- } |
- |
- @Override |
- public void releaseAnchorView(View anchorView) { |
- mContainerViewAtCreation.removeView(anchorView); |
- } |
- }; |
+ return mViewAndroidDelegate; |
} |
@VisibleForTesting |
@@ -537,6 +605,11 @@ public class ContentViewCore |
return mInputConnection; |
} |
+ @VisibleForTesting |
+ ViewAndroid getViewAndroid() { |
+ return mViewAndroid; |
+ } |
+ |
private ImeAdapter createImeAdapter(Context context) { |
return new ImeAdapter(mInputMethodManagerWrapper, |
new ImeAdapter.ImeAdapterDelegate() { |
@@ -605,11 +678,12 @@ public class ContentViewCore |
// deleting it after destroying the ContentViewCore. |
public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher, |
long nativeWebContents, WindowAndroid windowAndroid) { |
+ createContentViewAndroidDelegate(); |
setContainerView(containerView); |
- |
long windowNativePointer = windowAndroid.getNativePointer(); |
assert windowNativePointer != 0; |
- mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate()); |
+ createViewAndroid(windowAndroid); |
+ |
long viewAndroidNativePointer = mViewAndroid.getNativePointer(); |
assert viewAndroidNativePointer != 0; |
@@ -648,13 +722,23 @@ public class ContentViewCore |
}; |
} |
+ @VisibleForTesting |
+ void createContentViewAndroidDelegate() { |
+ mViewAndroidDelegate = new ContentViewAndroidDelegate(); |
+ } |
+ |
+ @VisibleForTesting |
+ void createViewAndroid(WindowAndroid windowAndroid) { |
+ mViewAndroid = new ViewAndroid(windowAndroid, mViewAndroidDelegate); |
+ } |
+ |
/** |
* Sets a new container view for this {@link ContentViewCore}. |
* |
- * <p>WARNING: This is not a general purpose method and has been designed with WebView |
- * fullscreen in mind. Please be aware that it might not be appropriate for other use cases |
- * and that it has a number of limitations. For example the PopupZoomer only works with the |
- * container view with which this ContentViewCore has been initialized. |
+ * <p>WARNING: This method can also be used to replace the existing container view, |
+ * but you should only do it if you have a very good reason to. Replacing the |
+ * container view has been designed to support fullscreen in the Webview so it |
+ * might not be appropriate for other use cases. |
* |
* <p>This method only performs a small part of replacing the container view and |
* embedders are responsible for: |
@@ -678,6 +762,7 @@ public class ContentViewCore |
mPositionObserver = new ViewPositionObserver(mContainerView); |
mContainerView.setWillNotDraw(false); |
mContainerView.setClickable(true); |
+ mViewAndroidDelegate.updateCurrentContainerView(); |
TraceEvent.end(); |
} |
@@ -696,7 +781,8 @@ public class ContentViewCore |
mContainerViewInternals = internalDispatcher; |
} |
- private void initPopupZoomer(Context context) { |
+ @VisibleForTesting |
+ void initPopupZoomer(Context context) { |
mPopupZoomer = new PopupZoomer(context); |
mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() { |
// mContainerView can change, but this OnVisibilityChangedListener can only be used |