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