Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / android_webview / test / shell / src / org / chromium / android_webview / test / AwTestContainerView.java
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 }