1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.android_webview.test;
7 import android.content.Context;
8 import android.content.res.Configuration;
9 import android.graphics.Canvas;
10 import android.graphics.PixelFormat;
11 import android.graphics.Rect;
12 import android.opengl.GLSurfaceView;
13 import android.os.Bundle;
14 import android.view.KeyEvent;
15 import android.view.MotionEvent;
16 import android.view.SurfaceHolder;
17 import android.view.View;
18 import android.view.accessibility.AccessibilityEvent;
19 import android.view.accessibility.AccessibilityNodeInfo;
20 import android.view.accessibility.AccessibilityNodeProvider;
21 import android.view.inputmethod.EditorInfo;
22 import android.view.inputmethod.InputConnection;
23 import android.widget.FrameLayout;
25 import org.chromium.android_webview.AwContents;
26 import org.chromium.android_webview.shell.DrawGL;
27 import org.chromium.content.browser.ContentViewCore;
29 import javax.microedition.khronos.egl.EGLConfig;
30 import javax.microedition.khronos.opengles.GL10;
33 * A View used for testing the AwContents internals.
35 * This class takes the place android.webkit.WebView would have in the production configuration.
37 public class AwTestContainerView extends FrameLayout {
38 private AwContents mAwContents;
39 private AwContents.NativeGLDelegate mNativeGLDelegate;
40 private AwContents.InternalAccessDelegate mInternalAccessDelegate;
42 HardwareView mHardwareView = null;
43 private boolean mAttachedContents = false;
45 private class HardwareView extends GLSurfaceView {
46 private static final int MODE_DRAW = 0;
47 private static final int MODE_PROCESS = 1;
48 private static final int MODE_PROCESS_NO_CONTEXT = 2;
49 private static final int MODE_SYNC = 3;
51 // mSyncLock is used to synchronized requestRender on the UI thread
52 // and drawGL on the rendering thread. The variables following
53 // are protected by it.
54 private final Object mSyncLock = new Object();
55 private boolean mFunctorAttached = false;
56 private boolean mNeedsProcessGL = false;
57 private boolean mNeedsDrawGL = false;
58 private boolean mWaitForCompletion = false;
59 private int mLastScrollX = 0;
60 private int mLastScrollY = 0;
62 private int mCommittedScrollX = 0;
63 private int mCommittedScrollY = 0;
65 private boolean mHaveSurface = false;
66 private Runnable mReadyToRenderCallback = null;
68 private long mDrawGL = 0;
69 private long mViewContext = 0;
71 public HardwareView(Context context) {
73 setEGLContextClientVersion(2); // GLES2
74 getHolder().setFormat(PixelFormat.OPAQUE);
75 setPreserveEGLContextOnPause(true);
76 setRenderer(new Renderer() {
77 private int mWidth = 0;
78 private int mHeight = 0;
81 public void onDrawFrame(GL10 gl) {
82 HardwareView.this.drawGL(mWidth, mHeight);
86 public void onSurfaceChanged(GL10 gl, int width, int height) {
87 gl.glViewport(0, 0, width, height);
88 gl.glScissor(0, 0, width, height);
94 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
98 setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
101 public void initialize(long drawGL, long viewContext) {
103 mViewContext = viewContext;
106 public boolean isReadyToRender() {
110 public void setReadyToRenderCallback(Runnable runner) {
111 assert !isReadyToRender() || runner == null;
112 mReadyToRenderCallback = runner;
116 public void surfaceCreated(SurfaceHolder holder) {
117 boolean didHaveSurface = mHaveSurface;
119 if (!didHaveSurface && mReadyToRenderCallback != null) {
120 mReadyToRenderCallback.run();
121 mReadyToRenderCallback = null;
123 super.surfaceCreated(holder);
127 public void surfaceDestroyed(SurfaceHolder holder) {
128 mHaveSurface = false;
129 super.surfaceDestroyed(holder);
132 public void updateScroll(int x, int y) {
133 synchronized (mSyncLock) {
139 public void detachGLFunctor() {
140 synchronized (mSyncLock) {
141 mFunctorAttached = false;
142 mNeedsProcessGL = false;
143 mNeedsDrawGL = false;
144 mWaitForCompletion = false;
148 public void requestRender(Canvas canvas, boolean waitForCompletion) {
149 synchronized (mSyncLock) {
150 super.requestRender();
151 mFunctorAttached = true;
152 mWaitForCompletion = waitForCompletion;
153 if (canvas == null) {
154 mNeedsProcessGL = true;
157 if (!waitForCompletion) {
158 // Wait until SYNC is complete only.
159 // Do this every time there was a new frame.
161 while (mNeedsDrawGL) {
164 } catch (InterruptedException e) {
169 if (waitForCompletion) {
171 while (mWaitForCompletion) {
174 } catch (InterruptedException e) {
181 public void drawGL(int width, int height) {
183 final boolean process;
184 final boolean waitForCompletion;
186 synchronized (mSyncLock) {
187 if (!mFunctorAttached) {
188 mSyncLock.notifyAll();
193 process = mNeedsProcessGL;
194 waitForCompletion = mWaitForCompletion;
197 DrawGL.drawGL(mDrawGL, mViewContext, width, height, 0, 0, MODE_SYNC);
198 mCommittedScrollX = mLastScrollX;
199 mCommittedScrollY = mLastScrollY;
201 mNeedsDrawGL = false;
202 mNeedsProcessGL = false;
203 if (!waitForCompletion) {
204 mSyncLock.notifyAll();
208 DrawGL.drawGL(mDrawGL, mViewContext, width, height,
209 mCommittedScrollX, mCommittedScrollY, MODE_DRAW);
210 } else if (process) {
211 DrawGL.drawGL(mDrawGL, mViewContext, width, height, 0, 0, MODE_PROCESS);
214 if (waitForCompletion) {
215 synchronized (mSyncLock) {
216 mWaitForCompletion = false;
217 mSyncLock.notifyAll();
223 private static boolean sCreatedOnce = false;
224 private HardwareView createHardwareViewOnlyOnce(Context context) {
225 if (sCreatedOnce) return null;
227 return new HardwareView(context);
230 public AwTestContainerView(Context context, boolean hardwareAccelerated) {
232 if (hardwareAccelerated) {
233 mHardwareView = createHardwareViewOnlyOnce(context);
235 if (mHardwareView != null) {
236 addView(mHardwareView,
237 new FrameLayout.LayoutParams(
238 FrameLayout.LayoutParams.MATCH_PARENT,
239 FrameLayout.LayoutParams.MATCH_PARENT));
241 setLayerType(LAYER_TYPE_SOFTWARE, null);
243 mNativeGLDelegate = new NativeGLDelegate();
244 mInternalAccessDelegate = new InternalAccessAdapter();
245 setOverScrollMode(View.OVER_SCROLL_ALWAYS);
247 setFocusableInTouchMode(true);
250 public void initialize(AwContents awContents) {
251 mAwContents = awContents;
252 if (mHardwareView != null) {
253 mHardwareView.initialize(
254 mAwContents.getAwDrawGLFunction(), mAwContents.getAwDrawGLViewContext());
258 public ContentViewCore getContentViewCore() {
259 return mAwContents.getContentViewCore();
262 public AwContents getAwContents() {
266 public AwContents.NativeGLDelegate getNativeGLDelegate() {
267 return mNativeGLDelegate;
270 public AwContents.InternalAccessDelegate getInternalAccessDelegate() {
271 return mInternalAccessDelegate;
274 public void destroy() {
275 mAwContents.destroy();
279 public void onConfigurationChanged(Configuration newConfig) {
280 super.onConfigurationChanged(newConfig);
281 mAwContents.onConfigurationChanged(newConfig);
285 public void onAttachedToWindow() {
286 super.onAttachedToWindow();
287 if (mHardwareView == null || mHardwareView.isReadyToRender()) {
288 mAwContents.onAttachedToWindow();
289 mAttachedContents = true;
291 mHardwareView.setReadyToRenderCallback(new Runnable() {
293 assert !mAttachedContents;
294 mAwContents.onAttachedToWindow();
295 mAttachedContents = true;
302 public void onDetachedFromWindow() {
303 super.onDetachedFromWindow();
304 mAwContents.onDetachedFromWindow();
305 if (mHardwareView != null) {
306 mHardwareView.setReadyToRenderCallback(null);
308 mAttachedContents = false;
312 public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
313 super.onFocusChanged(focused, direction, previouslyFocusedRect);
314 mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect);
318 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
319 return mAwContents.onCreateInputConnection(outAttrs);
323 public boolean onKeyUp(int keyCode, KeyEvent event) {
324 return mAwContents.onKeyUp(keyCode, event);
328 public boolean dispatchKeyEvent(KeyEvent event) {
329 return mAwContents.dispatchKeyEvent(event);
333 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
334 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
335 mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec);
339 public void onSizeChanged(int w, int h, int ow, int oh) {
340 super.onSizeChanged(w, h, ow, oh);
341 mAwContents.onSizeChanged(w, h, ow, oh);
345 public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
346 mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
350 public void onScrollChanged(int l, int t, int oldl, int oldt) {
351 super.onScrollChanged(l, t, oldl, oldt);
352 if (mAwContents != null) {
353 mAwContents.onContainerViewScrollChanged(l, t, oldl, oldt);
358 public void computeScroll() {
359 mAwContents.computeScroll();
363 public void onVisibilityChanged(View changedView, int visibility) {
364 super.onVisibilityChanged(changedView, visibility);
365 mAwContents.onVisibilityChanged(changedView, visibility);
369 public void onWindowVisibilityChanged(int visibility) {
370 super.onWindowVisibilityChanged(visibility);
371 mAwContents.onWindowVisibilityChanged(visibility);
375 public boolean onTouchEvent(MotionEvent ev) {
376 super.onTouchEvent(ev);
377 return mAwContents.onTouchEvent(ev);
381 public void onDraw(Canvas canvas) {
382 if (mHardwareView != null) {
383 mHardwareView.updateScroll(getScrollX(), getScrollY());
385 mAwContents.onDraw(canvas);
386 super.onDraw(canvas);
390 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
391 AccessibilityNodeProvider provider =
392 mAwContents.getAccessibilityNodeProvider();
393 return provider == null ? super.getAccessibilityNodeProvider() : provider;
397 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
398 super.onInitializeAccessibilityNodeInfo(info);
399 info.setClassName(AwContents.class.getName());
400 mAwContents.onInitializeAccessibilityNodeInfo(info);
404 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
405 super.onInitializeAccessibilityEvent(event);
406 event.setClassName(AwContents.class.getName());
407 mAwContents.onInitializeAccessibilityEvent(event);
411 public boolean performAccessibilityAction(int action, Bundle arguments) {
412 return mAwContents.performAccessibilityAction(action, arguments);
415 private class NativeGLDelegate implements AwContents.NativeGLDelegate {
417 public boolean requestDrawGL(Canvas canvas, boolean waitForCompletion,
418 View containerview) {
419 if (mHardwareView == null) return false;
420 mHardwareView.requestRender(canvas, waitForCompletion);
425 public void detachGLFunctor() {
426 if (mHardwareView != null) mHardwareView.detachGLFunctor();
430 // TODO: AwContents could define a generic class that holds an implementation similar to
432 private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
435 public boolean drawChild(Canvas canvas, View child, long drawingTime) {
436 return AwTestContainerView.super.drawChild(canvas, child, drawingTime);
440 public boolean super_onKeyUp(int keyCode, KeyEvent event) {
441 return AwTestContainerView.super.onKeyUp(keyCode, event);
445 public boolean super_dispatchKeyEventPreIme(KeyEvent event) {
446 return AwTestContainerView.super.dispatchKeyEventPreIme(event);
450 public boolean super_dispatchKeyEvent(KeyEvent event) {
451 return AwTestContainerView.super.dispatchKeyEvent(event);
455 public boolean super_onGenericMotionEvent(MotionEvent event) {
456 return AwTestContainerView.super.onGenericMotionEvent(event);
460 public void super_onConfigurationChanged(Configuration newConfig) {
461 AwTestContainerView.super.onConfigurationChanged(newConfig);
465 public void super_scrollTo(int scrollX, int scrollY) {
466 // We're intentionally not calling super.scrollTo here to make testing easier.
467 AwTestContainerView.this.scrollTo(scrollX, scrollY);
468 if (mHardwareView != null) {
469 // Undo the scroll that will be applied because of mHardwareView
470 // being a child of |this|.
471 mHardwareView.setTranslationX(scrollX);
472 mHardwareView.setTranslationY(scrollY);
477 public void overScrollBy(int deltaX, int deltaY,
478 int scrollX, int scrollY,
479 int scrollRangeX, int scrollRangeY,
480 int maxOverScrollX, int maxOverScrollY,
481 boolean isTouchEvent) {
482 // We're intentionally not calling super.scrollTo here to make testing easier.
483 AwTestContainerView.this.overScrollBy(deltaX, deltaY, scrollX, scrollY,
484 scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
488 public void onScrollChanged(int l, int t, int oldl, int oldt) {
489 AwTestContainerView.super.onScrollChanged(l, t, oldl, oldt);
493 public boolean awakenScrollBars() {
494 return AwTestContainerView.super.awakenScrollBars();
498 public boolean super_awakenScrollBars(int startDelay, boolean invalidate) {
499 return AwTestContainerView.super.awakenScrollBars(startDelay, invalidate);
503 public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
504 AwTestContainerView.super.setMeasuredDimension(measuredWidth, measuredHeight);
508 public int super_getScrollBarStyle() {
509 return AwTestContainerView.super.getScrollBarStyle();