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