summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--android_webview/browser/browser_view_renderer.cc8
-rw-r--r--android_webview/browser/browser_view_renderer.h4
-rw-r--r--android_webview/browser/hardware_renderer.cc2
-rw-r--r--android_webview/browser/shared_renderer_state.cc3
-rw-r--r--android_webview/browser/shared_renderer_state.h1
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java50
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwContents.java176
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwPdfExporter.java7
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwViewMethods.java2
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java6
-rw-r--r--android_webview/java/src/org/chromium/android_webview/FullScreenView.java214
-rw-r--r--android_webview/java/src/org/chromium/android_webview/NullAwViewMethods.java130
-rw-r--r--android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenVideoTest.java120
-rw-r--r--android_webview/javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java11
-rw-r--r--android_webview/javatests/src/org/chromium/android_webview/test/util/VideoTestWebServer.java2
-rw-r--r--android_webview/native/aw_contents.cc6
-rw-r--r--android_webview/native/aw_contents.h1
-rw-r--r--android_webview/test/shell/assets/full_screen_video_test.html1
-rw-r--r--content/public/test/android/javatests/src/org/chromium/content/browser/test/util/DOMUtils.java67
19 files changed, 754 insertions, 57 deletions
diff --git a/android_webview/browser/browser_view_renderer.cc b/android_webview/browser/browser_view_renderer.cc
index f27a90a..b0b34da 100644
--- a/android_webview/browser/browser_view_renderer.cc
+++ b/android_webview/browser/browser_view_renderer.cc
@@ -104,6 +104,7 @@ BrowserViewRenderer::BrowserViewRenderer(
attached_to_window_(false),
hardware_enabled_(false),
dip_scale_(0.0),
+ has_transparent_background_(false),
page_scale_factor_(1.0),
on_new_picture_enable_(false),
clear_view_(false),
@@ -254,6 +255,7 @@ bool BrowserViewRenderer::OnDrawHardware(jobject java_canvas) {
draw_gl_input->scroll_offset = last_on_draw_scroll_offset_;
draw_gl_input->width = width_;
draw_gl_input->height = height_;
+ draw_gl_input->has_transparent_background = has_transparent_background_;
gfx::Transform transform;
gfx::Size surface_size(width_, height_);
@@ -475,6 +477,10 @@ void BrowserViewRenderer::SetDipScale(float dip_scale) {
CHECK(dip_scale_ > 0);
}
+void BrowserViewRenderer::SetHasTransparentBackground(bool transparent) {
+ has_transparent_background_ = transparent;
+}
+
gfx::Vector2d BrowserViewRenderer::max_scroll_offset() const {
DCHECK_GT(dip_scale_, 0);
return gfx::ToCeiledVector2d(gfx::ScaleVector2d(
@@ -725,6 +731,8 @@ std::string BrowserViewRenderer::ToString(AwDrawGLInfo* draw_info) const {
base::StringAppendF(&str, "view_visible: %d ", view_visible_);
base::StringAppendF(&str, "window_visible: %d ", window_visible_);
base::StringAppendF(&str, "dip_scale: %f ", dip_scale_);
+ base::StringAppendF(&str, "has_transparent_background_: %d",
+ has_transparent_background_);
base::StringAppendF(&str, "page_scale_factor: %f ", page_scale_factor_);
base::StringAppendF(&str,
"compositor_needs_continuous_invalidate: %d ",
diff --git a/android_webview/browser/browser_view_renderer.h b/android_webview/browser/browser_view_renderer.h
index 378eeb6..876f563 100644
--- a/android_webview/browser/browser_view_renderer.h
+++ b/android_webview/browser/browser_view_renderer.h
@@ -99,6 +99,9 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient,
// Sets the scale for logical<->physical pixel conversions.
void SetDipScale(float dip_scale);
+ // Sets the background of the layer tree to transparent.
+ void SetHasTransparentBackground(bool transparent);
+
// Set the root layer scroll offset to |new_value|.
void ScrollTo(gfx::Vector2d new_value);
@@ -192,6 +195,7 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient,
bool attached_to_window_;
bool hardware_enabled_;
float dip_scale_;
+ bool has_transparent_background_;
float page_scale_factor_;
bool on_new_picture_enable_;
bool clear_view_;
diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc
index 3f37396..7d0f5c0 100644
--- a/android_webview/browser/hardware_renderer.cc
+++ b/android_webview/browser/hardware_renderer.cc
@@ -166,6 +166,8 @@ void HardwareRenderer::DrawGL(bool stencil_enabled,
bool size_changed = frame_size != frame_size_;
frame_size_ = frame_size;
scroll_offset_ = input->scroll_offset;
+ layer_tree_host_->set_has_transparent_background(
+ input->has_transparent_background);
if (!frame_provider_ || size_changed) {
if (delegated_layer_) {
diff --git a/android_webview/browser/shared_renderer_state.cc b/android_webview/browser/shared_renderer_state.cc
index 8845b76..36fece5 100644
--- a/android_webview/browser/shared_renderer_state.cc
+++ b/android_webview/browser/shared_renderer_state.cc
@@ -10,7 +10,8 @@
namespace android_webview {
-DrawGLInput::DrawGLInput() : width(0), height(0) {
+DrawGLInput::DrawGLInput() : width(0), height(0),
+ has_transparent_background(false) {
}
DrawGLInput::~DrawGLInput() {
diff --git a/android_webview/browser/shared_renderer_state.h b/android_webview/browser/shared_renderer_state.h
index 89554ac..9a458e4 100644
--- a/android_webview/browser/shared_renderer_state.h
+++ b/android_webview/browser/shared_renderer_state.h
@@ -31,6 +31,7 @@ struct DrawGLInput {
int width;
int height;
cc::CompositorFrame frame;
+ bool has_transparent_background;
DrawGLInput();
~DrawGLInput();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
index 55afba5..91c583f 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
@@ -9,10 +9,13 @@ import android.view.KeyEvent;
import android.view.View;
import android.webkit.URLUtil;
import android.webkit.WebChromeClient;
+import android.widget.FrameLayout;
+import org.chromium.base.CommandLine;
import org.chromium.content.browser.ContentVideoView;
import org.chromium.content.browser.ContentVideoViewClient;
import org.chromium.content.browser.ContentViewClient;
+import org.chromium.content.common.ContentSwitches;
/**
* ContentViewClient implementation for WebView
@@ -30,12 +33,44 @@ public class AwContentViewClient extends ContentViewClient {
contentVideoView.exitFullscreen(false);
}
};
- mAwContentsClient.onShowCustomView(view, cb);
+ // TODO(igsolla): remove the legacy path (kept as a fallback if things go awry).
+ if (!areHtmlControlsEnabled()) {
+ onShowCustomViewLegacy(view, cb);
+ } else {
+ onShowCustomView(view, cb);
+ }
return true;
}
+ private void onShowCustomViewLegacy(View view, WebChromeClient.CustomViewCallback cb) {
+ mAwContentsClient.onShowCustomView(view, cb);
+ }
+
+ private void onShowCustomView(View view, WebChromeClient.CustomViewCallback cb) {
+ final FrameLayout viewGroup = new FrameLayout(mContext);
+ viewGroup.addView(view);
+ viewGroup.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ // Intentional no-op (see onDestroyContentVideoView).
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ if (mAwContents.isFullScreen()) {
+ return;
+ }
+ viewGroup.addView(mAwContents.enterFullScreen());
+ }
+ });
+ mAwContentsClient.onShowCustomView(viewGroup, cb);
+ }
+
@Override
public void onDestroyContentVideoView() {
+ if (areHtmlControlsEnabled()) {
+ mAwContents.exitFullScreen();
+ }
mAwContentsClient.onHideCustomView();
}
@@ -47,10 +82,16 @@ public class AwContentViewClient extends ContentViewClient {
private AwContentsClient mAwContentsClient;
private AwSettings mAwSettings;
+ private AwContents mAwContents;
+ private Context mContext;
- public AwContentViewClient(AwContentsClient awContentsClient, AwSettings awSettings) {
+ public AwContentViewClient(
+ AwContentsClient awContentsClient, AwSettings awSettings, AwContents awContents,
+ Context context) {
mAwContentsClient = awContentsClient;
mAwSettings = awSettings;
+ mAwContents = awContents;
+ mContext = context;
}
@Override
@@ -84,4 +125,9 @@ public class AwContentViewClient extends ContentViewClient {
return mAwSettings != null ?
mAwSettings.getBlockNetworkLoads() && URLUtil.isNetworkUrl(url) : true;
}
+
+ private static boolean areHtmlControlsEnabled() {
+ return !CommandLine.getInstance().hasSwitch(
+ ContentSwitches.DISABLE_OVERLAY_FULLSCREEN_VIDEO_SUBTITLE);
+ }
}
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 380cc6e..e66afec 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -175,16 +175,17 @@ public class AwContents {
private long mNativeAwContents;
private final AwBrowserContext mBrowserContext;
- private final ViewGroup mContainerView;
+ private ViewGroup mContainerView;
+ private final AwLayoutChangeListener mLayoutChangeListener;
private final Context mContext;
private ContentViewCore mContentViewCore;
private final AwContentsClient mContentsClient;
private final AwContentViewClient mContentViewClient;
private final AwContentsClientBridge mContentsClientBridge;
- private final AwWebContentsDelegate mWebContentsDelegate;
+ private final AwWebContentsDelegateAdapter mWebContentsDelegate;
private final AwContentsIoThreadClient mIoThreadClient;
private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
- private final InternalAccessDelegate mInternalAccessAdapter;
+ private InternalAccessDelegate mInternalAccessAdapter;
private final NativeGLDelegate mNativeGLDelegate;
private final AwLayoutSizer mLayoutSizer;
private final AwZoomControls mZoomControls;
@@ -230,7 +231,8 @@ public class AwContents {
private AwPdfExporter mAwPdfExporter;
- private final AwViewMethods mAwViewMethods;
+ private AwViewMethods mAwViewMethods;
+ private final FullScreenTransitionsState mFullScreenTransitionsState;
// This flag indicates that ShouldOverrideUrlNavigation should be posted
// through the resourcethrottle. This is only used for popup windows.
@@ -247,6 +249,52 @@ public class AwContents {
}
}
+ /**
+ * A class that stores the state needed to enter and exit fullscreen.
+ */
+ private static class FullScreenTransitionsState {
+ private final ViewGroup mInitialContainerView;
+ private final InternalAccessDelegate mInitialInternalAccessAdapter;
+ private final AwViewMethods mInitialAwViewMethods;
+ private FullScreenView mFullScreenView;
+
+ private FullScreenTransitionsState(ViewGroup initialContainerView,
+ InternalAccessDelegate initialInternalAccessAdapter,
+ AwViewMethods initialAwViewMethods) {
+ mInitialContainerView = initialContainerView;
+ mInitialInternalAccessAdapter = initialInternalAccessAdapter;
+ mInitialAwViewMethods = initialAwViewMethods;
+ }
+
+ private void enterFullScreen(FullScreenView fullScreenView) {
+ mFullScreenView = fullScreenView;
+ }
+
+ private void exitFullScreen() {
+ mFullScreenView = null;
+ }
+
+ private boolean isFullScreen() {
+ return mFullScreenView != null;
+ }
+
+ private ViewGroup getInitialContainerView() {
+ return mInitialContainerView;
+ }
+
+ private InternalAccessDelegate getInitialInternalAccessDelegate() {
+ return mInitialInternalAccessAdapter;
+ }
+
+ private AwViewMethods getInitialAwViewMethods() {
+ return mInitialAwViewMethods;
+ }
+
+ private FullScreenView getFullScreenView() {
+ return mFullScreenView;
+ }
+ }
+
// Reference to the active mNativeAwContents pointer while it is active use
// (ie before it is destroyed).
private CleanupReference mCleanupReference;
@@ -510,7 +558,9 @@ public class AwContents {
mNativeGLDelegate = nativeGLDelegate;
mContentsClient = contentsClient;
mAwViewMethods = new AwViewMethodsImpl();
- mContentViewClient = new AwContentViewClient(contentsClient, settings);
+ mFullScreenTransitionsState = new FullScreenTransitionsState(
+ mContainerView, mInternalAccessAdapter, mAwViewMethods);
+ mContentViewClient = new AwContentViewClient(contentsClient, settings, this, mContext);
mLayoutSizer = dependencyFactory.createLayoutSizer();
mSettings = settings;
mDIPScale = DeviceDisplayInfo.create(mContext).getDIPScale();
@@ -544,12 +594,12 @@ public class AwContents {
setOverScrollMode(mContainerView.getOverScrollMode());
setScrollBarStyle(mInternalAccessAdapter.super_getScrollBarStyle());
- mContainerView.addOnLayoutChangeListener(new AwLayoutChangeListener());
+ mLayoutChangeListener = new AwLayoutChangeListener();
+ mContainerView.addOnLayoutChangeListener(mLayoutChangeListener);
setNewAwContents(nativeInit(mBrowserContext));
- onVisibilityChanged(mContainerView, mContainerView.getVisibility());
- onWindowVisibilityChanged(mContainerView.getWindowVisibility());
+ onContainerViewChanged();
}
private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView,
@@ -568,8 +618,112 @@ public class AwContents {
return contentViewCore;
}
+ boolean isFullScreen() {
+ return mFullScreenTransitionsState.isFullScreen();
+ }
+
+ /**
+ * Transitions this {@link AwContents} to fullscreen mode and returns the
+ * {@link View} where the contents will be drawn while in fullscreen.
+ */
+ View enterFullScreen() {
+ assert !isFullScreen();
+
+ // Detach to tear down the GL functor if this is still associated with the old
+ // container view. It will be recreated during the next call to onDraw attached to
+ // the new container view.
+ onDetachedFromWindow();
+
+ // In fullscreen mode FullScreenView owns the AwViewMethodsImpl and AwContents
+ // a NullAwViewMethods.
+ FullScreenView fullScreenView = new FullScreenView(mContext, mAwViewMethods);
+ mFullScreenTransitionsState.enterFullScreen(fullScreenView);
+ mAwViewMethods = new NullAwViewMethods(this, mInternalAccessAdapter, mContainerView);
+ mContainerView.removeOnLayoutChangeListener(mLayoutChangeListener);
+ fullScreenView.addOnLayoutChangeListener(mLayoutChangeListener);
+
+ // Associate this AwContents with the FullScreenView.
+ setInternalAccessAdapter(fullScreenView.getInternalAccessAdapter());
+ setContainerView(fullScreenView);
+
+ // Make the background transparent so that the ContentVideoView is visible
+ // behind the FullScreenView.
+ nativeSetHasTransparentBackground(mNativeAwContents, true);
+ return fullScreenView;
+ }
+
/**
- * Common initialization routine for adopting a native AwContents instance into this
+ * Returns this {@link AwContents} to embedded mode, where the {@link AwContents} are drawn
+ * in the WebView.
+ */
+ void exitFullScreen() {
+ assert isFullScreen();
+
+ // Detach to tear down the GL functor if this is still associated with the old
+ // container view. It will be recreated during the next call to onDraw attached to
+ // the new container view.
+ // NOTE: we cannot use mAwViewMethods here because its type is NullAwViewMethods.
+ AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods();
+ awViewMethodsImpl.onDetachedFromWindow();
+
+ // Swap the view delegates. In embedded mode the FullScreenView owns a
+ // NullAwViewMethods and AwContents the AwViewMethodsImpl.
+ FullScreenView fullscreenView = mFullScreenTransitionsState.getFullScreenView();
+ fullscreenView.setAwViewMethods(new NullAwViewMethods(
+ this, fullscreenView.getInternalAccessAdapter(), fullscreenView));
+ mAwViewMethods = awViewMethodsImpl;
+ ViewGroup initialContainerView = mFullScreenTransitionsState.getInitialContainerView();
+ initialContainerView.addOnLayoutChangeListener(mLayoutChangeListener);
+ fullscreenView.removeOnLayoutChangeListener(mLayoutChangeListener);
+
+ // Re-associate this AwContents with the WebView.
+ setInternalAccessAdapter(mFullScreenTransitionsState.getInitialInternalAccessDelegate());
+ setContainerView(initialContainerView);
+
+ nativeSetHasTransparentBackground(mNativeAwContents, false);
+ mFullScreenTransitionsState.exitFullScreen();
+ }
+
+ private void setInternalAccessAdapter(InternalAccessDelegate internalAccessAdapter) {
+ mInternalAccessAdapter = internalAccessAdapter;
+ mContentViewCore.setContainerViewInternals(mInternalAccessAdapter);
+ }
+
+ private void setContainerView(ViewGroup newContainerView) {
+ mContainerView = newContainerView;
+ mContentViewCore.setContainerView(mContainerView);
+ if (mAwPdfExporter != null) {
+ mAwPdfExporter.setContainerView(mContainerView);
+ }
+ mWebContentsDelegate.setContainerView(mContainerView);
+
+ onContainerViewChanged();
+ }
+
+ /**
+ * Reconciles the state of this AwContents object with the state of the new container view.
+ */
+ private void onContainerViewChanged() {
+ // NOTE: mAwViewMethods is used by the old container view, the WebView, so it might refer
+ // to a NullAwViewMethods when in fullscreen. To ensure that the state is reconciled with
+ // the new container view correctly, we bypass mAwViewMethods and use the real
+ // implementation directly.
+ AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods();
+ awViewMethodsImpl.onVisibilityChanged(mContainerView, mContainerView.getVisibility());
+ awViewMethodsImpl.onWindowVisibilityChanged(mContainerView.getWindowVisibility());
+ if (mContainerView.isAttachedToWindow()) {
+ awViewMethodsImpl.onAttachedToWindow();
+ } else {
+ awViewMethodsImpl.onDetachedFromWindow();
+ }
+ awViewMethodsImpl.onSizeChanged(
+ mContainerView.getWidth(), mContainerView.getHeight(), 0, 0);
+ awViewMethodsImpl.onWindowFocusChanged(mContainerView.hasWindowFocus());
+ awViewMethodsImpl.onFocusChanged(mContainerView.hasFocus(), 0, null);
+ mContainerView.requestLayout();
+ }
+
+ /* Common initialization routine for adopting a native AwContents instance into this
* java instance.
*
* TAKE CARE! This method can get called multiple times per java instance. Code accordingly.
@@ -1969,7 +2123,7 @@ public class AwContents {
// --------------------------------------------------------------------------------------------
// This is the AwViewMethods implementation that does real work. The AwViewMethodsImpl is
- // hooked up to the WebView in embedded mode and to the FullscreenView in fullscreen mode,
+ // hooked up to the WebView in embedded mode and to the FullScreenView in fullscreen mode,
// but not to both at the same time.
private class AwViewMethodsImpl implements AwViewMethods {
private int mLayerType = View.LAYER_TYPE_NONE;
@@ -2260,6 +2414,8 @@ public class AwContents {
private native long nativeReleasePopupAwContents(long nativeAwContents);
private native void nativeFocusFirstNode(long nativeAwContents);
private native void nativeSetBackgroundColor(long nativeAwContents, int color);
+ private native void nativeSetHasTransparentBackground(
+ long nativeAwContents, boolean transparent);
private native long nativeGetAwDrawGLViewContext(long nativeAwContents);
private native long nativeCapturePicture(long nativeAwContents, int width, int height);
diff --git a/android_webview/java/src/org/chromium/android_webview/AwPdfExporter.java b/android_webview/java/src/org/chromium/android_webview/AwPdfExporter.java
index d8b8e37..f9a02ce 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwPdfExporter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwPdfExporter.java
@@ -31,9 +31,14 @@ public class AwPdfExporter {
// Maintain a reference to the top level object (i.e. WebView) since in a common
// use case (offscreen webview) application may expect the framework's print manager
// to own the Webview (via PrintDocumentAdapter).
- private final ViewGroup mContainerView;
+ // NOTE: it looks unused, but please do not remove this reference.
+ private ViewGroup mContainerView;
AwPdfExporter(ViewGroup containerView) {
+ setContainerView(containerView);
+ }
+
+ public void setContainerView(ViewGroup containerView) {
mContainerView = containerView;
}
diff --git a/android_webview/java/src/org/chromium/android_webview/AwViewMethods.java b/android_webview/java/src/org/chromium/android_webview/AwViewMethods.java
index 070e2e3..e5031ad 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwViewMethods.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwViewMethods.java
@@ -18,7 +18,7 @@ import android.view.inputmethod.InputConnection;
* An interface that defines a subset of the {@link View} functionality.
*
* <p>This interface allows us to hook up drawing and input related methods to the
- * {@link AwContents}'s consumer in embedded mode, and to the {@link FullscreenView}
+ * {@link AwContents}'s consumer in embedded mode, and to the {@link FullScreenView}
* in fullscreen mode.
*/
interface AwViewMethods {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
index 7e085662..2edef84 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
@@ -24,11 +24,15 @@ class AwWebContentsDelegateAdapter extends AwWebContentsDelegate {
private static final String TAG = "AwWebContentsDelegateAdapter";
final AwContentsClient mContentsClient;
- final View mContainerView;
+ View mContainerView;
public AwWebContentsDelegateAdapter(AwContentsClient contentsClient,
View containerView) {
mContentsClient = contentsClient;
+ setContainerView(containerView);
+ }
+
+ public void setContainerView(View containerView) {
mContainerView = containerView;
}
diff --git a/android_webview/java/src/org/chromium/android_webview/FullScreenView.java b/android_webview/java/src/org/chromium/android_webview/FullScreenView.java
new file mode 100644
index 0000000..fc47d26
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/FullScreenView.java
@@ -0,0 +1,214 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.AbsoluteLayout;
+
+/**
+ * A view that is used to render the web contents in fullscreen mode, ie.
+ * html controls and subtitles, over the {@link ContentVideoView}.
+ */
+public class FullScreenView extends AbsoluteLayout {
+
+ private AwViewMethods mAwViewMethods;
+ private InternalAccessAdapter mInternalAccessAdapter;
+
+ public FullScreenView(Context context, AwViewMethods awViewMethods) {
+ super(context);
+ setAwViewMethods(awViewMethods);
+ mInternalAccessAdapter = new InternalAccessAdapter();
+ }
+
+ public InternalAccessAdapter getInternalAccessAdapter() {
+ return mInternalAccessAdapter;
+ }
+
+ public void setAwViewMethods(AwViewMethods awViewMethods) {
+ mAwViewMethods = awViewMethods;
+ }
+
+ @Override
+ public void onDraw(final Canvas canvas) {
+ mAwViewMethods.onDraw(canvas);
+ }
+
+ @Override
+ public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
+ mAwViewMethods.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public boolean requestFocus(final int direction, final Rect previouslyFocusedRect) {
+ mAwViewMethods.requestFocus();
+ return super.requestFocus(direction, previouslyFocusedRect);
+ }
+
+ @Override
+ public void setLayerType(int layerType, Paint paint) {
+ super.setLayerType(layerType, paint);
+ mAwViewMethods.setLayerType(layerType, paint);
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
+ return mAwViewMethods.onCreateInputConnection(outAttrs);
+ }
+
+ @Override
+ public boolean onKeyUp(final int keyCode, final KeyEvent event) {
+ return mAwViewMethods.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(final KeyEvent event) {
+ return mAwViewMethods.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(final MotionEvent event) {
+ return mAwViewMethods.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onHoverEvent(final MotionEvent event) {
+ return mAwViewMethods.onHoverEvent(event);
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(final MotionEvent event) {
+ return mAwViewMethods.onGenericMotionEvent(event);
+ }
+
+ @Override
+ public void onConfigurationChanged(final Configuration newConfig) {
+ mAwViewMethods.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mAwViewMethods.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mAwViewMethods.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onWindowFocusChanged(final boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ mAwViewMethods.onWindowFocusChanged(hasWindowFocus);
+ }
+
+ @Override
+ public void onFocusChanged(final boolean focused, final int direction,
+ final Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ mAwViewMethods.onFocusChanged(
+ focused, direction, previouslyFocusedRect);
+ }
+
+ @Override
+ public void onSizeChanged(final int w, final int h, final int ow, final int oh) {
+ super.onSizeChanged(w, h, ow, oh);
+ mAwViewMethods.onSizeChanged(w, h, ow, oh);
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ mAwViewMethods.onVisibilityChanged(changedView, visibility);
+ }
+
+ @Override
+ public void onWindowVisibilityChanged(final int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ mAwViewMethods.onWindowVisibilityChanged(visibility);
+ }
+
+ // AwContents.InternalAccessDelegate implementation --------------------------------------
+ private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
+
+ @Override
+ public boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ // Intentional no-op
+ return false;
+ }
+
+ @Override
+ public boolean super_onKeyUp(int keyCode, KeyEvent event) {
+ return FullScreenView.super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean super_dispatchKeyEventPreIme(KeyEvent event) {
+ return FullScreenView.super.dispatchKeyEventPreIme(event);
+ }
+
+ @Override
+ public boolean super_dispatchKeyEvent(KeyEvent event) {
+ return FullScreenView.super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean super_onGenericMotionEvent(MotionEvent event) {
+ return FullScreenView.super.onGenericMotionEvent(event);
+ }
+
+ @Override
+ public void super_onConfigurationChanged(Configuration newConfig) {
+ // Intentional no-op
+ }
+
+ @Override
+ public int super_getScrollBarStyle() {
+ return FullScreenView.super.getScrollBarStyle();
+ }
+
+ @Override
+ public boolean awakenScrollBars() {
+ return false;
+ }
+
+ @Override
+ public boolean super_awakenScrollBars(int startDelay, boolean invalidate) {
+ return false;
+ }
+
+ @Override
+ public void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
+ int scrollRangeX, int scrollRangeY, int maxOverScrollX,
+ int maxOverScrollY, boolean isTouchEvent) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void super_scrollTo(int scrollX, int scrollY) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
+ FullScreenView.this.setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+ }
+}
diff --git a/android_webview/java/src/org/chromium/android_webview/NullAwViewMethods.java b/android_webview/java/src/org/chromium/android_webview/NullAwViewMethods.java
new file mode 100644
index 0000000..2a94e60
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/NullAwViewMethods.java
@@ -0,0 +1,130 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview;
+
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+import org.chromium.android_webview.AwContents.InternalAccessDelegate;
+
+/**
+ * No-op implementation of {@link AwViewMethods} that follows the null object pattern.
+ * This {@link NullAwViewMethods} is hooked up to the WebView in fullscreen mode, and
+ * to the {@link FullScreenView} in embedded mode, but not to both at the same time.
+ */
+class NullAwViewMethods implements AwViewMethods {
+ private AwContents mAwContents;
+ private InternalAccessDelegate mInternalAccessAdapter;
+ private View mContainerView;
+
+ public NullAwViewMethods(
+ AwContents awContents, InternalAccessDelegate internalAccessAdapter,
+ View containerView) {
+ mAwContents = awContents;
+ mInternalAccessAdapter = internalAccessAdapter;
+ mContainerView = containerView;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ canvas.drawColor(mAwContents.getEffectiveBackgroundColor());
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // When the containerView is using the NullAwViewMethods then it is not
+ // attached to the AwContents. As such, we don't have any contents to measure
+ // and using the last measured dimension is the best we can do.
+ mInternalAccessAdapter.setMeasuredDimension(
+ mContainerView.getMeasuredWidth(), mContainerView.getMeasuredHeight());
+ }
+
+ @Override
+ public void requestFocus() {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void setLayerType(int layerType, Paint paint) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ return null; // Intentional no-op.
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return false; // Intentional no-op.
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ return false; // Intentional no-op.
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return false; // Intentional no-op.
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ return false; // Intentional no-op.
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return false; // Intentional no-op.
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void onSizeChanged(int w, int h, int ow, int oh) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void onVisibilityChanged(View changedView, int visibility) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void onWindowVisibilityChanged(int visibility) {
+ // Intentional no-op.
+ }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenVideoTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenVideoTest.java
index ee50901..fb29ea5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenVideoTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenVideoTest.java
@@ -6,17 +6,44 @@ package org.chromium.android_webview.test;
import android.test.suitebuilder.annotation.MediumTest;
import android.view.KeyEvent;
+import android.view.View;
+
+import junit.framework.Assert;
import org.chromium.android_webview.test.util.VideoTestWebServer;
+import org.chromium.base.CommandLine;
import org.chromium.base.test.util.Feature;
-import org.chromium.content.browser.ContentVideoView;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content.browser.test.util.DOMUtils;
import org.chromium.content.browser.test.util.TouchCommon;
+import org.chromium.content.common.ContentSwitches;
/**
* Test WebChromeClient::onShow/HideCustomView.
*/
public class AwContentsClientFullScreenVideoTest extends AwTestBase {
private FullScreenVideoTestAwContentsClient mContentsClient;
+ private ContentViewCore mContentViewCore;
+ private VideoTestWebServer webServer;
+ private AwTestContainerView testContainerView;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContentsClient = new FullScreenVideoTestAwContentsClient(getActivity());
+ testContainerView =
+ createAwTestContainerViewOnMainSync(mContentsClient);
+ mContentViewCore = testContainerView.getContentViewCore();
+ enableJavaScriptOnUiThread(testContainerView.getAwContents());
+ webServer = new VideoTestWebServer(
+ getInstrumentation().getTargetContext());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ if (webServer != null) webServer.getTestWebServer().shutdown();
+ }
@MediumTest
@Feature({"AndroidWebView"})
@@ -31,38 +58,77 @@ public class AwContentsClientFullScreenVideoTest extends AwTestBase {
@MediumTest
@Feature({"AndroidWebView"})
- public void testOnShowAndHideCustomViewWithBackKey() throws Throwable {
+ public void testOnShowAndHideCustomViewWithBackKeyLegacy() throws Throwable {
+ // When html controls are enabled we skip this test because pressing the back key
+ // moves away from the current activity instead of exiting fullscreen mode.
+ if (areHtmlControlsEnabled())
+ return;
+
doOnShowAndHideCustomViewTest(new Runnable() {
@Override
public void run() {
- ContentVideoView view = mContentsClient.getVideoView();
- view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
- view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));
+ View customView = mContentsClient.getCustomView();
+ customView.dispatchKeyEvent(
+ new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
+ customView.dispatchKeyEvent(
+ new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));
}
});
}
- private void doOnShowAndHideCustomViewTest(Runnable existFullscreen) throws Throwable {
- mContentsClient = new FullScreenVideoTestAwContentsClient(getActivity());
- AwTestContainerView testContainerView =
- createAwTestContainerViewOnMainSync(mContentsClient);
- enableJavaScriptOnUiThread(testContainerView.getAwContents());
- VideoTestWebServer webServer = new VideoTestWebServer(
- getInstrumentation().getTargetContext());
- try {
- loadUrlSync(testContainerView.getAwContents(),
- mContentsClient.getOnPageFinishedHelper(),
- webServer.getFullScreenVideoTestURL());
- Thread.sleep(5 * 1000);
-
- TouchCommon touchCommon = new TouchCommon(this);
- touchCommon.singleClickView(testContainerView);
- mContentsClient.waitForCustomViewShown();
-
- getInstrumentation().runOnMainSync(existFullscreen);
- mContentsClient.waitForCustomViewHidden();
- } finally {
- if (webServer != null) webServer.getTestWebServer().shutdown();
- }
+ @MediumTest
+ @Feature({"AndroidWebView"})
+ public void testOnShowAndHideCustomViewWithJavascript() throws Throwable {
+ doOnShowAndHideCustomViewTest(new Runnable() {
+ @Override
+ public void run() {
+ DOMUtils.exitFullscreen(mContentViewCore);
+ }
+ });
+ }
+
+ @MediumTest
+ @Feature({"AndroidWebView"})
+ public void testOnShowCustomViewAndPlayWithHtmlControl() throws Throwable {
+ if (!areHtmlControlsEnabled())
+ return;
+
+ doOnShowCustomViewTest();
+ Assert.assertFalse(DOMUtils.hasVideoEnded(
+ mContentViewCore, VideoTestWebServer.VIDEO_ID));
+
+ // Click the html play button that is rendered above the video right in the middle
+ // of the custom view. Note that we're not able to get the precise location of the
+ // control since it is a shadow element, so this test might break if the location
+ // ever moves.
+ TouchCommon touchCommon = new TouchCommon(
+ AwContentsClientFullScreenVideoTest.this);
+ touchCommon.singleClickView(mContentsClient.getCustomView());
+
+ Assert.assertTrue(DOMUtils.waitForEndOfVideo(
+ mContentViewCore, VideoTestWebServer.VIDEO_ID));
+ }
+
+ private static boolean areHtmlControlsEnabled() {
+ return !CommandLine.getInstance().hasSwitch(
+ ContentSwitches.DISABLE_OVERLAY_FULLSCREEN_VIDEO_SUBTITLE);
+ }
+
+ private void doOnShowAndHideCustomViewTest(final Runnable existFullscreen)
+ throws Throwable {
+ doOnShowCustomViewTest();
+ getInstrumentation().runOnMainSync(existFullscreen);
+ mContentsClient.waitForCustomViewHidden();
+ }
+
+ private void doOnShowCustomViewTest() throws Exception {
+ loadUrlSync(testContainerView.getAwContents(),
+ mContentsClient.getOnPageFinishedHelper(),
+ webServer.getFullScreenVideoTestURL());
+
+ // Click the button in full_screen_video_test.html to enter fullscreen.
+ TouchCommon touchCommon = new TouchCommon(this);
+ touchCommon.singleClickView(testContainerView);
+ mContentsClient.waitForCustomViewShown();
}
}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java b/android_webview/javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java
index 2307109..37edab5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java
@@ -14,7 +14,6 @@ import android.widget.FrameLayout;
import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
-import org.chromium.content.browser.ContentVideoView;
import org.chromium.content.browser.test.util.CallbackHelper;
import java.util.concurrent.TimeUnit;
@@ -29,7 +28,7 @@ public class FullScreenVideoTestAwContentsClient extends TestAwContentsClient {
private CallbackHelper mOnHideCustomViewCallbackHelper = new CallbackHelper();
private Activity mActivity;
- private ContentVideoView mVideoView;
+ private View mCustomView;
private WebChromeClient.CustomViewCallback mExitCallback;
public FullScreenVideoTestAwContentsClient(Activity activity) {
@@ -38,9 +37,7 @@ public class FullScreenVideoTestAwContentsClient extends TestAwContentsClient {
@Override
public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
- if (view instanceof ContentVideoView) {
- mVideoView = (ContentVideoView)view;
- }
+ mCustomView = view;
mExitCallback = callback;
mActivity.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
@@ -64,8 +61,8 @@ public class FullScreenVideoTestAwContentsClient extends TestAwContentsClient {
return mExitCallback;
}
- public ContentVideoView getVideoView() {
- return mVideoView;
+ public View getCustomView() {
+ return mCustomView;
}
public void waitForCustomViewShown() throws TimeoutException, InterruptedException {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/VideoTestWebServer.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/VideoTestWebServer.java
index 6acb9ee..cbd5cf5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/util/VideoTestWebServer.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/VideoTestWebServer.java
@@ -19,6 +19,8 @@ import java.util.List;
*/
public class VideoTestWebServer {
+ // VIDEO_ID must be kept in sync with the id in full_screen_video_test.html.
+ public static final String VIDEO_ID = "video";
public static final String ONE_PIXEL_ONE_FRAME_WEBM_FILENAME = "one_pixel_one_frame.webm";
public static final String ONE_PIXEL_ONE_FRAME_WEBM_BASE64 =
"GkXfo0AgQoaBAUL3gQFC8oEEQvOBCEKCQAR3ZWJtQoeBAkKFgQIYU4BnQN8VSalmQCgq17FAAw9C" +
diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc
index 10bbb18..f5336e0 100644
--- a/android_webview/native/aw_contents.cc
+++ b/android_webview/native/aw_contents.cc
@@ -976,6 +976,12 @@ void AwContents::SetBackgroundColor(JNIEnv* env, jobject obj, jint color) {
render_view_host_ext_->SetBackgroundColor(color);
}
+void AwContents::SetHasTransparentBackground(
+ JNIEnv* env, jobject obj, bool transparent) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ browser_view_renderer_.SetHasTransparentBackground(transparent);
+}
+
jlong AwContents::ReleasePopupAwContents(JNIEnv* env, jobject obj) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return reinterpret_cast<intptr_t>(pending_contents_.release());
diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h
index 8f69b619..e334829 100644
--- a/android_webview/native/aw_contents.h
+++ b/android_webview/native/aw_contents.h
@@ -111,6 +111,7 @@ class AwContents : public FindHelper::Listener,
jboolean RestoreFromOpaqueState(JNIEnv* env, jobject obj, jbyteArray state);
void FocusFirstNode(JNIEnv* env, jobject obj);
void SetBackgroundColor(JNIEnv* env, jobject obj, jint color);
+ void SetHasTransparentBackground(JNIEnv* env, jobject obj, bool transparent);
bool OnDraw(JNIEnv* env,
jobject obj,
jobject canvas,
diff --git a/android_webview/test/shell/assets/full_screen_video_test.html b/android_webview/test/shell/assets/full_screen_video_test.html
index 466a8d4..618b342 100644
--- a/android_webview/test/shell/assets/full_screen_video_test.html
+++ b/android_webview/test/shell/assets/full_screen_video_test.html
@@ -19,6 +19,7 @@ addEventListener('Loaded', function() {
</script></head><body>
<button autofocus style ='padding:1024px 1024px;' onclick="goFullscreen('video'); return false">Big enough you can't miss it</button>
<p></p>
+<!-- The video id must be kept in sync with VideoTestWebServer.VIDEO_ID -->
<video style = 'width: 10px; height: 10px;' id='video' controls>
<source id="webm" src="VIDEO_FILE_URL" type="video/webm">
</video>
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/DOMUtils.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/DOMUtils.java
index 7851318..fc2c228 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/DOMUtils.java
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/DOMUtils.java
@@ -22,6 +22,48 @@ import java.util.concurrent.TimeoutException;
public class DOMUtils {
/**
+ * Returns whether the video with given {@code nodeId} has ended.
+ */
+ public static boolean hasVideoEnded(final ContentViewCore viewCore, final String nodeId)
+ throws InterruptedException, TimeoutException {
+ return getNodeField("ended", viewCore, nodeId, Boolean.class);
+ }
+
+ /**
+ * Wait until the end of the video with given {@code nodeId}.
+ * @return Whether the video has ended.
+ */
+ public static boolean waitForEndOfVideo(final ContentViewCore viewCore, final String nodeId)
+ throws InterruptedException {
+ return CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ try {
+ return DOMUtils.hasVideoEnded(viewCore, nodeId);
+ } catch (InterruptedException e) {
+ // Intentionally do nothing
+ return false;
+ } catch (TimeoutException e) {
+ // Intentionally do nothing
+ return false;
+ }
+ }
+ });
+ }
+
+ /**
+ * Makes the document exit fullscreen.
+ */
+ public static void exitFullscreen(final ContentViewCore viewCore) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(function() {");
+ sb.append(" if (document.webkitExitFullscreen) document.webkitExitFullscreen();");
+ sb.append("})();");
+
+ JavaScriptUtils.executeJavaScript(viewCore, sb.toString());
+ }
+
+ /**
* Returns the rect boundaries for a node by its id.
*/
public static Rect getNodeBounds(final ContentViewCore viewCore, String nodeId)
@@ -116,7 +158,7 @@ public class DOMUtils {
*/
public static String getNodeContents(ContentViewCore viewCore, String nodeId)
throws InterruptedException, TimeoutException {
- return getNodeField("textContent", viewCore, nodeId);
+ return getNodeField("textContent", viewCore, nodeId, String.class);
}
/**
@@ -124,17 +166,16 @@ public class DOMUtils {
*/
public static String getNodeValue(final ContentViewCore viewCore, String nodeId)
throws InterruptedException, TimeoutException {
- return getNodeField("value", viewCore, nodeId);
+ return getNodeField("value", viewCore, nodeId, String.class);
}
- public static String getNodeField(String fieldName, final ContentViewCore viewCore,
- String nodeId)
+ private static <T> T getNodeField(String fieldName, final ContentViewCore viewCore,
+ String nodeId, Class<T> valueType)
throws InterruptedException, TimeoutException {
StringBuilder sb = new StringBuilder();
sb.append("(function() {");
sb.append(" var node = document.getElementById('" + nodeId + "');");
sb.append(" if (!node) return null;");
- sb.append(" if (!node." + fieldName + ") return null;");
sb.append(" return [ node." + fieldName + " ];");
sb.append("})();");
@@ -144,10 +185,10 @@ public class DOMUtils {
jsonText.trim().equalsIgnoreCase("null"));
JsonReader jsonReader = new JsonReader(new StringReader(jsonText));
- String value = null;
+ T value = null;
try {
jsonReader.beginArray();
- if (jsonReader.hasNext()) value = jsonReader.nextString();
+ if (jsonReader.hasNext()) value = readValue(jsonReader, valueType);
jsonReader.endArray();
Assert.assertNotNull("Invalid contents returned.", value);
@@ -158,6 +199,18 @@ public class DOMUtils {
return value;
}
+ @SuppressWarnings("unchecked")
+ private static <T> T readValue(JsonReader jsonReader, Class<T> valueType)
+ throws IOException {
+ if (valueType.equals(String.class)) return ((T) jsonReader.nextString());
+ if (valueType.equals(Boolean.class)) return ((T) ((Boolean) jsonReader.nextBoolean()));
+ if (valueType.equals(Integer.class)) return ((T) ((Integer) jsonReader.nextInt()));
+ if (valueType.equals(Long.class)) return ((T) ((Long) jsonReader.nextLong()));
+ if (valueType.equals(Double.class)) return ((T) ((Double) jsonReader.nextDouble()));
+
+ throw new IllegalArgumentException("Cannot read values of type " + valueType);
+ }
+
/**
* Wait until a given node has non-zero bounds.
* @return Whether the node started having non-zero bounds.