TestShellActivity.java revision 5dc0c8253b368239a90d7176ff6325ae5ba371b3
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dumprendertree;
18
19import com.android.dumprendertree.forwarder.ForwardService;
20
21import android.app.Activity;
22import android.app.AlertDialog;
23import android.content.Context;
24import android.content.DialogInterface;
25import android.content.Intent;
26import android.content.DialogInterface.OnClickListener;
27import android.graphics.Bitmap;
28import android.graphics.Canvas;
29import android.graphics.Bitmap.CompressFormat;
30import android.graphics.Bitmap.Config;
31import android.net.http.SslError;
32import android.os.Bundle;
33import android.os.Handler;
34import android.os.Message;
35import android.util.Log;
36import android.view.ViewGroup;
37import android.view.Window;
38import android.webkit.ConsoleMessage;
39import android.webkit.GeolocationPermissions;
40import android.webkit.HttpAuthHandler;
41import android.webkit.JsPromptResult;
42import android.webkit.JsResult;
43import android.webkit.SslErrorHandler;
44import android.webkit.WebChromeClient;
45import android.webkit.WebSettings;
46import android.webkit.WebStorage;
47import android.webkit.WebView;
48import android.webkit.WebViewClient;
49import android.widget.LinearLayout;
50
51import java.io.BufferedReader;
52import java.io.File;
53import java.io.FileOutputStream;
54import java.io.FileReader;
55import java.io.IOException;
56import java.net.MalformedURLException;
57import java.net.URL;
58import java.util.HashMap;
59import java.util.Map;
60import java.util.Vector;
61
62public class TestShellActivity extends Activity implements LayoutTestController {
63
64    static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
65
66    // String constants for use with layoutTestController.overridePreferences
67    private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED = "WebKitOfflineWebApplicationCacheEnabled";
68
69    public class AsyncHandler extends Handler {
70        @Override
71        public void handleMessage(Message msg) {
72            if (msg.what == MSG_TIMEOUT) {
73                mTimedOut = true;
74                if (mCallback != null)
75                    mCallback.timedOut(mWebView.getUrl());
76                if (!mRequestedWebKitData) {
77                    requestWebKitData();
78                } else {
79                    // if timed out and webkit data has been dumped before
80                    // finish directly
81                    finished();
82                }
83                return;
84            } else if (msg.what == MSG_WEBKIT_DATA) {
85                TestShellActivity.this.dump(mTimedOut, (String)msg.obj);
86                return;
87            }
88
89            super.handleMessage(msg);
90        }
91    }
92
93    public void requestWebKitData() {
94        Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA);
95
96        if (mRequestedWebKitData)
97            throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl());
98
99        mRequestedWebKitData = true;
100        Log.v(LOGTAG, "message sent to WebView to dump text.");
101        switch (mDumpDataType) {
102            case DUMP_AS_TEXT:
103                callback.arg1 = mDumpTopFrameAsText ? 1 : 0;
104                callback.arg2 = mDumpChildFramesAsText ? 1 : 0;
105                mWebView.documentAsText(callback);
106                break;
107            case EXT_REPR:
108                mWebView.externalRepresentation(callback);
109                break;
110            default:
111                finished();
112                break;
113        }
114    }
115
116    public void clearCache() {
117      mWebView.freeMemory();
118    }
119
120    @Override
121    protected void onCreate(Bundle icicle) {
122        super.onCreate(icicle);
123        requestWindowFeature(Window.FEATURE_PROGRESS);
124
125        LinearLayout contentView = new LinearLayout(this);
126        contentView.setOrientation(LinearLayout.VERTICAL);
127        setContentView(contentView);
128
129        mWebView = new WebView(this);
130        mEventSender = new WebViewEventSender(mWebView);
131        mCallbackProxy = new CallbackProxy(mEventSender, this);
132
133        mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController");
134        mWebView.addJavascriptInterface(mCallbackProxy, "eventSender");
135        setupWebViewForLayoutTests(mWebView, mCallbackProxy);
136
137        contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0.0f));
138
139        mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
140
141        // Expose window.gc function to JavaScript. JSC build exposes
142        // this function by default, but V8 requires the flag to turn it on.
143        // WebView::setJsFlags is noop in JSC build.
144        mWebView.setJsFlags("--expose_gc");
145
146        mHandler = new AsyncHandler();
147
148        Intent intent = getIntent();
149        if (intent != null) {
150            executeIntent(intent);
151        }
152    }
153
154    @Override
155    protected void onNewIntent(Intent intent) {
156        super.onNewIntent(intent);
157        executeIntent(intent);
158    }
159
160    private void executeIntent(Intent intent) {
161        resetTestStatus();
162        if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
163            return;
164        }
165
166        mTotalTestCount = intent.getIntExtra(TOTAL_TEST_COUNT, mTotalTestCount);
167        mCurrentTestNumber = intent.getIntExtra(CURRENT_TEST_NUMBER, mCurrentTestNumber);
168
169        mTestUrl = intent.getStringExtra(TEST_URL);
170        if (mTestUrl == null) {
171            mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST);
172            if(mUiAutoTestPath != null) {
173                beginUiAutoTest();
174            }
175            return;
176        }
177
178        mResultFile = intent.getStringExtra(RESULT_FILE);
179        mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
180        mGetDrawtime = intent.getBooleanExtra(GET_DRAW_TIME, false);
181        mSaveImagePath = intent.getStringExtra(SAVE_IMAGE);
182        setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount);
183        float ratio = (float)mCurrentTestNumber / mTotalTestCount;
184        int progress = (int)(ratio * Window.PROGRESS_END);
185        getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress);
186
187        Log.v(LOGTAG, "  Loading " + mTestUrl);
188        mWebView.loadUrl(mTestUrl);
189
190        if (mTimeoutInMillis > 0) {
191            // Create a timeout timer
192            Message m = mHandler.obtainMessage(MSG_TIMEOUT);
193            mHandler.sendMessageDelayed(m, mTimeoutInMillis);
194        }
195    }
196
197    private void beginUiAutoTest() {
198        try {
199            mTestListReader = new BufferedReader(
200                    new FileReader(mUiAutoTestPath));
201        } catch (IOException ioe) {
202            Log.e(LOGTAG, "Failed to open test list for read.", ioe);
203            finishUiAutoTest();
204            return;
205        }
206        moveToNextTest();
207    }
208
209    private void finishUiAutoTest() {
210        try {
211            if(mTestListReader != null)
212                mTestListReader.close();
213        } catch (IOException ioe) {
214            Log.w(LOGTAG, "Failed to close test list file.", ioe);
215        }
216        ForwardService.getForwardService().stopForwardService();
217        finished();
218    }
219
220    private void moveToNextTest() {
221        String url = null;
222        try {
223            url = mTestListReader.readLine();
224        } catch (IOException ioe) {
225            Log.e(LOGTAG, "Failed to read next test.", ioe);
226            finishUiAutoTest();
227            return;
228        }
229        if (url == null) {
230            mUiAutoTestPath = null;
231            finishUiAutoTest();
232            AlertDialog.Builder builder = new AlertDialog.Builder(this);
233            builder.setMessage("All tests finished. Exit?")
234                   .setCancelable(false)
235                   .setPositiveButton("Yes", new OnClickListener(){
236                       public void onClick(DialogInterface dialog, int which) {
237                           TestShellActivity.this.finish();
238                       }
239                   })
240                   .setNegativeButton("No", new OnClickListener(){
241                       public void onClick(DialogInterface dialog, int which) {
242                           dialog.cancel();
243                       }
244                   });
245            builder.create().show();
246            return;
247        }
248        Intent intent = new Intent(Intent.ACTION_VIEW);
249        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
250        intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(url));
251        intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, ++mCurrentTestNumber);
252        intent.putExtra(TIMEOUT_IN_MILLIS, 10000);
253        executeIntent(intent);
254    }
255
256    @Override
257    protected void onStop() {
258        super.onStop();
259        mWebView.stopLoading();
260    }
261
262    @Override
263    protected void onDestroy() {
264        super.onDestroy();
265        mWebView.destroy();
266        mWebView = null;
267    }
268
269    @Override
270    public void onLowMemory() {
271        super.onLowMemory();
272        Log.e(LOGTAG, "Low memory, clearing caches");
273        mWebView.freeMemory();
274    }
275
276    // Dump the page
277    public void dump(boolean timeout, String webkitData) {
278        mDumpWebKitData = true;
279        if (mResultFile == null || mResultFile.length() == 0) {
280            finished();
281            return;
282        }
283
284        try {
285            File parentDir = new File(mResultFile).getParentFile();
286            if (!parentDir.exists()) {
287                parentDir.mkdirs();
288            }
289
290            FileOutputStream os = new FileOutputStream(mResultFile);
291            if (timeout) {
292                Log.w("Layout test: Timeout", mResultFile);
293                os.write(TIMEOUT_STR.getBytes());
294                os.write('\n');
295            }
296            if (mDumpTitleChanges)
297                os.write(mTitleChanges.toString().getBytes());
298            if (mDialogStrings != null)
299                os.write(mDialogStrings.toString().getBytes());
300            mDialogStrings = null;
301            if (mDatabaseCallbackStrings != null)
302                os.write(mDatabaseCallbackStrings.toString().getBytes());
303            mDatabaseCallbackStrings = null;
304            if (mConsoleMessages != null)
305                os.write(mConsoleMessages.toString().getBytes());
306            mConsoleMessages = null;
307            if (webkitData != null)
308                os.write(webkitData.getBytes());
309            os.flush();
310            os.close();
311        } catch (IOException ex) {
312            Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
313        }
314
315        finished();
316    }
317
318    public void setCallback(TestShellCallback callback) {
319        mCallback = callback;
320    }
321
322    public boolean finished() {
323        if (canMoveToNextTest()) {
324            mHandler.removeMessages(MSG_TIMEOUT);
325            if (mUiAutoTestPath != null) {
326                //don't really finish here
327                moveToNextTest();
328            } else {
329                if (mCallback != null) {
330                    mCallback.finished();
331                }
332            }
333            return true;
334        }
335        return false;
336    }
337
338    public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) {
339        mDefaultDumpDataType = defaultDumpDataType;
340    }
341
342    // .......................................
343    // LayoutTestController Functions
344    public void dumpAsText() {
345        mDumpDataType = DumpDataType.DUMP_AS_TEXT;
346        mDumpTopFrameAsText = true;
347        if (mWebView != null) {
348            String url = mWebView.getUrl();
349            Log.v(LOGTAG, "dumpAsText called: "+url);
350        }
351    }
352
353    public void dumpChildFramesAsText() {
354        mDumpDataType = DumpDataType.DUMP_AS_TEXT;
355        mDumpChildFramesAsText = true;
356        if (mWebView != null) {
357            String url = mWebView.getUrl();
358            Log.v(LOGTAG, "dumpChildFramesAsText called: "+url);
359        }
360    }
361
362    public void waitUntilDone() {
363        mWaitUntilDone = true;
364        String url = mWebView.getUrl();
365        Log.v(LOGTAG, "waitUntilDone called: " + url);
366    }
367
368    public void notifyDone() {
369        String url = mWebView.getUrl();
370        Log.v(LOGTAG, "notifyDone called: " + url);
371        if (mWaitUntilDone) {
372            mWaitUntilDone = false;
373            if (!mRequestedWebKitData && !mTimedOut && !finished()) {
374                requestWebKitData();
375            }
376        }
377    }
378
379    public void display() {
380        mWebView.invalidate();
381    }
382
383    public void clearBackForwardList() {
384        mWebView.clearHistory();
385
386    }
387
388    public void dumpBackForwardList() {
389        //printf("\n============== Back Forward List ==============\n");
390        // mWebHistory
391        //printf("===============================================\n");
392
393    }
394
395    public void dumpChildFrameScrollPositions() {
396        // TODO Auto-generated method stub
397
398    }
399
400    public void dumpEditingCallbacks() {
401        // TODO Auto-generated method stub
402
403    }
404
405    public void dumpSelectionRect() {
406        // TODO Auto-generated method stub
407
408    }
409
410    public void dumpTitleChanges() {
411        if (!mDumpTitleChanges) {
412            mTitleChanges = new StringBuffer();
413        }
414        mDumpTitleChanges = true;
415    }
416
417    public void keepWebHistory() {
418        if (!mKeepWebHistory) {
419            mWebHistory = new Vector();
420        }
421        mKeepWebHistory = true;
422    }
423
424    public void queueBackNavigation(int howfar) {
425        // TODO Auto-generated method stub
426
427    }
428
429    public void queueForwardNavigation(int howfar) {
430        // TODO Auto-generated method stub
431
432    }
433
434    public void queueLoad(String Url, String frameTarget) {
435        // TODO Auto-generated method stub
436
437    }
438
439    public void queueReload() {
440        mWebView.reload();
441    }
442
443    public void queueScript(String scriptToRunInCurrentContext) {
444        mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
445    }
446
447    public void repaintSweepHorizontally() {
448        // TODO Auto-generated method stub
449
450    }
451
452    public void setAcceptsEditing(boolean b) {
453        // TODO Auto-generated method stub
454
455    }
456
457    public void setMainFrameIsFirstResponder(boolean b) {
458        // TODO Auto-generated method stub
459
460    }
461
462    public void setWindowIsKey(boolean b) {
463        // This is meant to show/hide the window. The best I can find
464        // is setEnabled()
465        mWebView.setEnabled(b);
466    }
467
468    public void testRepaint() {
469        mWebView.invalidate();
470    }
471
472    public void dumpDatabaseCallbacks() {
473        Log.v(LOGTAG, "dumpDatabaseCallbacks called.");
474        mDumpDatabaseCallbacks = true;
475    }
476
477    public void setCanOpenWindows() {
478        Log.v(LOGTAG, "setCanOpenWindows called.");
479        mCanOpenWindows = true;
480    }
481
482    /**
483     * Sets the Geolocation permission state to be used for all future requests.
484     */
485    public void setGeolocationPermission(boolean allow) {
486        mGeolocationPermissionSet = true;
487        mGeolocationPermission = allow;
488    }
489
490    public void overridePreference(String key, boolean value) {
491        // TODO: We should look up the correct WebView for the frame which
492        // called the layoutTestController method. Currently, we just use the
493        // WebView for the main frame. EventSender suffers from the same
494        // problem.
495        if (key.equals(WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED)) {
496            mWebView.getSettings().setAppCacheEnabled(value);
497        }
498    }
499
500    public void setXSSAuditorEnabled (boolean flag) {
501        mWebView.getSettings().setXSSAuditorEnabled(flag);
502    }
503
504    private final WebViewClient mViewClient = new WebViewClient(){
505        @Override
506        public void onPageFinished(WebView view, String url) {
507            Log.v(LOGTAG, "onPageFinished, url=" + url);
508            mPageFinished = true;
509            // get page draw time
510            if (FsUtils.isTestPageUrl(url)) {
511                if (mGetDrawtime) {
512                    long[] times = new long[DRAW_RUNS];
513                    times = getDrawWebViewTime(mWebView, DRAW_RUNS);
514                    FsUtils.writeDrawTime(DRAW_TIME_LOG, url, times);
515                }
516                if (mSaveImagePath != null) {
517                    String name = FsUtils.getLastSegmentInPath(url);
518                    drawPageToFile(mSaveImagePath + "/" + name + ".png", mWebView);
519                }
520            }
521
522            // Calling finished() will check if we've met all the conditions for completing
523            // this test and move to the next one if we are ready. Otherwise we ask WebCore to
524            // dump the page.
525            if (finished()) {
526                return;
527            }
528
529            if (!mWaitUntilDone && !mRequestedWebKitData && !mTimedOut) {
530                requestWebKitData();
531            } else {
532                if (mWaitUntilDone) {
533                    Log.v(LOGTAG, "page finished loading but waiting for notifyDone to be called: " + url);
534                }
535
536                if (mRequestedWebKitData) {
537                    Log.v(LOGTAG, "page finished loading but webkit data has already been requested: " + url);
538                }
539
540                if (mTimedOut) {
541                    Log.v(LOGTAG, "page finished loading but already timed out: " + url);
542                }
543            }
544
545            super.onPageFinished(view, url);
546        }
547
548        @Override
549        public void onPageStarted(WebView view, String url, Bitmap favicon) {
550            Log.v(LOGTAG, "onPageStarted, url=" + url);
551            mPageFinished = false;
552            super.onPageStarted(view, url, favicon);
553        }
554
555        @Override
556        public void onReceivedError(WebView view, int errorCode, String description,
557                String failingUrl) {
558            Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode
559                    + ", desc=" + description + ", url=" + failingUrl);
560            super.onReceivedError(view, errorCode, description, failingUrl);
561        }
562
563        @Override
564        public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
565                String host, String realm) {
566            if (handler.useHttpAuthUsernamePassword() && view != null) {
567                String[] credentials = view.getHttpAuthUsernamePassword(host, realm);
568                if (credentials != null && credentials.length == 2) {
569                    handler.proceed(credentials[0], credentials[1]);
570                    return;
571                }
572            }
573            handler.cancel();
574        }
575
576        @Override
577        public void onReceivedSslError(WebView view, SslErrorHandler handler,
578                SslError error) {
579            handler.proceed();
580        }
581    };
582
583
584    private final WebChromeClient mChromeClient = new WebChromeClient() {
585        @Override
586        public void onReceivedTitle(WebView view, String title) {
587            setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount + ": "+ title);
588            if (mDumpTitleChanges) {
589                mTitleChanges.append("TITLE CHANGED: ");
590                mTitleChanges.append(title);
591                mTitleChanges.append("\n");
592            }
593        }
594
595        @Override
596        public boolean onJsAlert(WebView view, String url, String message,
597                JsResult result) {
598            if (mDialogStrings == null) {
599                mDialogStrings = new StringBuffer();
600            }
601            mDialogStrings.append("ALERT: ");
602            mDialogStrings.append(message);
603            mDialogStrings.append('\n');
604            result.confirm();
605            return true;
606        }
607
608        @Override
609        public boolean onJsConfirm(WebView view, String url, String message,
610                JsResult result) {
611            if (mDialogStrings == null) {
612                mDialogStrings = new StringBuffer();
613            }
614            mDialogStrings.append("CONFIRM: ");
615            mDialogStrings.append(message);
616            mDialogStrings.append('\n');
617            result.confirm();
618            return true;
619        }
620
621        @Override
622        public boolean onJsPrompt(WebView view, String url, String message,
623                String defaultValue, JsPromptResult result) {
624            if (mDialogStrings == null) {
625                mDialogStrings = new StringBuffer();
626            }
627            mDialogStrings.append("PROMPT: ");
628            mDialogStrings.append(message);
629            mDialogStrings.append(", default text: ");
630            mDialogStrings.append(defaultValue);
631            mDialogStrings.append('\n');
632            result.confirm();
633            return true;
634        }
635
636        @Override
637        public boolean onJsTimeout() {
638            Log.v(LOGTAG, "JavaScript timeout");
639            return false;
640        }
641
642        @Override
643        public void onExceededDatabaseQuota(String url_str,
644                String databaseIdentifier, long currentQuota,
645                long estimatedSize, long totalUsedQuota,
646                WebStorage.QuotaUpdater callback) {
647            if (mDumpDatabaseCallbacks) {
648                if (mDatabaseCallbackStrings == null) {
649                    mDatabaseCallbackStrings = new StringBuffer();
650                }
651
652                String protocol = "";
653                String host = "";
654                int port = 0;
655
656                try {
657                    URL url = new URL(url_str);
658                    protocol = url.getProtocol();
659                    host = url.getHost();
660                    if (url.getPort() > -1) {
661                        port = url.getPort();
662                    }
663                } catch (MalformedURLException e) {}
664
665                String databaseCallbackString =
666                        "UI DELEGATE DATABASE CALLBACK: " +
667                        "exceededDatabaseQuotaForSecurityOrigin:{" + protocol +
668                        ", " + host + ", " + port + "} database:" +
669                        databaseIdentifier + "\n";
670                Log.v(LOGTAG, "LOG: "+databaseCallbackString);
671                mDatabaseCallbackStrings.append(databaseCallbackString);
672            }
673            // Give 5MB more quota.
674            callback.updateQuota(currentQuota + 1024 * 1024 * 5);
675        }
676
677        /**
678         * Instructs the client to show a prompt to ask the user to set the
679         * Geolocation permission state for the specified origin.
680         */
681        @Override
682        public void onGeolocationPermissionsShowPrompt(String origin,
683                GeolocationPermissions.Callback callback) {
684            if (mGeolocationPermissionSet) {
685                callback.invoke(origin, mGeolocationPermission, false);
686            }
687        }
688
689        @Override
690        public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
691            String msg = "CONSOLE MESSAGE: line " + consoleMessage.lineNumber() + ": "
692                    + consoleMessage.message() + "\n";
693            if (mConsoleMessages == null) {
694                mConsoleMessages = new StringBuffer();
695            }
696            mConsoleMessages.append(msg);
697            Log.v(LOGTAG, "LOG: " + msg);
698            // the rationale here is that if there's an error of either type, and the test was
699            // waiting for "notifyDone" signal to finish, then there's no point in waiting
700            // anymore because the JS execution is already terminated at this point and a
701            // "notifyDone" will never come out so it's just wasting time till timeout kicks in
702            if (msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:")
703                    && mWaitUntilDone) {
704                Log.w(LOGTAG, "Terminating test case on uncaught ReferenceError or TypeError.");
705                mHandler.postDelayed(new Runnable() {
706                    public void run() {
707                        notifyDone();
708                    }
709                }, 500);
710            }
711            return true;
712        }
713
714        @Override
715        public boolean onCreateWindow(WebView view, boolean dialog,
716                boolean userGesture, Message resultMsg) {
717            if (!mCanOpenWindows) {
718                // We can't open windows, so just send null back.
719                WebView.WebViewTransport transport =
720                        (WebView.WebViewTransport) resultMsg.obj;
721                transport.setWebView(null);
722                resultMsg.sendToTarget();
723                return true;
724            }
725
726            // We never display the new window, just create the view and
727            // allow it's content to execute and be recorded by the test
728            // runner.
729
730            HashMap<String, Object> jsIfaces = new HashMap<String, Object>();
731            jsIfaces.put("layoutTestController", mCallbackProxy);
732            jsIfaces.put("eventSender", mCallbackProxy);
733            WebView newWindowView = new NewWindowWebView(TestShellActivity.this, jsIfaces);
734            setupWebViewForLayoutTests(newWindowView, mCallbackProxy);
735            WebView.WebViewTransport transport =
736                    (WebView.WebViewTransport) resultMsg.obj;
737            transport.setWebView(newWindowView);
738            resultMsg.sendToTarget();
739            return true;
740        }
741
742        @Override
743        public void onCloseWindow(WebView view) {
744            view.destroy();
745        }
746    };
747
748    private static class NewWindowWebView extends WebView {
749        public NewWindowWebView(Context context, Map<String, Object> jsIfaces) {
750            super(context, null, 0, jsIfaces);
751        }
752    }
753
754    private void resetTestStatus() {
755        mWaitUntilDone = false;
756        mDumpDataType = mDefaultDumpDataType;
757        mDumpTopFrameAsText = false;
758        mDumpChildFramesAsText = false;
759        mTimedOut = false;
760        mDumpTitleChanges = false;
761        mRequestedWebKitData = false;
762        mDumpDatabaseCallbacks = false;
763        mCanOpenWindows = false;
764        mEventSender.resetMouse();
765        mEventSender.clearTouchPoints();
766        mEventSender.clearTouchMetaState();
767        mPageFinished = false;
768        mDumpWebKitData = false;
769        mGetDrawtime = false;
770        mSaveImagePath = null;
771        setDefaultWebSettings(mWebView);
772    }
773
774    private long[] getDrawWebViewTime(WebView view, int count) {
775        if (count == 0)
776            return null;
777        long[] ret = new long[count];
778        long start;
779        Canvas canvas = new Canvas();
780        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Config.ARGB_8888);
781        canvas.setBitmap(bitmap);
782        for (int i = 0; i < count; i++) {
783            start = System.currentTimeMillis();
784            view.draw(canvas);
785            ret[i] = System.currentTimeMillis() - start;
786        }
787        return ret;
788    }
789
790    private void drawPageToFile(String fileName, WebView view) {
791        Canvas canvas = new Canvas();
792        Bitmap bitmap = Bitmap.createBitmap(view.getContentWidth(), view.getContentHeight(),
793                Config.ARGB_8888);
794        canvas.setBitmap(bitmap);
795        view.drawPage(canvas);
796        try {
797            FileOutputStream fos = new FileOutputStream(fileName);
798            if(!bitmap.compress(CompressFormat.PNG, 90, fos)) {
799                Log.w(LOGTAG, "Failed to compress and save image.");
800            }
801        } catch (IOException ioe) {
802            Log.e(LOGTAG, "", ioe);
803        }
804        bitmap.recycle();
805    }
806
807    private boolean canMoveToNextTest() {
808        return (mDumpWebKitData && mPageFinished && !mWaitUntilDone) || mTimedOut;
809    }
810
811    private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) {
812        if (webview == null) {
813            return;
814        }
815
816        setDefaultWebSettings(webview);
817
818        webview.setWebChromeClient(mChromeClient);
819        webview.setWebViewClient(mViewClient);
820        // Setting a touch interval of -1 effectively disables the optimisation in WebView
821        // that stops repeated touch events flooding WebCore. The Event Sender only sends a
822        // single event rather than a stream of events (like what would generally happen in
823        // a real use of touch events in a WebView)  and so if the WebView drops the event,
824        // the test will fail as the test expects one callback for every touch it synthesizes.
825        webview.setTouchInterval(-1);
826    }
827
828    public void setDefaultWebSettings(WebView webview) {
829        WebSettings settings = webview.getSettings();
830        settings.setAppCacheEnabled(true);
831        settings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
832        settings.setAppCacheMaxSize(Long.MAX_VALUE);
833        settings.setJavaScriptEnabled(true);
834        settings.setJavaScriptCanOpenWindowsAutomatically(true);
835        settings.setSupportMultipleWindows(true);
836        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
837        settings.setDatabaseEnabled(true);
838        settings.setDatabasePath(getDir("databases",0).getAbsolutePath());
839        settings.setDomStorageEnabled(true);
840        settings.setWorkersEnabled(false);
841    }
842
843    private WebView mWebView;
844    private WebViewEventSender mEventSender;
845    private AsyncHandler mHandler;
846    private TestShellCallback mCallback;
847
848    private CallbackProxy mCallbackProxy;
849
850    private String mTestUrl;
851    private String mResultFile;
852    private int mTimeoutInMillis;
853    private String mUiAutoTestPath;
854    private String mSaveImagePath;
855    private BufferedReader mTestListReader;
856    private boolean mGetDrawtime;
857    private int mTotalTestCount;
858    private int mCurrentTestNumber;
859
860    // States
861    private boolean mTimedOut;
862    private boolean mRequestedWebKitData;
863    private boolean mFinishedRunning;
864
865    // Layout test controller variables.
866    private DumpDataType mDumpDataType;
867    private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR;
868    private boolean mDumpTopFrameAsText;
869    private boolean mDumpChildFramesAsText;
870    private boolean mWaitUntilDone;
871    private boolean mDumpTitleChanges;
872    private StringBuffer mTitleChanges;
873    private StringBuffer mDialogStrings;
874    private boolean mKeepWebHistory;
875    private Vector mWebHistory;
876    private boolean mDumpDatabaseCallbacks;
877    private StringBuffer mDatabaseCallbackStrings;
878    private StringBuffer mConsoleMessages;
879    private boolean mCanOpenWindows;
880
881    private boolean mPageFinished = false;
882    private boolean mDumpWebKitData = false;
883
884    static final String TIMEOUT_STR = "**Test timeout";
885
886    static final int MSG_TIMEOUT = 0;
887    static final int MSG_WEBKIT_DATA = 1;
888
889    static final String LOGTAG="TestShell";
890
891    static final String TEST_URL = "TestUrl";
892    static final String RESULT_FILE = "ResultFile";
893    static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
894    static final String UI_AUTO_TEST = "UiAutoTest";
895    static final String GET_DRAW_TIME = "GetDrawTime";
896    static final String SAVE_IMAGE = "SaveImage";
897    static final String TOTAL_TEST_COUNT = "TestCount";
898    static final String CURRENT_TEST_NUMBER = "TestNumber";
899
900    static final int DRAW_RUNS = 5;
901    static final String DRAW_TIME_LOG = "/sdcard/android/page_draw_time.txt";
902
903    private boolean mGeolocationPermissionSet;
904    private boolean mGeolocationPermission;
905}
906