Home | History | Annotate | Download | only in test
      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.
      4 
      5 package org.chromium.android_webview.test;
      6 
      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;
     24 
     25 import org.chromium.android_webview.AwContents;
     26 import org.chromium.android_webview.shell.DrawGL;
     27 import org.chromium.content.browser.ContentViewCore;
     28 
     29 import javax.microedition.khronos.egl.EGLConfig;
     30 import javax.microedition.khronos.opengles.GL10;
     31 
     32 /**
     33  * A View used for testing the AwContents internals.
     34  *
     35  * This class takes the place android.webkit.WebView would have in the production configuration.
     36  */
     37 public class AwTestContainerView extends FrameLayout {
     38     private AwContents mAwContents;
     39     private AwContents.NativeGLDelegate mNativeGLDelegate;
     40     private AwContents.InternalAccessDelegate mInternalAccessDelegate;
     41 
     42     HardwareView mHardwareView = null;
     43     private boolean mAttachedContents = false;
     44 
     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;
     50 
     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;
     61 
     62         private int mCommittedScrollX = 0;
     63         private int mCommittedScrollY = 0;
     64 
     65         private boolean mHaveSurface = false;
     66         private Runnable mReadyToRenderCallback = null;
     67 
     68         private long mDrawGL = 0;
     69         private long mViewContext = 0;
     70 
     71         public HardwareView(Context context) {
     72             super(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;
     79 
     80                 @Override
     81                 public void onDrawFrame(GL10 gl) {
     82                     HardwareView.this.drawGL(mWidth, mHeight);
     83                 }
     84 
     85                 @Override
     86                 public void onSurfaceChanged(GL10 gl, int width, int height) {
     87                     gl.glViewport(0, 0, width, height);
     88                     gl.glScissor(0, 0, width, height);
     89                     mWidth = width;
     90                     mHeight = height;
     91                 }
     92 
     93                 @Override
     94                 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
     95                 }
     96             });
     97 
     98             setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
     99         }
    100 
    101         public void initialize(long drawGL, long viewContext) {
    102             mDrawGL = drawGL;
    103             mViewContext = viewContext;
    104         }
    105 
    106         public boolean isReadyToRender() {
    107             return mHaveSurface;
    108         }
    109 
    110         public void setReadyToRenderCallback(Runnable runner) {
    111             assert !isReadyToRender() || runner == null;
    112             mReadyToRenderCallback = runner;
    113         }
    114 
    115         @Override
    116         public void surfaceCreated(SurfaceHolder holder) {
    117             boolean didHaveSurface = mHaveSurface;
    118             mHaveSurface = true;
    119             if (!didHaveSurface && mReadyToRenderCallback != null) {
    120                 mReadyToRenderCallback.run();
    121                 mReadyToRenderCallback = null;
    122             }
    123             super.surfaceCreated(holder);
    124         }
    125 
    126         @Override
    127         public void surfaceDestroyed(SurfaceHolder holder) {
    128             mHaveSurface = false;
    129             super.surfaceDestroyed(holder);
    130         }
    131 
    132         public void updateScroll(int x, int y) {
    133             synchronized (mSyncLock) {
    134                 mLastScrollX = x;
    135                 mLastScrollY = y;
    136             }
    137         }
    138 
    139         public void detachGLFunctor() {
    140             synchronized (mSyncLock) {
    141                 mFunctorAttached = false;
    142                 mNeedsProcessGL = false;
    143                 mNeedsDrawGL = false;
    144                 mWaitForCompletion = false;
    145             }
    146         }
    147 
    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;
    155                 } else {
    156                     mNeedsDrawGL = true;
    157                     if (!waitForCompletion) {
    158                         // Wait until SYNC is complete only.
    159                         // Do this every time there was a new frame.
    160                         try {
    161                             while (mNeedsDrawGL) {
    162                                 mSyncLock.wait();
    163                             }
    164                         } catch (InterruptedException e) {
    165                             // ...
    166                         }
    167                     }
    168                 }
    169                 if (waitForCompletion) {
    170                     try {
    171                         while (mWaitForCompletion) {
    172                             mSyncLock.wait();
    173                         }
    174                     } catch (InterruptedException e) {
    175                         // ...
    176                     }
    177                 }
    178             }
    179         }
    180 
    181         public void drawGL(int width, int height) {
    182             final boolean draw;
    183             final boolean process;
    184             final boolean waitForCompletion;
    185 
    186             synchronized (mSyncLock) {
    187                 if (!mFunctorAttached) {
    188                     mSyncLock.notifyAll();
    189                     return;
    190                 }
    191 
    192                 draw = mNeedsDrawGL;
    193                 process = mNeedsProcessGL;
    194                 waitForCompletion = mWaitForCompletion;
    195 
    196                 if (draw) {
    197                     DrawGL.drawGL(mDrawGL, mViewContext, width, height, 0, 0, MODE_SYNC);
    198                     mCommittedScrollX = mLastScrollX;
    199                     mCommittedScrollY = mLastScrollY;
    200                 }
    201                 mNeedsDrawGL = false;
    202                 mNeedsProcessGL = false;
    203                 if (!waitForCompletion) {
    204                     mSyncLock.notifyAll();
    205                 }
    206             }
    207             if (draw) {
    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);
    212             }
    213 
    214             if (waitForCompletion) {
    215                 synchronized (mSyncLock) {
    216                     mWaitForCompletion = false;
    217                     mSyncLock.notifyAll();
    218                 }
    219             }
    220         }
    221     }
    222 
    223     private static boolean sCreatedOnce = false;
    224     private HardwareView createHardwareViewOnlyOnce(Context context) {
    225         if (sCreatedOnce) return null;
    226         sCreatedOnce = true;
    227         return new HardwareView(context);
    228     }
    229 
    230     public AwTestContainerView(Context context, boolean hardwareAccelerated) {
    231         super(context);
    232         if (hardwareAccelerated) {
    233             mHardwareView = createHardwareViewOnlyOnce(context);
    234         }
    235         if (mHardwareView != null) {
    236             addView(mHardwareView,
    237                     new FrameLayout.LayoutParams(
    238                         FrameLayout.LayoutParams.MATCH_PARENT,
    239                         FrameLayout.LayoutParams.MATCH_PARENT));
    240         } else {
    241           setLayerType(LAYER_TYPE_SOFTWARE, null);
    242         }
    243         mNativeGLDelegate = new NativeGLDelegate();
    244         mInternalAccessDelegate = new InternalAccessAdapter();
    245         setOverScrollMode(View.OVER_SCROLL_ALWAYS);
    246         setFocusable(true);
    247         setFocusableInTouchMode(true);
    248     }
    249 
    250     public void initialize(AwContents awContents) {
    251         mAwContents = awContents;
    252         if (mHardwareView != null) {
    253             mHardwareView.initialize(
    254                     mAwContents.getAwDrawGLFunction(), mAwContents.getAwDrawGLViewContext());
    255         }
    256     }
    257 
    258     public ContentViewCore getContentViewCore() {
    259         return mAwContents.getContentViewCore();
    260     }
    261 
    262     public AwContents getAwContents() {
    263         return mAwContents;
    264     }
    265 
    266     public AwContents.NativeGLDelegate getNativeGLDelegate() {
    267         return mNativeGLDelegate;
    268     }
    269 
    270     public AwContents.InternalAccessDelegate getInternalAccessDelegate() {
    271         return mInternalAccessDelegate;
    272     }
    273 
    274     public void destroy() {
    275         mAwContents.destroy();
    276     }
    277 
    278     @Override
    279     public void onConfigurationChanged(Configuration newConfig) {
    280         super.onConfigurationChanged(newConfig);
    281         mAwContents.onConfigurationChanged(newConfig);
    282     }
    283 
    284     @Override
    285     public void onAttachedToWindow() {
    286         super.onAttachedToWindow();
    287         if (mHardwareView == null || mHardwareView.isReadyToRender()) {
    288             mAwContents.onAttachedToWindow();
    289             mAttachedContents = true;
    290         } else {
    291             mHardwareView.setReadyToRenderCallback(new Runnable() {
    292                 public void run() {
    293                     assert !mAttachedContents;
    294                     mAwContents.onAttachedToWindow();
    295                     mAttachedContents = true;
    296                 }
    297             });
    298         }
    299     }
    300 
    301     @Override
    302     public void onDetachedFromWindow() {
    303         super.onDetachedFromWindow();
    304         mAwContents.onDetachedFromWindow();
    305         if (mHardwareView != null) {
    306             mHardwareView.setReadyToRenderCallback(null);
    307         }
    308         mAttachedContents = false;
    309     }
    310 
    311     @Override
    312     public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
    313         super.onFocusChanged(focused, direction, previouslyFocusedRect);
    314         mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect);
    315     }
    316 
    317     @Override
    318     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
    319         return mAwContents.onCreateInputConnection(outAttrs);
    320     }
    321 
    322     @Override
    323     public boolean onKeyUp(int keyCode, KeyEvent event) {
    324         return mAwContents.onKeyUp(keyCode, event);
    325     }
    326 
    327     @Override
    328     public boolean dispatchKeyEvent(KeyEvent event) {
    329         return mAwContents.dispatchKeyEvent(event);
    330     }
    331 
    332     @Override
    333     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    334         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    335         mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec);
    336     }
    337 
    338     @Override
    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);
    342     }
    343 
    344     @Override
    345     public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    346         mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
    347     }
    348 
    349     @Override
    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);
    354         }
    355     }
    356 
    357     @Override
    358     public void computeScroll() {
    359         mAwContents.computeScroll();
    360     }
    361 
    362     @Override
    363     public void onVisibilityChanged(View changedView, int visibility) {
    364         super.onVisibilityChanged(changedView, visibility);
    365         mAwContents.onVisibilityChanged(changedView, visibility);
    366     }
    367 
    368     @Override
    369     public void onWindowVisibilityChanged(int visibility) {
    370         super.onWindowVisibilityChanged(visibility);
    371         mAwContents.onWindowVisibilityChanged(visibility);
    372     }
    373 
    374     @Override
    375     public boolean onTouchEvent(MotionEvent ev) {
    376         super.onTouchEvent(ev);
    377         return mAwContents.onTouchEvent(ev);
    378     }
    379 
    380     @Override
    381     public void onDraw(Canvas canvas) {
    382         if (mHardwareView != null) {
    383             mHardwareView.updateScroll(getScrollX(), getScrollY());
    384         }
    385         mAwContents.onDraw(canvas);
    386         super.onDraw(canvas);
    387     }
    388 
    389     @Override
    390     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
    391         AccessibilityNodeProvider provider =
    392             mAwContents.getAccessibilityNodeProvider();
    393         return provider == null ? super.getAccessibilityNodeProvider() : provider;
    394     }
    395 
    396     @Override
    397     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    398         super.onInitializeAccessibilityNodeInfo(info);
    399         info.setClassName(AwContents.class.getName());
    400         mAwContents.onInitializeAccessibilityNodeInfo(info);
    401     }
    402 
    403     @Override
    404     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    405         super.onInitializeAccessibilityEvent(event);
    406         event.setClassName(AwContents.class.getName());
    407         mAwContents.onInitializeAccessibilityEvent(event);
    408     }
    409 
    410     @Override
    411     public boolean performAccessibilityAction(int action, Bundle arguments) {
    412         return mAwContents.performAccessibilityAction(action, arguments);
    413     }
    414 
    415     private class NativeGLDelegate implements AwContents.NativeGLDelegate {
    416         @Override
    417         public boolean requestDrawGL(Canvas canvas, boolean waitForCompletion,
    418                 View containerview) {
    419             if (mHardwareView == null) return false;
    420             mHardwareView.requestRender(canvas, waitForCompletion);
    421             return true;
    422         }
    423 
    424         @Override
    425         public void detachGLFunctor() {
    426             if (mHardwareView != null) mHardwareView.detachGLFunctor();
    427         }
    428     }
    429 
    430     // TODO: AwContents could define a generic class that holds an implementation similar to
    431     // the one below.
    432     private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
    433 
    434         @Override
    435         public boolean drawChild(Canvas canvas, View child, long drawingTime) {
    436             return AwTestContainerView.super.drawChild(canvas, child, drawingTime);
    437         }
    438 
    439         @Override
    440         public boolean super_onKeyUp(int keyCode, KeyEvent event) {
    441             return AwTestContainerView.super.onKeyUp(keyCode, event);
    442         }
    443 
    444         @Override
    445         public boolean super_dispatchKeyEventPreIme(KeyEvent event) {
    446             return AwTestContainerView.super.dispatchKeyEventPreIme(event);
    447         }
    448 
    449         @Override
    450         public boolean super_dispatchKeyEvent(KeyEvent event) {
    451             return AwTestContainerView.super.dispatchKeyEvent(event);
    452         }
    453 
    454         @Override
    455         public boolean super_onGenericMotionEvent(MotionEvent event) {
    456             return AwTestContainerView.super.onGenericMotionEvent(event);
    457         }
    458 
    459         @Override
    460         public void super_onConfigurationChanged(Configuration newConfig) {
    461             AwTestContainerView.super.onConfigurationChanged(newConfig);
    462         }
    463 
    464         @Override
    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);
    473             }
    474         }
    475 
    476         @Override
    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);
    485         }
    486 
    487         @Override
    488         public void onScrollChanged(int l, int t, int oldl, int oldt) {
    489             AwTestContainerView.super.onScrollChanged(l, t, oldl, oldt);
    490         }
    491 
    492         @Override
    493         public boolean awakenScrollBars() {
    494             return AwTestContainerView.super.awakenScrollBars();
    495         }
    496 
    497         @Override
    498         public boolean super_awakenScrollBars(int startDelay, boolean invalidate) {
    499             return AwTestContainerView.super.awakenScrollBars(startDelay, invalidate);
    500         }
    501 
    502         @Override
    503         public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    504             AwTestContainerView.super.setMeasuredDimension(measuredWidth, measuredHeight);
    505         }
    506 
    507         @Override
    508         public int super_getScrollBarStyle() {
    509             return AwTestContainerView.super.getScrollBarStyle();
    510         }
    511     }
    512 }
    513