TestShellActivity.java revision e6f3e45dc4d8a2b482183c57c61dd9ea22505c85
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 android.app.Activity;
20import android.content.Intent;
21import android.graphics.Bitmap;
22import android.net.http.SslError;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.Message;
26import android.util.Log;
27import android.view.ViewGroup;
28import android.webkit.HttpAuthHandler;
29import android.webkit.JsPromptResult;
30import android.webkit.JsResult;
31import android.webkit.SslErrorHandler;
32import android.webkit.WebChromeClient;
33import android.webkit.WebSettings;
34import android.webkit.WebStorage;
35import android.webkit.WebView;
36import android.webkit.WebViewClient;
37import android.widget.LinearLayout;
38
39import java.io.File;
40import java.io.FileOutputStream;
41import java.io.IOException;
42import java.net.MalformedURLException;
43import java.net.URL;
44import java.util.Vector;
45
46public class TestShellActivity extends Activity implements LayoutTestController {
47
48    static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
49
50    public class AsyncHandler extends Handler {
51        @Override
52        public void handleMessage(Message msg) {
53            if (msg.what == MSG_TIMEOUT) {
54                mTimedOut = true;
55                mCallback.timedOut(mWebView.getUrl());
56                requestWebKitData();
57                return;
58            } else if (msg.what == MSG_WEBKIT_DATA) {
59                TestShellActivity.this.dump(mTimedOut, (String)msg.obj);
60                return;
61            }
62
63            super.handleMessage(msg);
64        }
65    }
66
67    public void requestWebKitData() {
68        Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA);
69
70        if (mRequestedWebKitData)
71            throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl());
72
73        mRequestedWebKitData = true;
74        switch (mDumpDataType) {
75            case DUMP_AS_TEXT:
76                mWebView.documentAsText(callback);
77                break;
78            case EXT_REPR:
79                mWebView.externalRepresentation(callback);
80                break;
81            default:
82                finished();
83                break;
84        }
85    }
86
87    @Override
88    protected void onCreate(Bundle icicle) {
89        super.onCreate(icicle);
90
91        LinearLayout contentView = new LinearLayout(this);
92        contentView.setOrientation(LinearLayout.VERTICAL);
93        setContentView(contentView);
94
95        mWebView = new WebView(this);
96        mEventSender = new WebViewEventSender(mWebView);
97        mCallbackProxy = new CallbackProxy(mEventSender, this);
98
99        setupWebViewForLayoutTests(mWebView, mCallbackProxy);
100
101        contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f));
102
103        mHandler = new AsyncHandler();
104
105        Intent intent = getIntent();
106        if (intent != null) {
107            executeIntent(intent);
108        }
109    }
110
111    @Override
112    protected void onNewIntent(Intent intent) {
113        super.onNewIntent(intent);
114        executeIntent(intent);
115    }
116
117    private void executeIntent(Intent intent) {
118        resetTestStatus();
119        if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
120            return;
121        }
122
123        mTestUrl = intent.getStringExtra(TEST_URL);
124        if (mTestUrl == null)
125            return;
126
127        mResultFile = intent.getStringExtra(RESULT_FILE);
128        mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
129
130        Log.v(LOGTAG, "  Loading " + mTestUrl);
131        mWebView.loadUrl(mTestUrl);
132
133        if (mTimeoutInMillis > 0) {
134            // Create a timeout timer
135            Message m = mHandler.obtainMessage(MSG_TIMEOUT);
136            mHandler.sendMessageDelayed(m, mTimeoutInMillis);
137        }
138    }
139
140    @Override
141    protected void onStop() {
142        super.onStop();
143        mWebView.stopLoading();
144    }
145
146    @Override
147    protected void onDestroy() {
148        super.onDestroy();
149        mWebView.destroy();
150        mWebView = null;
151    }
152
153    @Override
154    public void onLowMemory() {
155        super.onLowMemory();
156        Log.e(LOGTAG, "Low memory, kill self");
157        System.exit(1);
158    }
159
160    // Dump the page
161    public void dump(boolean timeout, String webkitData) {
162        if (mResultFile == null || mResultFile.length() == 0) {
163            finished();
164            return;
165        }
166
167        try {
168            File parentDir = new File(mResultFile).getParentFile();
169            if (!parentDir.exists()) {
170                parentDir.mkdirs();
171            }
172
173            FileOutputStream os = new FileOutputStream(mResultFile);
174            if (timeout) {
175                Log.w("Layout test: Timeout", mResultFile);
176                os.write(TIMEOUT_STR.getBytes());
177                os.write('\n');
178            }
179            if (mDumpTitleChanges)
180                os.write(mTitleChanges.toString().getBytes());
181            if (mDialogStrings != null)
182                os.write(mDialogStrings.toString().getBytes());
183            mDialogStrings = null;
184            if (mDatabaseCallbackStrings != null)
185                os.write(mDatabaseCallbackStrings.toString().getBytes());
186            mDatabaseCallbackStrings = null;
187            if (mConsoleMessages != null)
188                os.write(mConsoleMessages.toString().getBytes());
189            mConsoleMessages = null;
190            if (webkitData != null)
191                os.write(webkitData.getBytes());
192            os.flush();
193            os.close();
194        } catch (IOException ex) {
195            Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
196        }
197
198        finished();
199    }
200
201    public void setCallback(TestShellCallback callback) {
202        mCallback = callback;
203    }
204
205    public void finished() {
206        if (mCallback != null) {
207            mCallback.finished();
208        }
209    }
210
211    public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) {
212        mDefaultDumpDataType = defaultDumpDataType;
213    }
214
215    // .......................................
216    // LayoutTestController Functions
217    public void dumpAsText() {
218        mDumpDataType = DumpDataType.DUMP_AS_TEXT;
219        if (mWebView != null) {
220            String url = mWebView.getUrl();
221            Log.v(LOGTAG, "dumpAsText called: "+url);
222        }
223    }
224
225    public void waitUntilDone() {
226        mWaitUntilDone = true;
227        String url = mWebView.getUrl();
228        Log.v(LOGTAG, "waitUntilDone called: " + url);
229    }
230
231    public void notifyDone() {
232        String url = mWebView.getUrl();
233        Log.v(LOGTAG, "notifyDone called: " + url);
234        if (mWaitUntilDone) {
235            mWaitUntilDone = false;
236            mChromeClient.onProgressChanged(mWebView, 100);
237        }
238    }
239
240    public void display() {
241        mWebView.invalidate();
242    }
243
244    public void clearBackForwardList() {
245        mWebView.clearHistory();
246
247    }
248
249    public void dumpBackForwardList() {
250        //printf("\n============== Back Forward List ==============\n");
251        // mWebHistory
252        //printf("===============================================\n");
253
254    }
255
256    public void dumpChildFrameScrollPositions() {
257        // TODO Auto-generated method stub
258
259    }
260
261    public void dumpEditingCallbacks() {
262        // TODO Auto-generated method stub
263
264    }
265
266    public void dumpSelectionRect() {
267        // TODO Auto-generated method stub
268
269    }
270
271    public void dumpTitleChanges() {
272        if (!mDumpTitleChanges) {
273            mTitleChanges = new StringBuffer();
274        }
275        mDumpTitleChanges = true;
276    }
277
278    public void keepWebHistory() {
279        if (!mKeepWebHistory) {
280            mWebHistory = new Vector();
281        }
282        mKeepWebHistory = true;
283    }
284
285    public void queueBackNavigation(int howfar) {
286        // TODO Auto-generated method stub
287
288    }
289
290    public void queueForwardNavigation(int howfar) {
291        // TODO Auto-generated method stub
292
293    }
294
295    public void queueLoad(String Url, String frameTarget) {
296        // TODO Auto-generated method stub
297
298    }
299
300    public void queueReload() {
301        mWebView.reload();
302    }
303
304    public void queueScript(String scriptToRunInCurrentContext) {
305        mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
306    }
307
308    public void repaintSweepHorizontally() {
309        // TODO Auto-generated method stub
310
311    }
312
313    public void setAcceptsEditing(boolean b) {
314        // TODO Auto-generated method stub
315
316    }
317
318    public void setMainFrameIsFirstResponder(boolean b) {
319        // TODO Auto-generated method stub
320
321    }
322
323    public void setWindowIsKey(boolean b) {
324        // This is meant to show/hide the window. The best I can find
325        // is setEnabled()
326        mWebView.setEnabled(b);
327    }
328
329    public void testRepaint() {
330        mWebView.invalidate();
331    }
332
333    public void dumpDatabaseCallbacks() {
334        Log.v(LOGTAG, "dumpDatabaseCallbacks called.");
335        mDumpDatabaseCallbacks = true;
336    }
337
338    public void setCanOpenWindows() {
339        Log.v(LOGTAG, "setCanOpenWindows called.");
340        mCanOpenWindows = true;
341    }
342
343    private final WebViewClient mViewClient = new WebViewClient(){
344        @Override
345        public void onPageFinished(WebView view, String url) {
346            Log.v(LOGTAG, "onPageFinished, url=" + url);
347            super.onPageFinished(view, url);
348        }
349
350        @Override
351        public void onPageStarted(WebView view, String url, Bitmap favicon) {
352            Log.v(LOGTAG, "onPageStarted, url=" + url);
353            super.onPageStarted(view, url, favicon);
354        }
355
356        @Override
357        public void onReceivedError(WebView view, int errorCode, String description,
358                String failingUrl) {
359            Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode
360                    + ", desc=" + description + ", url=" + failingUrl);
361            super.onReceivedError(view, errorCode, description, failingUrl);
362        }
363
364        @Override
365        public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
366                String host, String realm) {
367            handler.cancel();
368        }
369
370        @Override
371        public void onReceivedSslError(WebView view, SslErrorHandler handler,
372                SslError error) {
373            handler.proceed();
374        }
375    };
376
377
378    private final WebChromeClient mChromeClient = new WebChromeClient() {
379        @Override
380        public void onProgressChanged(WebView view, int newProgress) {
381            if (newProgress == 100) {
382                if (!mTimedOut && !mWaitUntilDone && !mRequestedWebKitData) {
383                    String url = mWebView.getUrl();
384                    Log.v(LOGTAG, "Finished: "+ url);
385                    mHandler.removeMessages(MSG_TIMEOUT);
386                    requestWebKitData();
387                } else {
388                    String url = mWebView.getUrl();
389                    if (mTimedOut) {
390                        Log.v(LOGTAG, "Timed out before finishing: " + url);
391                    } else if (mWaitUntilDone) {
392                        Log.v(LOGTAG, "Waiting for notifyDone: " + url);
393                    } else if (mRequestedWebKitData) {
394                        Log.v(LOGTAG, "Requested webkit data ready: " + url);
395                    }
396                }
397            }
398        }
399
400        @Override
401        public void onReceivedTitle(WebView view, String title) {
402            if (title.length() > 30)
403                title = "..."+title.substring(title.length()-30);
404            setTitle(title);
405            if (mDumpTitleChanges) {
406                mTitleChanges.append("TITLE CHANGED: ");
407                mTitleChanges.append(title);
408                mTitleChanges.append("\n");
409            }
410        }
411
412        @Override
413        public boolean onJsAlert(WebView view, String url, String message,
414                JsResult result) {
415            if (mDialogStrings == null) {
416                mDialogStrings = new StringBuffer();
417            }
418            mDialogStrings.append("ALERT: ");
419            mDialogStrings.append(message);
420            mDialogStrings.append('\n');
421            result.confirm();
422            return true;
423        }
424
425        @Override
426        public boolean onJsConfirm(WebView view, String url, String message,
427                JsResult result) {
428            if (mDialogStrings == null) {
429                mDialogStrings = new StringBuffer();
430            }
431            mDialogStrings.append("CONFIRM: ");
432            mDialogStrings.append(message);
433            mDialogStrings.append('\n');
434            result.confirm();
435            return true;
436        }
437
438        @Override
439        public boolean onJsPrompt(WebView view, String url, String message,
440                String defaultValue, JsPromptResult result) {
441            if (mDialogStrings == null) {
442                mDialogStrings = new StringBuffer();
443            }
444            mDialogStrings.append("PROMPT: ");
445            mDialogStrings.append(message);
446            mDialogStrings.append(", default text: ");
447            mDialogStrings.append(defaultValue);
448            mDialogStrings.append('\n');
449            result.confirm();
450            return true;
451        }
452
453        @Override
454        public void onExceededDatabaseQuota(String url_str,
455                String databaseIdentifier, long currentQuota,
456                WebStorage.QuotaUpdater callback) {
457            if (mDumpDatabaseCallbacks) {
458                if (mDatabaseCallbackStrings == null) {
459                    mDatabaseCallbackStrings = new StringBuffer();
460                }
461
462                String protocol = "";
463                String host = "";
464                int port = 0;
465
466                try {
467                    URL url = new URL(url_str);
468                    protocol = url.getProtocol();
469                    host = url.getHost();
470                    if (url.getPort() > -1) {
471                        port = url.getPort();
472                    }
473                } catch (MalformedURLException e) {}
474
475                String databaseCallbackString =
476                        "UI DELEGATE DATABASE CALLBACK: " +
477                        "exceededDatabaseQuotaForSecurityOrigin:{" + protocol +
478                        ", " + host + ", " + port + "} database:" +
479                        databaseIdentifier + "\n";
480                Log.v(LOGTAG, "LOG: "+databaseCallbackString);
481                mDatabaseCallbackStrings.append(databaseCallbackString);
482            }
483            // Give 5MB more quota.
484            callback.updateQuota(currentQuota + 1024 * 1024 * 5);
485        }
486
487        @Override
488        public void addMessageToConsole(String message, int lineNumber,
489                String sourceID) {
490            if (mConsoleMessages == null) {
491                mConsoleMessages = new StringBuffer();
492            }
493            String consoleMessage = "CONSOLE MESSAGE: line "
494                    + lineNumber +": "+ message +"\n";
495            mConsoleMessages.append(consoleMessage);
496            Log.v(LOGTAG, "LOG: "+consoleMessage);
497        }
498
499        @Override
500        public boolean onCreateWindow(WebView view, boolean dialog,
501                boolean userGesture, Message resultMsg) {
502            if (!mCanOpenWindows) {
503                return false;
504            }
505
506            // We never display the new window, just create the view and
507            // allow it's content to execute and be recorded by the test
508            // runner.
509
510            WebView newWindowView = new WebView(TestShellActivity.this);
511            setupWebViewForLayoutTests(newWindowView, mCallbackProxy);
512            WebView.WebViewTransport transport =
513                    (WebView.WebViewTransport) resultMsg.obj;
514            transport.setWebView(newWindowView);
515            resultMsg.sendToTarget();
516            return true;
517        }
518    };
519
520    private void resetTestStatus() {
521        mWaitUntilDone = false;
522        mDumpDataType = mDefaultDumpDataType;
523        mTimedOut = false;
524        mDumpTitleChanges = false;
525        mRequestedWebKitData = false;
526        mDumpDatabaseCallbacks = false;
527        mCanOpenWindows = false;
528        mEventSender.resetMouse();
529    }
530
531    private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) {
532        if (webview == null) {
533            return;
534        }
535
536        WebSettings settings = webview.getSettings();
537        settings.setJavaScriptEnabled(true);
538        settings.setJavaScriptCanOpenWindowsAutomatically(true);
539        settings.setSupportMultipleWindows(true);
540        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
541        settings.setDatabaseEnabled(true);
542        settings.setDatabasePath(getDir("databases",0).getAbsolutePath());
543        settings.setDomStorageEnabled(true);
544
545        webview.addJavascriptInterface(callbackProxy, "layoutTestController");
546        webview.addJavascriptInterface(callbackProxy, "eventSender");
547
548        webview.setWebChromeClient(mChromeClient);
549        webview.setWebViewClient(mViewClient);
550    }
551
552    private WebView mWebView;
553    private WebViewEventSender mEventSender;
554    private AsyncHandler mHandler;
555    private TestShellCallback mCallback;
556
557    private CallbackProxy mCallbackProxy;
558
559    private String mTestUrl;
560    private String mResultFile;
561    private int mTimeoutInMillis;
562
563    // States
564    private boolean mTimedOut;
565    private boolean mRequestedWebKitData;
566    private boolean mFinishedRunning;
567
568    // Layout test controller variables.
569    private DumpDataType mDumpDataType;
570    private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR;
571    private boolean mWaitUntilDone;
572    private boolean mDumpTitleChanges;
573    private StringBuffer mTitleChanges;
574    private StringBuffer mDialogStrings;
575    private boolean mKeepWebHistory;
576    private Vector mWebHistory;
577    private boolean mDumpDatabaseCallbacks;
578    private StringBuffer mDatabaseCallbackStrings;
579    private StringBuffer mConsoleMessages;
580    private boolean mCanOpenWindows;
581
582    static final String TIMEOUT_STR = "**Test timeout";
583
584    static final int MSG_TIMEOUT = 0;
585    static final int MSG_WEBKIT_DATA = 1;
586
587    static final String LOGTAG="TestShell";
588
589    static final String TEST_URL = "TestUrl";
590    static final String RESULT_FILE = "ResultFile";
591    static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
592}
593