• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 
17 package android.webkit;
18 
19 import android.accessibilityservice.AccessibilityServiceInfo;
20 import android.animation.ObjectAnimator;
21 import android.annotation.Widget;
22 import android.app.ActivityManager;
23 import android.app.AlertDialog;
24 import android.content.BroadcastReceiver;
25 import android.content.ClipData;
26 import android.content.ClipboardManager;
27 import android.content.ComponentCallbacks2;
28 import android.content.Context;
29 import android.content.DialogInterface;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.pm.PackageManager;
33 import android.content.res.Configuration;
34 import android.database.DataSetObserver;
35 import android.graphics.Bitmap;
36 import android.graphics.BitmapFactory;
37 import android.graphics.BitmapShader;
38 import android.graphics.Canvas;
39 import android.graphics.Color;
40 import android.graphics.ColorFilter;
41 import android.graphics.DrawFilter;
42 import android.graphics.Paint;
43 import android.graphics.PaintFlagsDrawFilter;
44 import android.graphics.Picture;
45 import android.graphics.Point;
46 import android.graphics.PointF;
47 import android.graphics.Rect;
48 import android.graphics.RectF;
49 import android.graphics.Region;
50 import android.graphics.RegionIterator;
51 import android.graphics.Shader;
52 import android.graphics.drawable.Drawable;
53 import android.net.Proxy;
54 import android.net.ProxyProperties;
55 import android.net.Uri;
56 import android.net.http.SslCertificate;
57 import android.os.AsyncTask;
58 import android.os.Build;
59 import android.os.Bundle;
60 import android.os.Handler;
61 import android.os.Looper;
62 import android.os.Message;
63 import android.os.SystemClock;
64 import android.security.KeyChain;
65 import android.text.Editable;
66 import android.text.InputType;
67 import android.text.Selection;
68 import android.text.TextUtils;
69 import android.util.DisplayMetrics;
70 import android.util.EventLog;
71 import android.util.Log;
72 import android.view.Gravity;
73 import android.view.HapticFeedbackConstants;
74 import android.view.HardwareCanvas;
75 import android.view.InputDevice;
76 import android.view.KeyCharacterMap;
77 import android.view.KeyEvent;
78 import android.view.LayoutInflater;
79 import android.view.MotionEvent;
80 import android.view.ScaleGestureDetector;
81 import android.view.SoundEffectConstants;
82 import android.view.VelocityTracker;
83 import android.view.View;
84 import android.view.View.MeasureSpec;
85 import android.view.ViewConfiguration;
86 import android.view.ViewGroup;
87 import android.view.ViewParent;
88 import android.view.ViewRootImpl;
89 import android.view.accessibility.AccessibilityEvent;
90 import android.view.accessibility.AccessibilityManager;
91 import android.view.accessibility.AccessibilityNodeInfo;
92 import android.view.inputmethod.BaseInputConnection;
93 import android.view.inputmethod.EditorInfo;
94 import android.view.inputmethod.InputConnection;
95 import android.view.inputmethod.InputMethodManager;
96 import android.webkit.WebView.HitTestResult;
97 import android.webkit.WebView.PictureListener;
98 import android.webkit.WebViewCore.DrawData;
99 import android.webkit.WebViewCore.EventHub;
100 import android.webkit.WebViewCore.TextFieldInitData;
101 import android.webkit.WebViewCore.TextSelectionData;
102 import android.webkit.WebViewCore.WebKitHitTest;
103 import android.widget.AbsoluteLayout;
104 import android.widget.Adapter;
105 import android.widget.AdapterView;
106 import android.widget.AdapterView.OnItemClickListener;
107 import android.widget.ArrayAdapter;
108 import android.widget.CheckedTextView;
109 import android.widget.LinearLayout;
110 import android.widget.ListView;
111 import android.widget.OverScroller;
112 import android.widget.PopupWindow;
113 import android.widget.Scroller;
114 import android.widget.TextView;
115 import android.widget.Toast;
116 
117 import junit.framework.Assert;
118 
119 import java.io.BufferedWriter;
120 import java.io.ByteArrayOutputStream;
121 import java.io.File;
122 import java.io.FileInputStream;
123 import java.io.FileNotFoundException;
124 import java.io.FileOutputStream;
125 import java.io.IOException;
126 import java.io.InputStream;
127 import java.io.OutputStream;
128 import java.net.URLDecoder;
129 import java.util.ArrayList;
130 import java.util.HashMap;
131 import java.util.HashSet;
132 import java.util.List;
133 import java.util.Locale;
134 import java.util.Map;
135 import java.util.Set;
136 import java.util.Vector;
137 
138 /**
139  * Implements a backend provider for the {@link WebView} public API.
140  * @hide
141  */
142 // TODO: Check if any WebView published API methods are called from within here, and if so
143 // we should bounce the call out via the proxy to enable any sub-class to override it.
144 @Widget
145 @SuppressWarnings("deprecation")
146 public final class WebViewClassic implements WebViewProvider, WebViewProvider.ScrollDelegate,
147         WebViewProvider.ViewDelegate {
148     /**
149      * InputConnection used for ContentEditable. This captures changes
150      * to the text and sends them either as key strokes or text changes.
151      */
152     class WebViewInputConnection extends BaseInputConnection {
153         // Used for mapping characters to keys typed.
154         private KeyCharacterMap mKeyCharacterMap;
155         private boolean mIsKeySentByMe;
156         private int mInputType;
157         private int mImeOptions;
158         private String mHint;
159         private int mMaxLength;
160         private boolean mIsAutoFillable;
161         private boolean mIsAutoCompleteEnabled;
162         private String mName;
163         private int mBatchLevel;
164 
WebViewInputConnection()165         public WebViewInputConnection() {
166             super(mWebView, true);
167         }
168 
setAutoFillable(int queryId)169         public void setAutoFillable(int queryId) {
170             mIsAutoFillable = getSettings().getAutoFillEnabled()
171                     && (queryId != WebTextView.FORM_NOT_AUTOFILLABLE);
172             int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION;
173             if (variation != EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD
174                     && (mIsAutoFillable || mIsAutoCompleteEnabled)) {
175                 if (mName != null && mName.length() > 0) {
176                     requestFormData(mName, mFieldPointer, mIsAutoFillable,
177                             mIsAutoCompleteEnabled);
178                 }
179             }
180         }
181 
182         @Override
beginBatchEdit()183         public boolean beginBatchEdit() {
184             if (mBatchLevel == 0) {
185                 beginTextBatch();
186             }
187             mBatchLevel++;
188             return false;
189         }
190 
191         @Override
endBatchEdit()192         public boolean endBatchEdit() {
193             mBatchLevel--;
194             if (mBatchLevel == 0) {
195                 commitTextBatch();
196             }
197             return false;
198         }
199 
getIsAutoFillable()200         public boolean getIsAutoFillable() {
201             return mIsAutoFillable;
202         }
203 
204         @Override
sendKeyEvent(KeyEvent event)205         public boolean sendKeyEvent(KeyEvent event) {
206             // Some IMEs send key events directly using sendKeyEvents.
207             // WebViewInputConnection should treat these as text changes.
208             if (!mIsKeySentByMe) {
209                 if (event.getAction() == KeyEvent.ACTION_UP) {
210                     if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
211                         return deleteSurroundingText(1, 0);
212                     } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
213                         return deleteSurroundingText(0, 1);
214                     } else if (event.getUnicodeChar() != 0){
215                         String newComposingText =
216                                 Character.toString((char)event.getUnicodeChar());
217                         return commitText(newComposingText, 1);
218                     }
219                 } else if (event.getAction() == KeyEvent.ACTION_DOWN &&
220                         (event.getKeyCode() == KeyEvent.KEYCODE_DEL
221                         || event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL
222                         || event.getUnicodeChar() != 0)) {
223                     return true; // only act on action_down
224                 }
225             }
226             return super.sendKeyEvent(event);
227         }
228 
setTextAndKeepSelection(CharSequence text)229         public void setTextAndKeepSelection(CharSequence text) {
230             Editable editable = getEditable();
231             int selectionStart = Selection.getSelectionStart(editable);
232             int selectionEnd = Selection.getSelectionEnd(editable);
233             text = limitReplaceTextByMaxLength(text, editable.length());
234             editable.replace(0, editable.length(), text);
235             restartInput();
236             // Keep the previous selection.
237             selectionStart = Math.min(selectionStart, editable.length());
238             selectionEnd = Math.min(selectionEnd, editable.length());
239             setSelection(selectionStart, selectionEnd);
240             finishComposingText();
241         }
242 
replaceSelection(CharSequence text)243         public void replaceSelection(CharSequence text) {
244             Editable editable = getEditable();
245             int selectionStart = Selection.getSelectionStart(editable);
246             int selectionEnd = Selection.getSelectionEnd(editable);
247             text = limitReplaceTextByMaxLength(text, selectionEnd - selectionStart);
248             setNewText(selectionStart, selectionEnd, text);
249             editable.replace(selectionStart, selectionEnd, text);
250             restartInput();
251             // Move caret to the end of the new text
252             int newCaret = selectionStart + text.length();
253             setSelection(newCaret, newCaret);
254         }
255 
256         @Override
setComposingText(CharSequence text, int newCursorPosition)257         public boolean setComposingText(CharSequence text, int newCursorPosition) {
258             Editable editable = getEditable();
259             int start = getComposingSpanStart(editable);
260             int end = getComposingSpanEnd(editable);
261             if (start < 0 || end < 0) {
262                 start = Selection.getSelectionStart(editable);
263                 end = Selection.getSelectionEnd(editable);
264             }
265             if (end < start) {
266                 int temp = end;
267                 end = start;
268                 start = temp;
269             }
270             CharSequence limitedText = limitReplaceTextByMaxLength(text, end - start);
271             setNewText(start, end, limitedText);
272             if (limitedText != text) {
273                 newCursorPosition -= text.length() - limitedText.length();
274             }
275             super.setComposingText(limitedText, newCursorPosition);
276             updateSelection();
277             if (limitedText != text) {
278                 int lastCaret = start + limitedText.length();
279                 finishComposingText();
280                 setSelection(lastCaret, lastCaret);
281             }
282             return true;
283         }
284 
285         @Override
commitText(CharSequence text, int newCursorPosition)286         public boolean commitText(CharSequence text, int newCursorPosition) {
287             setComposingText(text, newCursorPosition);
288             finishComposingText();
289             return true;
290         }
291 
292         @Override
deleteSurroundingText(int leftLength, int rightLength)293         public boolean deleteSurroundingText(int leftLength, int rightLength) {
294             // This code is from BaseInputConnection#deleteSurroundText.
295             // We have to delete the same text in webkit.
296             Editable content = getEditable();
297             int a = Selection.getSelectionStart(content);
298             int b = Selection.getSelectionEnd(content);
299 
300             if (a > b) {
301                 int tmp = a;
302                 a = b;
303                 b = tmp;
304             }
305 
306             int ca = getComposingSpanStart(content);
307             int cb = getComposingSpanEnd(content);
308             if (cb < ca) {
309                 int tmp = ca;
310                 ca = cb;
311                 cb = tmp;
312             }
313             if (ca != -1 && cb != -1) {
314                 if (ca < a) a = ca;
315                 if (cb > b) b = cb;
316             }
317 
318             int endDelete = Math.min(content.length(), b + rightLength);
319             if (endDelete > b) {
320                 setNewText(b, endDelete, "");
321             }
322             int startDelete = Math.max(0, a - leftLength);
323             if (startDelete < a) {
324                 setNewText(startDelete, a, "");
325             }
326             return super.deleteSurroundingText(leftLength, rightLength);
327         }
328 
329         @Override
performEditorAction(int editorAction)330         public boolean performEditorAction(int editorAction) {
331 
332             boolean handled = true;
333             switch (editorAction) {
334             case EditorInfo.IME_ACTION_NEXT:
335                 mWebView.requestFocus(View.FOCUS_FORWARD);
336                 break;
337             case EditorInfo.IME_ACTION_PREVIOUS:
338                 mWebView.requestFocus(View.FOCUS_BACKWARD);
339                 break;
340             case EditorInfo.IME_ACTION_DONE:
341                 WebViewClassic.this.hideSoftKeyboard();
342                 break;
343             case EditorInfo.IME_ACTION_GO:
344             case EditorInfo.IME_ACTION_SEARCH:
345                 WebViewClassic.this.hideSoftKeyboard();
346                 String text = getEditable().toString();
347                 passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_DOWN,
348                         KeyEvent.KEYCODE_ENTER));
349                 passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_UP,
350                         KeyEvent.KEYCODE_ENTER));
351                 break;
352 
353             default:
354                 handled = super.performEditorAction(editorAction);
355                 break;
356             }
357 
358             return handled;
359         }
360 
initEditorInfo(WebViewCore.TextFieldInitData initData)361         public void initEditorInfo(WebViewCore.TextFieldInitData initData) {
362             int type = initData.mType;
363             int inputType = InputType.TYPE_CLASS_TEXT
364                     | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
365             int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
366                     | EditorInfo.IME_FLAG_NO_FULLSCREEN;
367             if (!initData.mIsSpellCheckEnabled) {
368                 inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
369             }
370             if (WebTextView.TEXT_AREA != type) {
371                 if (initData.mIsTextFieldNext) {
372                     imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
373                 }
374                 if (initData.mIsTextFieldPrev) {
375                     imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
376                 }
377             }
378             int action = EditorInfo.IME_ACTION_GO;
379             switch (type) {
380                 case WebTextView.NORMAL_TEXT_FIELD:
381                     break;
382                 case WebTextView.TEXT_AREA:
383                     inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE
384                             | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
385                             | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
386                     action = EditorInfo.IME_ACTION_NONE;
387                     break;
388                 case WebTextView.PASSWORD:
389                     inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
390                     break;
391                 case WebTextView.SEARCH:
392                     action = EditorInfo.IME_ACTION_SEARCH;
393                     break;
394                 case WebTextView.EMAIL:
395                     // inputType needs to be overwritten because of the different text variation.
396                     inputType = InputType.TYPE_CLASS_TEXT
397                             | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
398                     break;
399                 case WebTextView.NUMBER:
400                     // inputType needs to be overwritten because of the different class.
401                     inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL
402                             | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL;
403                     // Number and telephone do not have both a Tab key and an
404                     // action, so set the action to NEXT
405                     break;
406                 case WebTextView.TELEPHONE:
407                     // inputType needs to be overwritten because of the different class.
408                     inputType = InputType.TYPE_CLASS_PHONE;
409                     break;
410                 case WebTextView.URL:
411                     // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
412                     // exclude it for now.
413                     inputType |= InputType.TYPE_TEXT_VARIATION_URI;
414                     break;
415                 default:
416                     break;
417             }
418             imeOptions |= action;
419             mHint = initData.mLabel;
420             mInputType = inputType;
421             mImeOptions = imeOptions;
422             mMaxLength = initData.mMaxLength;
423             mIsAutoCompleteEnabled = initData.mIsAutoCompleteEnabled;
424             mName = initData.mName;
425             mAutoCompletePopup.clearAdapter();
426         }
427 
setupEditorInfo(EditorInfo outAttrs)428         public void setupEditorInfo(EditorInfo outAttrs) {
429             outAttrs.inputType = mInputType;
430             outAttrs.imeOptions = mImeOptions;
431             outAttrs.hintText = mHint;
432             outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
433 
434             Editable editable = getEditable();
435             int selectionStart = Selection.getSelectionStart(editable);
436             int selectionEnd = Selection.getSelectionEnd(editable);
437             if (selectionStart < 0 || selectionEnd < 0) {
438                 selectionStart = editable.length();
439                 selectionEnd = selectionStart;
440             }
441             outAttrs.initialSelStart = selectionStart;
442             outAttrs.initialSelEnd = selectionEnd;
443         }
444 
445         @Override
setSelection(int start, int end)446         public boolean setSelection(int start, int end) {
447             boolean result = super.setSelection(start, end);
448             updateSelection();
449             return result;
450         }
451 
452         @Override
setComposingRegion(int start, int end)453         public boolean setComposingRegion(int start, int end) {
454             boolean result = super.setComposingRegion(start, end);
455             updateSelection();
456             return result;
457         }
458 
459         /**
460          * Send the selection and composing spans to the IME.
461          */
updateSelection()462         private void updateSelection() {
463             Editable editable = getEditable();
464             int selectionStart = Selection.getSelectionStart(editable);
465             int selectionEnd = Selection.getSelectionEnd(editable);
466             int composingStart = getComposingSpanStart(editable);
467             int composingEnd = getComposingSpanEnd(editable);
468             InputMethodManager imm = InputMethodManager.peekInstance();
469             if (imm != null) {
470                 imm.updateSelection(mWebView, selectionStart, selectionEnd,
471                         composingStart, composingEnd);
472             }
473         }
474 
475         /**
476          * Sends a text change to webkit indirectly. If it is a single-
477          * character add or delete, it sends it as a key stroke. If it cannot
478          * be represented as a key stroke, it sends it as a field change.
479          * @param start The start offset (inclusive) of the text being changed.
480          * @param end The end offset (exclusive) of the text being changed.
481          * @param text The new text to replace the changed text.
482          */
setNewText(int start, int end, CharSequence text)483         private void setNewText(int start, int end, CharSequence text) {
484             mIsKeySentByMe = true;
485             Editable editable = getEditable();
486             CharSequence original = editable.subSequence(start, end);
487             boolean isCharacterAdd = false;
488             boolean isCharacterDelete = false;
489             int textLength = text.length();
490             int originalLength = original.length();
491             int selectionStart = Selection.getSelectionStart(editable);
492             int selectionEnd = Selection.getSelectionEnd(editable);
493             if (selectionStart == selectionEnd) {
494                 if (textLength > originalLength) {
495                     isCharacterAdd = (textLength == originalLength + 1)
496                             && TextUtils.regionMatches(text, 0, original, 0,
497                                     originalLength);
498                 } else if (originalLength > textLength) {
499                     isCharacterDelete = (textLength == originalLength - 1)
500                             && TextUtils.regionMatches(text, 0, original, 0,
501                                     textLength);
502                 }
503             }
504             if (isCharacterAdd) {
505                 sendCharacter(text.charAt(textLength - 1));
506             } else if (isCharacterDelete) {
507                 sendKey(KeyEvent.KEYCODE_DEL);
508             } else if ((textLength != originalLength) ||
509                     !TextUtils.regionMatches(text, 0, original, 0,
510                             textLength)) {
511                 // Send a message so that key strokes and text replacement
512                 // do not come out of order.
513                 Message replaceMessage = mPrivateHandler.obtainMessage(
514                         REPLACE_TEXT, start,  end, text.toString());
515                 mPrivateHandler.sendMessage(replaceMessage);
516             }
517             if (mAutoCompletePopup != null) {
518                 StringBuilder newText = new StringBuilder();
519                 newText.append(editable.subSequence(0, start));
520                 newText.append(text);
521                 newText.append(editable.subSequence(end, editable.length()));
522                 mAutoCompletePopup.setText(newText.toString());
523             }
524             mIsKeySentByMe = false;
525         }
526 
527         /**
528          * Send a single character to the WebView as a key down and up event.
529          * @param c The character to be sent.
530          */
sendCharacter(char c)531         private void sendCharacter(char c) {
532             if (mKeyCharacterMap == null) {
533                 mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
534             }
535             char[] chars = new char[1];
536             chars[0] = c;
537             KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
538             if (events != null) {
539                 for (KeyEvent event : events) {
540                     sendKeyEvent(event);
541                 }
542             } else {
543                 Message msg = mPrivateHandler.obtainMessage(KEY_PRESS, (int) c, 0);
544                 mPrivateHandler.sendMessage(msg);
545             }
546         }
547 
548         /**
549          * Send a key event for a specific key code, not a standard
550          * unicode character.
551          * @param keyCode The key code to send.
552          */
sendKey(int keyCode)553         private void sendKey(int keyCode) {
554             long eventTime = SystemClock.uptimeMillis();
555             sendKeyEvent(new KeyEvent(eventTime, eventTime,
556                     KeyEvent.ACTION_DOWN, keyCode, 0, 0,
557                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
558                     KeyEvent.FLAG_SOFT_KEYBOARD));
559             sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
560                     KeyEvent.ACTION_UP, keyCode, 0, 0,
561                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
562                     KeyEvent.FLAG_SOFT_KEYBOARD));
563         }
564 
limitReplaceTextByMaxLength(CharSequence text, int numReplaced)565         private CharSequence limitReplaceTextByMaxLength(CharSequence text,
566                 int numReplaced) {
567             if (mMaxLength > 0) {
568                 Editable editable = getEditable();
569                 int maxReplace = mMaxLength - editable.length() + numReplaced;
570                 if (maxReplace < text.length()) {
571                     maxReplace = Math.max(maxReplace, 0);
572                     // New length is greater than the maximum. trim it down.
573                     text = text.subSequence(0, maxReplace);
574                 }
575             }
576             return text;
577         }
578 
restartInput()579         private void restartInput() {
580             InputMethodManager imm = InputMethodManager.peekInstance();
581             if (imm != null) {
582                 // Since the text has changed, do not allow the IME to replace the
583                 // existing text as though it were a completion.
584                 imm.restartInput(mWebView);
585             }
586         }
587     }
588 
589     private class PastePopupWindow extends PopupWindow implements View.OnClickListener {
590         private ViewGroup mContentView;
591         private TextView mPasteTextView;
592 
PastePopupWindow()593         public PastePopupWindow() {
594             super(mContext, null,
595                     com.android.internal.R.attr.textSelectHandleWindowStyle);
596             setClippingEnabled(true);
597             LinearLayout linearLayout = new LinearLayout(mContext);
598             linearLayout.setOrientation(LinearLayout.HORIZONTAL);
599             mContentView = linearLayout;
600             mContentView.setBackgroundResource(
601                     com.android.internal.R.drawable.text_edit_paste_window);
602 
603             LayoutInflater inflater = (LayoutInflater)mContext.
604                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
605 
606             ViewGroup.LayoutParams wrapContent = new ViewGroup.LayoutParams(
607                     ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
608 
609             mPasteTextView = (TextView) inflater.inflate(
610                     com.android.internal.R.layout.text_edit_action_popup_text, null);
611             mPasteTextView.setLayoutParams(wrapContent);
612             mContentView.addView(mPasteTextView);
613             mPasteTextView.setText(com.android.internal.R.string.paste);
614             mPasteTextView.setOnClickListener(this);
615             this.setContentView(mContentView);
616         }
617 
show(Point cursorBottom, Point cursorTop, int windowLeft, int windowTop)618         public void show(Point cursorBottom, Point cursorTop,
619                 int windowLeft, int windowTop) {
620             measureContent();
621 
622             int width = mContentView.getMeasuredWidth();
623             int height = mContentView.getMeasuredHeight();
624             int y = cursorTop.y - height;
625             int x = cursorTop.x - (width / 2);
626             if (y < windowTop) {
627                 // There's not enough room vertically, move it below the
628                 // handle.
629                 ensureSelectionHandles();
630                 y = cursorBottom.y + mSelectHandleCenter.getIntrinsicHeight();
631                 x = cursorBottom.x - (width / 2);
632             }
633             if (x < windowLeft) {
634                 x = windowLeft;
635             }
636             if (!isShowing()) {
637                 showAtLocation(mWebView, Gravity.NO_GRAVITY, x, y);
638             }
639             update(x, y, width, height);
640         }
641 
hide()642         public void hide() {
643             dismiss();
644         }
645 
646         @Override
onClick(View view)647         public void onClick(View view) {
648             pasteFromClipboard();
649             selectionDone();
650         }
651 
measureContent()652         protected void measureContent() {
653             final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
654             mContentView.measure(
655                     View.MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels,
656                             View.MeasureSpec.AT_MOST),
657                     View.MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels,
658                             View.MeasureSpec.AT_MOST));
659         }
660     }
661 
662     // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
663     // the screen all-the-time. Good for profiling our drawing code
664     static private final boolean AUTO_REDRAW_HACK = false;
665 
666     // The rate at which edit text is scrolled in content pixels per millisecond
667     static private final float TEXT_SCROLL_RATE = 0.01f;
668 
669     // The presumed scroll rate for the first scroll of edit text
670     static private final long TEXT_SCROLL_FIRST_SCROLL_MS = 16;
671 
672     // Buffer pixels of the caret rectangle when moving edit text into view
673     // after resize.
674     static private final int EDIT_RECT_BUFFER = 10;
675 
676     static private final long SELECTION_HANDLE_ANIMATION_MS = 150;
677 
678     // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
679     private boolean mAutoRedraw;
680 
681     // Reference to the AlertDialog displayed by InvokeListBox.
682     // It's used to dismiss the dialog in destroy if not done before.
683     private AlertDialog mListBoxDialog = null;
684 
685     // Reference to the save password dialog so it can be dimissed in
686     // destroy if not done before.
687     private AlertDialog mSavePasswordDialog = null;
688 
689     static final String LOGTAG = "webview";
690 
691     private ZoomManager mZoomManager;
692 
693     private final Rect mInvScreenRect = new Rect();
694     private final Rect mScreenRect = new Rect();
695     private final RectF mVisibleContentRect = new RectF();
696     private boolean mIsWebViewVisible = true;
697     WebViewInputConnection mInputConnection = null;
698     private int mFieldPointer;
699     private PastePopupWindow mPasteWindow;
700     private AutoCompletePopup mAutoCompletePopup;
701     Rect mEditTextContentBounds = new Rect();
702     Rect mEditTextContent = new Rect();
703     int mEditTextLayerId;
704     boolean mIsEditingText = false;
705     ArrayList<Message> mBatchedTextChanges = new ArrayList<Message>();
706     boolean mIsBatchingTextChanges = false;
707     private long mLastEditScroll = 0;
708 
709     private static class OnTrimMemoryListener implements ComponentCallbacks2 {
710         private static OnTrimMemoryListener sInstance = null;
711 
init(Context c)712         static void init(Context c) {
713             if (sInstance == null) {
714                 sInstance = new OnTrimMemoryListener(c.getApplicationContext());
715             }
716         }
717 
OnTrimMemoryListener(Context c)718         private OnTrimMemoryListener(Context c) {
719             c.registerComponentCallbacks(this);
720         }
721 
722         @Override
onConfigurationChanged(Configuration newConfig)723         public void onConfigurationChanged(Configuration newConfig) {
724             // Ignore
725         }
726 
727         @Override
onLowMemory()728         public void onLowMemory() {
729             // Ignore
730         }
731 
732         @Override
onTrimMemory(int level)733         public void onTrimMemory(int level) {
734             if (DebugFlags.WEB_VIEW) {
735                 Log.d("WebView", "onTrimMemory: " + level);
736             }
737             // When framework reset EGL context during high memory pressure, all
738             // the existing GL resources for the html5 video will be destroyed
739             // at native side.
740             // Here we just need to clean up the Surface Texture which is static.
741             if (level > TRIM_MEMORY_UI_HIDDEN) {
742                 HTML5VideoInline.cleanupSurfaceTexture();
743                 HTML5VideoView.release();
744             }
745             WebViewClassic.nativeOnTrimMemory(level);
746         }
747     }
748 
749     // A final CallbackProxy shared by WebViewCore and BrowserFrame.
750     private CallbackProxy mCallbackProxy;
751 
752     private WebViewDatabaseClassic mDatabase;
753 
754     // SSL certificate for the main top-level page (if secure)
755     private SslCertificate mCertificate;
756 
757     // Native WebView pointer that is 0 until the native object has been
758     // created.
759     private int mNativeClass;
760     // This would be final but it needs to be set to null when the WebView is
761     // destroyed.
762     private WebViewCore mWebViewCore;
763     // Handler for dispatching UI messages.
764     /* package */ final Handler mPrivateHandler = new PrivateHandler();
765     // Used to ignore changes to webkit text that arrives to the UI side after
766     // more key events.
767     private int mTextGeneration;
768 
incrementTextGeneration()769     /* package */ void incrementTextGeneration() { mTextGeneration++; }
770 
771     // Used by WebViewCore to create child views.
772     /* package */ ViewManager mViewManager;
773 
774     // Used to display in full screen mode
775     PluginFullScreenHolder mFullScreenHolder;
776 
777     /**
778      * Position of the last touch event in pixels.
779      * Use integer to prevent loss of dragging delta calculation accuracy;
780      * which was done in float and converted to integer, and resulted in gradual
781      * and compounding touch position and view dragging mismatch.
782      */
783     private int mLastTouchX;
784     private int mLastTouchY;
785     private int mStartTouchX;
786     private int mStartTouchY;
787     private float mAverageAngle;
788 
789     /**
790      * Time of the last touch event.
791      */
792     private long mLastTouchTime;
793 
794     /**
795      * Time of the last time sending touch event to WebViewCore
796      */
797     private long mLastSentTouchTime;
798 
799     /**
800      * The minimum elapsed time before sending another ACTION_MOVE event to
801      * WebViewCore. This really should be tuned for each type of the devices.
802      * For example in Google Map api test case, it takes Dream device at least
803      * 150ms to do a full cycle in the WebViewCore by processing a touch event,
804      * triggering the layout and drawing the picture. While the same process
805      * takes 60+ms on the current high speed device. If we make
806      * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
807      * to WebViewCore queue and the real layout and draw events will be pushed
808      * to further, which slows down the refresh rate. Choose 50 to favor the
809      * current high speed devices. For Dream like devices, 100 is a better
810      * choice. Maybe make this in the buildspec later.
811      * (Update 12/14/2010: changed to 0 since current device should be able to
812      * handle the raw events and Map team voted to have the raw events too.
813      */
814     private static final int TOUCH_SENT_INTERVAL = 0;
815     private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
816 
817     /**
818      * Helper class to get velocity for fling
819      */
820     VelocityTracker mVelocityTracker;
821     private int mMaximumFling;
822     private float mLastVelocity;
823     private float mLastVelX;
824     private float mLastVelY;
825 
826     // The id of the native layer being scrolled.
827     private int mCurrentScrollingLayerId;
828     private Rect mScrollingLayerRect = new Rect();
829 
830     // only trigger accelerated fling if the new velocity is at least
831     // MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity
832     private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f;
833 
834     /**
835      * Touch mode
836      * TODO: Some of this is now unnecessary as it is handled by
837      * WebInputTouchDispatcher (such as click, long press, and double tap).
838      */
839     private int mTouchMode = TOUCH_DONE_MODE;
840     private static final int TOUCH_INIT_MODE = 1;
841     private static final int TOUCH_DRAG_START_MODE = 2;
842     private static final int TOUCH_DRAG_MODE = 3;
843     private static final int TOUCH_SHORTPRESS_START_MODE = 4;
844     private static final int TOUCH_SHORTPRESS_MODE = 5;
845     private static final int TOUCH_DOUBLE_TAP_MODE = 6;
846     private static final int TOUCH_DONE_MODE = 7;
847     private static final int TOUCH_PINCH_DRAG = 8;
848     private static final int TOUCH_DRAG_LAYER_MODE = 9;
849     private static final int TOUCH_DRAG_TEXT_MODE = 10;
850 
851     // true when the touch movement exceeds the slop
852     private boolean mConfirmMove;
853     private boolean mTouchInEditText;
854 
855     // Whether or not to draw the cursor ring.
856     private boolean mDrawCursorRing = true;
857 
858     // true if onPause has been called (and not onResume)
859     private boolean mIsPaused;
860 
861     private HitTestResult mInitialHitTestResult;
862     private WebKitHitTest mFocusedNode;
863 
864     /**
865      * Customizable constant
866      */
867     // pre-computed square of ViewConfiguration.getScaledTouchSlop()
868     private int mTouchSlopSquare;
869     // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
870     private int mDoubleTapSlopSquare;
871     // pre-computed density adjusted navigation slop
872     private int mNavSlop;
873     // This should be ViewConfiguration.getTapTimeout()
874     // But system time out is 100ms, which is too short for the browser.
875     // In the browser, if it switches out of tap too soon, jump tap won't work.
876     // In addition, a double tap on a trackpad will always have a duration of
877     // 300ms, so this value must be at least that (otherwise we will timeout the
878     // first tap and convert it to a long press).
879     private static final int TAP_TIMEOUT = 300;
880     // This should be ViewConfiguration.getLongPressTimeout()
881     // But system time out is 500ms, which is too short for the browser.
882     // With a short timeout, it's difficult to treat trigger a short press.
883     private static final int LONG_PRESS_TIMEOUT = 1000;
884     // needed to avoid flinging after a pause of no movement
885     private static final int MIN_FLING_TIME = 250;
886     // draw unfiltered after drag is held without movement
887     private static final int MOTIONLESS_TIME = 100;
888     // The amount of content to overlap between two screens when going through
889     // pages with the space bar, in pixels.
890     private static final int PAGE_SCROLL_OVERLAP = 24;
891 
892     /**
893      * These prevent calling requestLayout if either dimension is fixed. This
894      * depends on the layout parameters and the measure specs.
895      */
896     boolean mWidthCanMeasure;
897     boolean mHeightCanMeasure;
898 
899     // Remember the last dimensions we sent to the native side so we can avoid
900     // sending the same dimensions more than once.
901     int mLastWidthSent;
902     int mLastHeightSent;
903     // Since view height sent to webkit could be fixed to avoid relayout, this
904     // value records the last sent actual view height.
905     int mLastActualHeightSent;
906 
907     private int mContentWidth;   // cache of value from WebViewCore
908     private int mContentHeight;  // cache of value from WebViewCore
909 
910     // Need to have the separate control for horizontal and vertical scrollbar
911     // style than the View's single scrollbar style
912     private boolean mOverlayHorizontalScrollbar = true;
913     private boolean mOverlayVerticalScrollbar = false;
914 
915     // our standard speed. this way small distances will be traversed in less
916     // time than large distances, but we cap the duration, so that very large
917     // distances won't take too long to get there.
918     private static final int STD_SPEED = 480;  // pixels per second
919     // time for the longest scroll animation
920     private static final int MAX_DURATION = 750;   // milliseconds
921 
922     // Used by OverScrollGlow
923     OverScroller mScroller;
924     Scroller mEditTextScroller;
925 
926     private boolean mInOverScrollMode = false;
927     private static Paint mOverScrollBackground;
928     private static Paint mOverScrollBorder;
929 
930     private boolean mWrapContent;
931     private static final int MOTIONLESS_FALSE           = 0;
932     private static final int MOTIONLESS_PENDING         = 1;
933     private static final int MOTIONLESS_TRUE            = 2;
934     private static final int MOTIONLESS_IGNORE          = 3;
935     private int mHeldMotionless;
936 
937     // Lazily-instantiated instance for injecting accessibility.
938     private AccessibilityInjector mAccessibilityInjector;
939 
940     /**
941      * How long the caret handle will last without being touched.
942      */
943     private static final long CARET_HANDLE_STAMINA_MS = 3000;
944 
945     private Drawable mSelectHandleLeft;
946     private Drawable mSelectHandleRight;
947     private Drawable mSelectHandleCenter;
948     private Point mSelectOffset;
949     private Point mSelectCursorBase = new Point();
950     private Rect mSelectHandleBaseBounds = new Rect();
951     private int mSelectCursorBaseLayerId;
952     private QuadF mSelectCursorBaseTextQuad = new QuadF();
953     private Point mSelectCursorExtent = new Point();
954     private Rect mSelectHandleExtentBounds = new Rect();
955     private int mSelectCursorExtentLayerId;
956     private QuadF mSelectCursorExtentTextQuad = new QuadF();
957     private Point mSelectDraggingCursor;
958     private QuadF mSelectDraggingTextQuad;
959     private boolean mIsCaretSelection;
960     static final int HANDLE_ID_BASE = 0;
961     static final int HANDLE_ID_EXTENT = 1;
962 
963     // the color used to highlight the touch rectangles
964     static final int HIGHLIGHT_COLOR = 0x6633b5e5;
965     // the region indicating where the user touched on the screen
966     private Region mTouchHighlightRegion = new Region();
967     // the paint for the touch highlight
968     private Paint mTouchHightlightPaint = new Paint();
969     // debug only
970     private static final boolean DEBUG_TOUCH_HIGHLIGHT = true;
971     private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000;
972     private Paint mTouchCrossHairColor;
973     private int mTouchHighlightX;
974     private int mTouchHighlightY;
975     private boolean mShowTapHighlight;
976 
977     // Basically this proxy is used to tell the Video to update layer tree at
978     // SetBaseLayer time and to pause when WebView paused.
979     private HTML5VideoViewProxy mHTML5VideoViewProxy;
980 
981     // If we are using a set picture, don't send view updates to webkit
982     private boolean mBlockWebkitViewMessages = false;
983 
984     // cached value used to determine if we need to switch drawing models
985     private boolean mHardwareAccelSkia = false;
986 
987     /*
988      * Private message ids
989      */
990     private static final int REMEMBER_PASSWORD          = 1;
991     private static final int NEVER_REMEMBER_PASSWORD    = 2;
992     private static final int SWITCH_TO_SHORTPRESS       = 3;
993     private static final int SWITCH_TO_LONGPRESS        = 4;
994     private static final int RELEASE_SINGLE_TAP         = 5;
995     private static final int REQUEST_FORM_DATA          = 6;
996     private static final int DRAG_HELD_MOTIONLESS       = 8;
997     private static final int PREVENT_DEFAULT_TIMEOUT    = 10;
998     private static final int SCROLL_SELECT_TEXT         = 11;
999 
1000 
1001     private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD;
1002     private static final int LAST_PRIVATE_MSG_ID = SCROLL_SELECT_TEXT;
1003 
1004     /*
1005      * Package message ids
1006      */
1007     static final int SCROLL_TO_MSG_ID                   = 101;
1008     static final int NEW_PICTURE_MSG_ID                 = 105;
1009     static final int WEBCORE_INITIALIZED_MSG_ID         = 107;
1010     static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 108;
1011     static final int UPDATE_ZOOM_RANGE                  = 109;
1012     static final int TAKE_FOCUS                         = 110;
1013     static final int CLEAR_TEXT_ENTRY                   = 111;
1014     static final int UPDATE_TEXT_SELECTION_MSG_ID       = 112;
1015     static final int SHOW_RECT_MSG_ID                   = 113;
1016     static final int LONG_PRESS_CENTER                  = 114;
1017     static final int PREVENT_TOUCH_ID                   = 115;
1018     static final int WEBCORE_NEED_TOUCH_EVENTS          = 116;
1019     // obj=Rect in doc coordinates
1020     static final int INVAL_RECT_MSG_ID                  = 117;
1021     static final int REQUEST_KEYBOARD                   = 118;
1022     static final int SHOW_FULLSCREEN                    = 120;
1023     static final int HIDE_FULLSCREEN                    = 121;
1024     static final int UPDATE_MATCH_COUNT                 = 126;
1025     static final int CENTER_FIT_RECT                    = 127;
1026     static final int SET_SCROLLBAR_MODES                = 129;
1027     static final int SELECTION_STRING_CHANGED           = 130;
1028     static final int HIT_TEST_RESULT                    = 131;
1029     static final int SAVE_WEBARCHIVE_FINISHED           = 132;
1030 
1031     static final int SET_AUTOFILLABLE                   = 133;
1032     static final int AUTOFILL_COMPLETE                  = 134;
1033 
1034     static final int SCREEN_ON                          = 136;
1035     static final int UPDATE_ZOOM_DENSITY                = 139;
1036     static final int EXIT_FULLSCREEN_VIDEO              = 140;
1037 
1038     static final int COPY_TO_CLIPBOARD                  = 141;
1039     static final int INIT_EDIT_FIELD                    = 142;
1040     static final int REPLACE_TEXT                       = 143;
1041     static final int CLEAR_CARET_HANDLE                 = 144;
1042     static final int KEY_PRESS                          = 145;
1043     static final int RELOCATE_AUTO_COMPLETE_POPUP       = 146;
1044     static final int FOCUS_NODE_CHANGED                 = 147;
1045     static final int AUTOFILL_FORM                      = 148;
1046     static final int SCROLL_EDIT_TEXT                   = 149;
1047     static final int EDIT_TEXT_SIZE_CHANGED             = 150;
1048     static final int SHOW_CARET_HANDLE                  = 151;
1049     static final int UPDATE_CONTENT_BOUNDS              = 152;
1050     static final int SCROLL_HANDLE_INTO_VIEW            = 153;
1051 
1052     private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
1053     private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;
1054 
1055     static final String[] HandlerPrivateDebugString = {
1056         "REMEMBER_PASSWORD", //              = 1;
1057         "NEVER_REMEMBER_PASSWORD", //        = 2;
1058         "SWITCH_TO_SHORTPRESS", //           = 3;
1059         "SWITCH_TO_LONGPRESS", //            = 4;
1060         "RELEASE_SINGLE_TAP", //             = 5;
1061         "REQUEST_FORM_DATA", //              = 6;
1062         "RESUME_WEBCORE_PRIORITY", //        = 7;
1063         "DRAG_HELD_MOTIONLESS", //           = 8;
1064         "", //             = 9;
1065         "PREVENT_DEFAULT_TIMEOUT", //        = 10;
1066         "SCROLL_SELECT_TEXT" //              = 11;
1067     };
1068 
1069     static final String[] HandlerPackageDebugString = {
1070         "SCROLL_TO_MSG_ID", //               = 101;
1071         "102", //                            = 102;
1072         "103", //                            = 103;
1073         "104", //                            = 104;
1074         "NEW_PICTURE_MSG_ID", //             = 105;
1075         "UPDATE_TEXT_ENTRY_MSG_ID", //       = 106;
1076         "WEBCORE_INITIALIZED_MSG_ID", //     = 107;
1077         "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 108;
1078         "UPDATE_ZOOM_RANGE", //              = 109;
1079         "UNHANDLED_NAV_KEY", //              = 110;
1080         "CLEAR_TEXT_ENTRY", //               = 111;
1081         "UPDATE_TEXT_SELECTION_MSG_ID", //   = 112;
1082         "SHOW_RECT_MSG_ID", //               = 113;
1083         "LONG_PRESS_CENTER", //              = 114;
1084         "PREVENT_TOUCH_ID", //               = 115;
1085         "WEBCORE_NEED_TOUCH_EVENTS", //      = 116;
1086         "INVAL_RECT_MSG_ID", //              = 117;
1087         "REQUEST_KEYBOARD", //               = 118;
1088         "DO_MOTION_UP", //                   = 119;
1089         "SHOW_FULLSCREEN", //                = 120;
1090         "HIDE_FULLSCREEN", //                = 121;
1091         "DOM_FOCUS_CHANGED", //              = 122;
1092         "REPLACE_BASE_CONTENT", //           = 123;
1093         "RETURN_LABEL", //                   = 125;
1094         "UPDATE_MATCH_COUNT", //             = 126;
1095         "CENTER_FIT_RECT", //                = 127;
1096         "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
1097         "SET_SCROLLBAR_MODES", //            = 129;
1098         "SELECTION_STRING_CHANGED", //       = 130;
1099         "SET_TOUCH_HIGHLIGHT_RECTS", //      = 131;
1100         "SAVE_WEBARCHIVE_FINISHED", //       = 132;
1101         "SET_AUTOFILLABLE", //               = 133;
1102         "AUTOFILL_COMPLETE", //              = 134;
1103         "SELECT_AT", //                      = 135;
1104         "SCREEN_ON", //                      = 136;
1105         "ENTER_FULLSCREEN_VIDEO", //         = 137;
1106         "UPDATE_SELECTION", //               = 138;
1107         "UPDATE_ZOOM_DENSITY" //             = 139;
1108     };
1109 
1110     // If the site doesn't use the viewport meta tag to specify the viewport,
1111     // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
1112     static final int DEFAULT_VIEWPORT_WIDTH = 980;
1113 
1114     // normally we try to fit the content to the minimum preferred width
1115     // calculated by the Webkit. To avoid the bad behavior when some site's
1116     // minimum preferred width keeps growing when changing the viewport width or
1117     // the minimum preferred width is huge, an upper limit is needed.
1118     static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
1119 
1120     // initial scale in percent. 0 means using default.
1121     private int mInitialScaleInPercent = 0;
1122 
1123     // Whether or not a scroll event should be sent to webkit.  This is only set
1124     // to false when restoring the scroll position.
1125     private boolean mSendScrollEvent = true;
1126 
1127     private int mSnapScrollMode = SNAP_NONE;
1128     private static final int SNAP_NONE = 0;
1129     private static final int SNAP_LOCK = 1; // not a separate state
1130     private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
1131     private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
1132     private boolean mSnapPositive;
1133 
1134     // keep these in sync with their counterparts in WebView.cpp
1135     private static final int DRAW_EXTRAS_NONE = 0;
1136     private static final int DRAW_EXTRAS_SELECTION = 1;
1137     private static final int DRAW_EXTRAS_CURSOR_RING = 2;
1138 
1139     // keep this in sync with WebCore:ScrollbarMode in WebKit
1140     private static final int SCROLLBAR_AUTO = 0;
1141     private static final int SCROLLBAR_ALWAYSOFF = 1;
1142     // as we auto fade scrollbar, this is ignored.
1143     private static final int SCROLLBAR_ALWAYSON = 2;
1144     private int mHorizontalScrollBarMode = SCROLLBAR_AUTO;
1145     private int mVerticalScrollBarMode = SCROLLBAR_AUTO;
1146 
1147     /**
1148      * Max distance to overscroll by in pixels.
1149      * This how far content can be pulled beyond its normal bounds by the user.
1150      */
1151     private int mOverscrollDistance;
1152 
1153     /**
1154      * Max distance to overfling by in pixels.
1155      * This is how far flinged content can move beyond the end of its normal bounds.
1156      */
1157     private int mOverflingDistance;
1158 
1159     private OverScrollGlow mOverScrollGlow;
1160 
1161     // Used to match key downs and key ups
1162     private Vector<Integer> mKeysPressed;
1163 
1164     /* package */ static boolean mLogEvent = true;
1165 
1166     // for event log
1167     private long mLastTouchUpTime = 0;
1168 
1169     private WebViewCore.AutoFillData mAutoFillData;
1170 
1171     private static boolean sNotificationsEnabled = true;
1172 
1173     /**
1174      * URI scheme for telephone number
1175      */
1176     public static final String SCHEME_TEL = "tel:";
1177     /**
1178      * URI scheme for email address
1179      */
1180     public static final String SCHEME_MAILTO = "mailto:";
1181     /**
1182      * URI scheme for map address
1183      */
1184     public static final String SCHEME_GEO = "geo:0,0?q=";
1185 
1186     private int mBackgroundColor = Color.WHITE;
1187 
1188     private static final long SELECT_SCROLL_INTERVAL = 1000 / 60; // 60 / second
1189     private int mAutoScrollX = 0;
1190     private int mAutoScrollY = 0;
1191     private int mMinAutoScrollX = 0;
1192     private int mMaxAutoScrollX = 0;
1193     private int mMinAutoScrollY = 0;
1194     private int mMaxAutoScrollY = 0;
1195     private Rect mScrollingLayerBounds = new Rect();
1196     private boolean mSentAutoScrollMessage = false;
1197 
1198     // used for serializing asynchronously handled touch events.
1199     private WebViewInputDispatcher mInputDispatcher;
1200 
1201     // Used to track whether picture updating was paused due to a window focus change.
1202     private boolean mPictureUpdatePausedForFocusChange = false;
1203 
1204     // Used to notify listeners of a new picture.
1205     private PictureListener mPictureListener;
1206 
1207     // Used to notify listeners about find-on-page results.
1208     private WebView.FindListener mFindListener;
1209 
1210     // Used to prevent resending save password message
1211     private Message mResumeMsg;
1212 
1213     /**
1214      * Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
1215      */
1216     static class FocusNodeHref {
1217         static final String TITLE = "title";
1218         static final String URL = "url";
1219         static final String SRC = "src";
1220     }
1221 
WebViewClassic(WebView webView, WebView.PrivateAccess privateAccess)1222     public WebViewClassic(WebView webView, WebView.PrivateAccess privateAccess) {
1223         mWebView = webView;
1224         mWebViewPrivate = privateAccess;
1225         mContext = webView.getContext();
1226     }
1227 
1228     /**
1229      * See {@link WebViewProvider#init(Map, boolean)}
1230      */
1231     @Override
init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing)1232     public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
1233         Context context = mContext;
1234 
1235         // Used by the chrome stack to find application paths
1236         JniUtil.setContext(context);
1237 
1238         mCallbackProxy = new CallbackProxy(context, this);
1239         mViewManager = new ViewManager(this);
1240         L10nUtils.setApplicationContext(context.getApplicationContext());
1241         mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces);
1242         mDatabase = WebViewDatabaseClassic.getInstance(context);
1243         mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
1244         mZoomManager = new ZoomManager(this, mCallbackProxy);
1245 
1246         /* The init method must follow the creation of certain member variables,
1247          * such as the mZoomManager.
1248          */
1249         init();
1250         setupPackageListener(context);
1251         setupProxyListener(context);
1252         setupTrustStorageListener(context);
1253         updateMultiTouchSupport(context);
1254 
1255         if (privateBrowsing) {
1256             startPrivateBrowsing();
1257         }
1258 
1259         mAutoFillData = new WebViewCore.AutoFillData();
1260         mEditTextScroller = new Scroller(context);
1261     }
1262 
1263     // WebViewProvider bindings
1264 
1265     static class Factory implements WebViewFactoryProvider,  WebViewFactoryProvider.Statics {
1266         @Override
findAddress(String addr)1267         public String findAddress(String addr) {
1268             return WebViewClassic.findAddress(addr);
1269         }
1270         @Override
setPlatformNotificationsEnabled(boolean enable)1271         public void setPlatformNotificationsEnabled(boolean enable) {
1272             if (enable) {
1273                 WebViewClassic.enablePlatformNotifications();
1274             } else {
1275                 WebViewClassic.disablePlatformNotifications();
1276             }
1277         }
1278 
1279         @Override
getStatics()1280         public Statics getStatics() { return this; }
1281 
1282         @Override
createWebView(WebView webView, WebView.PrivateAccess privateAccess)1283         public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
1284             return new WebViewClassic(webView, privateAccess);
1285         }
1286 
1287         @Override
getGeolocationPermissions()1288         public GeolocationPermissions getGeolocationPermissions() {
1289             return GeolocationPermissionsClassic.getInstance();
1290         }
1291 
1292         @Override
getCookieManager()1293         public CookieManager getCookieManager() {
1294             return CookieManagerClassic.getInstance();
1295         }
1296 
1297         @Override
getWebIconDatabase()1298         public WebIconDatabase getWebIconDatabase() {
1299             return WebIconDatabaseClassic.getInstance();
1300         }
1301 
1302         @Override
getWebStorage()1303         public WebStorage getWebStorage() {
1304             return WebStorageClassic.getInstance();
1305         }
1306 
1307         @Override
getWebViewDatabase(Context context)1308         public WebViewDatabase getWebViewDatabase(Context context) {
1309             return WebViewDatabaseClassic.getInstance(context);
1310         }
1311 
1312         @Override
getDefaultUserAgent(Context context)1313         public String getDefaultUserAgent(Context context) {
1314             return WebSettingsClassic.getDefaultUserAgentForLocale(context,
1315                     Locale.getDefault());
1316         }
1317     }
1318 
onHandleUiEvent(MotionEvent event, int eventType, int flags)1319     private void onHandleUiEvent(MotionEvent event, int eventType, int flags) {
1320         switch (eventType) {
1321         case WebViewInputDispatcher.EVENT_TYPE_LONG_PRESS:
1322             HitTestResult hitTest = getHitTestResult();
1323             if (hitTest != null) {
1324                 mWebView.performLongClick();
1325             }
1326             break;
1327         case WebViewInputDispatcher.EVENT_TYPE_DOUBLE_TAP:
1328             mZoomManager.handleDoubleTap(event.getX(), event.getY());
1329             break;
1330         case WebViewInputDispatcher.EVENT_TYPE_TOUCH:
1331             onHandleUiTouchEvent(event);
1332             break;
1333         case WebViewInputDispatcher.EVENT_TYPE_CLICK:
1334             if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) {
1335                 mWebView.playSoundEffect(SoundEffectConstants.CLICK);
1336                 overrideLoading(mFocusedNode.mIntentUrl);
1337             }
1338             break;
1339         }
1340     }
1341 
onHandleUiTouchEvent(MotionEvent ev)1342     private void onHandleUiTouchEvent(MotionEvent ev) {
1343         final ScaleGestureDetector detector =
1344                 mZoomManager.getScaleGestureDetector();
1345 
1346         int action = ev.getActionMasked();
1347         final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
1348         final boolean configChanged =
1349             action == MotionEvent.ACTION_POINTER_UP ||
1350             action == MotionEvent.ACTION_POINTER_DOWN;
1351         final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
1352 
1353         // Determine focal point
1354         float sumX = 0, sumY = 0;
1355         final int count = ev.getPointerCount();
1356         for (int i = 0; i < count; i++) {
1357             if (skipIndex == i) continue;
1358             sumX += ev.getX(i);
1359             sumY += ev.getY(i);
1360         }
1361         final int div = pointerUp ? count - 1 : count;
1362         float x = sumX / div;
1363         float y = sumY / div;
1364 
1365         if (configChanged) {
1366             mLastTouchX = Math.round(x);
1367             mLastTouchY = Math.round(y);
1368             mLastTouchTime = ev.getEventTime();
1369             mWebView.cancelLongPress();
1370             mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
1371         }
1372 
1373         if (detector != null) {
1374             detector.onTouchEvent(ev);
1375             if (detector.isInProgress()) {
1376                 mLastTouchTime = ev.getEventTime();
1377 
1378                 if (!mZoomManager.supportsPanDuringZoom()) {
1379                     return;
1380                 }
1381                 mTouchMode = TOUCH_DRAG_MODE;
1382                 if (mVelocityTracker == null) {
1383                     mVelocityTracker = VelocityTracker.obtain();
1384                 }
1385             }
1386         }
1387 
1388         if (action == MotionEvent.ACTION_POINTER_DOWN) {
1389             cancelTouch();
1390             action = MotionEvent.ACTION_DOWN;
1391         } else if (action == MotionEvent.ACTION_MOVE) {
1392             // negative x or y indicate it is on the edge, skip it.
1393             if (x < 0 || y < 0) {
1394                 return;
1395             }
1396         }
1397 
1398         handleTouchEventCommon(ev, action, Math.round(x), Math.round(y));
1399     }
1400 
1401     // The webview that is bound to this WebViewClassic instance. Primarily needed for supplying
1402     // as the first param in the WebViewClient and WebChromeClient callbacks.
1403     final private WebView mWebView;
1404     // Callback interface, provides priviledged access into the WebView instance.
1405     final private WebView.PrivateAccess mWebViewPrivate;
1406     // Cached reference to mWebView.getContext(), for convenience.
1407     final private Context mContext;
1408 
1409     /**
1410      * @return The webview proxy that this classic webview is bound to.
1411      */
getWebView()1412     public WebView getWebView() {
1413         return mWebView;
1414     }
1415 
1416     @Override
getViewDelegate()1417     public ViewDelegate getViewDelegate() {
1418         return this;
1419     }
1420 
1421     @Override
getScrollDelegate()1422     public ScrollDelegate getScrollDelegate() {
1423         return this;
1424     }
1425 
fromWebView(WebView webView)1426     public static WebViewClassic fromWebView(WebView webView) {
1427         return webView == null ? null : (WebViewClassic) webView.getWebViewProvider();
1428     }
1429 
1430     // Accessors, purely for convenience (and to reduce code churn during webview proxy migration).
getScrollX()1431     int getScrollX() {
1432         return mWebView.getScrollX();
1433     }
1434 
getScrollY()1435     int getScrollY() {
1436         return mWebView.getScrollY();
1437     }
1438 
getWidth()1439     int getWidth() {
1440         return mWebView.getWidth();
1441     }
1442 
getHeight()1443     int getHeight() {
1444         return mWebView.getHeight();
1445     }
1446 
getContext()1447     Context getContext() {
1448         return mContext;
1449     }
1450 
invalidate()1451     void invalidate() {
1452         mWebView.invalidate();
1453     }
1454 
1455     // Setters for the Scroll X & Y, without invoking the onScrollChanged etc code paths.
setScrollXRaw(int mScrollX)1456     void setScrollXRaw(int mScrollX) {
1457         mWebViewPrivate.setScrollXRaw(mScrollX);
1458     }
1459 
setScrollYRaw(int mScrollY)1460     void setScrollYRaw(int mScrollY) {
1461         mWebViewPrivate.setScrollYRaw(mScrollY);
1462     }
1463 
1464     private static class TrustStorageListener extends BroadcastReceiver {
1465         @Override
onReceive(Context context, Intent intent)1466         public void onReceive(Context context, Intent intent) {
1467             if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
1468                 handleCertTrustChanged();
1469             }
1470         }
1471     }
1472     private static TrustStorageListener sTrustStorageListener;
1473 
1474     /**
1475      * Handles update to the trust storage.
1476      */
handleCertTrustChanged()1477     private static void handleCertTrustChanged() {
1478         // send a message for indicating trust storage change
1479         WebViewCore.sendStaticMessage(EventHub.TRUST_STORAGE_UPDATED, null);
1480     }
1481 
1482     /*
1483      * @param context This method expects this to be a valid context.
1484      */
setupTrustStorageListener(Context context)1485     private static void setupTrustStorageListener(Context context) {
1486         if (sTrustStorageListener != null ) {
1487             return;
1488         }
1489         IntentFilter filter = new IntentFilter();
1490         filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
1491         sTrustStorageListener = new TrustStorageListener();
1492         Intent current =
1493             context.getApplicationContext().registerReceiver(sTrustStorageListener, filter);
1494         if (current != null) {
1495             handleCertTrustChanged();
1496         }
1497     }
1498 
1499     private static class ProxyReceiver extends BroadcastReceiver {
1500         @Override
onReceive(Context context, Intent intent)1501         public void onReceive(Context context, Intent intent) {
1502             if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
1503                 handleProxyBroadcast(intent);
1504             }
1505         }
1506     }
1507 
1508     /*
1509      * Receiver for PROXY_CHANGE_ACTION, will be null when it is not added handling broadcasts.
1510      */
1511     private static ProxyReceiver sProxyReceiver;
1512 
1513     /*
1514      * @param context This method expects this to be a valid context
1515      */
setupProxyListener(Context context)1516     private static synchronized void setupProxyListener(Context context) {
1517         if (sProxyReceiver != null || sNotificationsEnabled == false) {
1518             return;
1519         }
1520         IntentFilter filter = new IntentFilter();
1521         filter.addAction(Proxy.PROXY_CHANGE_ACTION);
1522         sProxyReceiver = new ProxyReceiver();
1523         Intent currentProxy = context.getApplicationContext().registerReceiver(
1524                 sProxyReceiver, filter);
1525         if (currentProxy != null) {
1526             handleProxyBroadcast(currentProxy);
1527         }
1528     }
1529 
1530     /*
1531      * @param context This method expects this to be a valid context
1532      */
disableProxyListener(Context context)1533     private static synchronized void disableProxyListener(Context context) {
1534         if (sProxyReceiver == null)
1535             return;
1536 
1537         context.getApplicationContext().unregisterReceiver(sProxyReceiver);
1538         sProxyReceiver = null;
1539     }
1540 
handleProxyBroadcast(Intent intent)1541     private static void handleProxyBroadcast(Intent intent) {
1542         ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
1543         if (proxyProperties == null || proxyProperties.getHost() == null) {
1544             WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, null);
1545             return;
1546         }
1547         WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, proxyProperties);
1548     }
1549 
1550     /*
1551      * A variable to track if there is a receiver added for ACTION_PACKAGE_ADDED
1552      * or ACTION_PACKAGE_REMOVED.
1553      */
1554     private static boolean sPackageInstallationReceiverAdded = false;
1555 
1556     /*
1557      * A set of Google packages we monitor for the
1558      * navigator.isApplicationInstalled() API. Add additional packages as
1559      * needed.
1560      */
1561     private static Set<String> sGoogleApps;
1562     static {
1563         sGoogleApps = new HashSet<String>();
1564         sGoogleApps.add("com.google.android.youtube");
1565     }
1566 
1567     private static class PackageListener extends BroadcastReceiver {
1568         @Override
onReceive(Context context, Intent intent)1569         public void onReceive(Context context, Intent intent) {
1570             final String action = intent.getAction();
1571             final String packageName = intent.getData().getSchemeSpecificPart();
1572             final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1573             if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
1574                 // if it is replacing, refreshPlugins() when adding
1575                 return;
1576             }
1577 
1578             if (sGoogleApps.contains(packageName)) {
1579                 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
1580                     WebViewCore.sendStaticMessage(EventHub.ADD_PACKAGE_NAME, packageName);
1581                 } else {
1582                     WebViewCore.sendStaticMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
1583                 }
1584             }
1585 
1586             PluginManager pm = PluginManager.getInstance(context);
1587             if (pm.containsPluginPermissionAndSignatures(packageName)) {
1588                 pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action));
1589             }
1590         }
1591     }
1592 
setupPackageListener(Context context)1593     private void setupPackageListener(Context context) {
1594 
1595         /*
1596          * we must synchronize the instance check and the creation of the
1597          * receiver to ensure that only ONE receiver exists for all WebView
1598          * instances.
1599          */
1600         synchronized (WebViewClassic.class) {
1601 
1602             // if the receiver already exists then we do not need to register it
1603             // again
1604             if (sPackageInstallationReceiverAdded) {
1605                 return;
1606             }
1607 
1608             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1609             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1610             filter.addDataScheme("package");
1611             BroadcastReceiver packageListener = new PackageListener();
1612             context.getApplicationContext().registerReceiver(packageListener, filter);
1613             sPackageInstallationReceiverAdded = true;
1614         }
1615 
1616         // check if any of the monitored apps are already installed
1617         AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() {
1618 
1619             @Override
1620             protected Set<String> doInBackground(Void... unused) {
1621                 Set<String> installedPackages = new HashSet<String>();
1622                 PackageManager pm = mContext.getPackageManager();
1623                 for (String name : sGoogleApps) {
1624                     try {
1625                         pm.getPackageInfo(name,
1626                                 PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
1627                         installedPackages.add(name);
1628                     } catch (PackageManager.NameNotFoundException e) {
1629                         // package not found
1630                     }
1631                 }
1632                 return installedPackages;
1633             }
1634 
1635             // Executes on the UI thread
1636             @Override
1637             protected void onPostExecute(Set<String> installedPackages) {
1638                 if (mWebViewCore != null) {
1639                     mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages);
1640                 }
1641             }
1642         };
1643         task.execute();
1644     }
1645 
updateMultiTouchSupport(Context context)1646     void updateMultiTouchSupport(Context context) {
1647         mZoomManager.updateMultiTouchSupport(context);
1648     }
1649 
updateJavaScriptEnabled(boolean enabled)1650     void updateJavaScriptEnabled(boolean enabled) {
1651         if (isAccessibilityInjectionEnabled()) {
1652             getAccessibilityInjector().updateJavaScriptEnabled(enabled);
1653         }
1654     }
1655 
init()1656     private void init() {
1657         OnTrimMemoryListener.init(mContext);
1658         mWebView.setWillNotDraw(false);
1659         mWebView.setClickable(true);
1660         mWebView.setLongClickable(true);
1661 
1662         final ViewConfiguration configuration = ViewConfiguration.get(mContext);
1663         int slop = configuration.getScaledTouchSlop();
1664         mTouchSlopSquare = slop * slop;
1665         slop = configuration.getScaledDoubleTapSlop();
1666         mDoubleTapSlopSquare = slop * slop;
1667         final float density = WebViewCore.getFixedDisplayDensity(mContext);
1668         // use one line height, 16 based on our current default font, for how
1669         // far we allow a touch be away from the edge of a link
1670         mNavSlop = (int) (16 * density);
1671         mZoomManager.init(density);
1672         mMaximumFling = configuration.getScaledMaximumFlingVelocity();
1673 
1674         // Compute the inverse of the density squared.
1675         DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
1676 
1677         mOverscrollDistance = configuration.getScaledOverscrollDistance();
1678         mOverflingDistance = configuration.getScaledOverflingDistance();
1679 
1680         setScrollBarStyle(mWebViewPrivate.super_getScrollBarStyle());
1681         // Initially use a size of two, since the user is likely to only hold
1682         // down two keys at a time (shift + another key)
1683         mKeysPressed = new Vector<Integer>(2);
1684         mHTML5VideoViewProxy = null ;
1685     }
1686 
1687     @Override
shouldDelayChildPressedState()1688     public boolean shouldDelayChildPressedState() {
1689         return true;
1690     }
1691 
1692     @Override
performAccessibilityAction(int action, Bundle arguments)1693     public boolean performAccessibilityAction(int action, Bundle arguments) {
1694         if (!mWebView.isEnabled()) {
1695             // Only default actions are supported while disabled.
1696             return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
1697         }
1698 
1699         if (getAccessibilityInjector().supportsAccessibilityAction(action)) {
1700             return getAccessibilityInjector().performAccessibilityAction(action, arguments);
1701         }
1702 
1703         switch (action) {
1704             case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
1705             case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
1706                 final int convertedContentHeight = contentToViewY(getContentHeight());
1707                 final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
1708                         - mWebView.getPaddingBottom();
1709                 final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
1710                 final boolean canScrollBackward = (getScrollY() > 0);
1711                 final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
1712                 if ((action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) && canScrollBackward) {
1713                     mWebView.scrollBy(0, adjustedViewHeight);
1714                     return true;
1715                 }
1716                 if ((action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) && canScrollForward) {
1717                     mWebView.scrollBy(0, -adjustedViewHeight);
1718                     return true;
1719                 }
1720                 return false;
1721             }
1722         }
1723 
1724         return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
1725     }
1726 
1727     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)1728     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1729         if (!mWebView.isEnabled()) {
1730             // Only default actions are supported while disabled.
1731             return;
1732         }
1733 
1734         info.setScrollable(isScrollableForAccessibility());
1735 
1736         final int convertedContentHeight = contentToViewY(getContentHeight());
1737         final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
1738                 - mWebView.getPaddingBottom();
1739         final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
1740         final boolean canScrollBackward = (getScrollY() > 0);
1741         final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
1742 
1743         if (canScrollForward) {
1744             info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
1745         }
1746 
1747         if (canScrollForward) {
1748             info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
1749         }
1750 
1751         getAccessibilityInjector().onInitializeAccessibilityNodeInfo(info);
1752     }
1753 
1754     @Override
onInitializeAccessibilityEvent(AccessibilityEvent event)1755     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1756         event.setScrollable(isScrollableForAccessibility());
1757         event.setScrollX(getScrollX());
1758         event.setScrollY(getScrollY());
1759         final int convertedContentWidth = contentToViewX(getContentWidth());
1760         final int adjustedViewWidth = getWidth() - mWebView.getPaddingLeft()
1761                 - mWebView.getPaddingLeft();
1762         event.setMaxScrollX(Math.max(convertedContentWidth - adjustedViewWidth, 0));
1763         final int convertedContentHeight = contentToViewY(getContentHeight());
1764         final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
1765                 - mWebView.getPaddingBottom();
1766         event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
1767     }
1768 
isAccessibilityInjectionEnabled()1769     private boolean isAccessibilityInjectionEnabled() {
1770         final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
1771         if (!manager.isEnabled()) {
1772             return false;
1773         }
1774 
1775         // Accessibility scripts should be injected only when a speaking service
1776         // is enabled. This may need to change later to accommodate Braille.
1777         final List<AccessibilityServiceInfo> services = manager.getEnabledAccessibilityServiceList(
1778                 AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1779         if (services.isEmpty()) {
1780             return false;
1781         }
1782 
1783         return true;
1784     }
1785 
getAccessibilityInjector()1786     private AccessibilityInjector getAccessibilityInjector() {
1787         if (mAccessibilityInjector == null) {
1788             mAccessibilityInjector = new AccessibilityInjector(this);
1789         }
1790         return mAccessibilityInjector;
1791     }
1792 
isScrollableForAccessibility()1793     private boolean isScrollableForAccessibility() {
1794         return (contentToViewX(getContentWidth()) > getWidth() - mWebView.getPaddingLeft()
1795                 - mWebView.getPaddingRight()
1796                 || contentToViewY(getContentHeight()) > getHeight() - mWebView.getPaddingTop()
1797                 - mWebView.getPaddingBottom());
1798     }
1799 
1800     @Override
setOverScrollMode(int mode)1801     public void setOverScrollMode(int mode) {
1802         if (mode != View.OVER_SCROLL_NEVER) {
1803             if (mOverScrollGlow == null) {
1804                 mOverScrollGlow = new OverScrollGlow(this);
1805             }
1806         } else {
1807             mOverScrollGlow = null;
1808         }
1809     }
1810 
adjustDefaultZoomDensity(int zoomDensity)1811     /* package */ void adjustDefaultZoomDensity(int zoomDensity) {
1812         final float density = WebViewCore.getFixedDisplayDensity(mContext)
1813                 * 100 / zoomDensity;
1814         updateDefaultZoomDensity(density);
1815     }
1816 
updateDefaultZoomDensity(float density)1817     /* package */ void updateDefaultZoomDensity(float density) {
1818         mNavSlop = (int) (16 * density);
1819         mZoomManager.updateDefaultZoomDensity(density);
1820     }
1821 
getScaledNavSlop()1822     /* package */ int getScaledNavSlop() {
1823         return viewToContentDimension(mNavSlop);
1824     }
1825 
onSavePassword(String schemePlusHost, String username, String password, final Message resumeMsg)1826     /* package */ boolean onSavePassword(String schemePlusHost, String username,
1827             String password, final Message resumeMsg) {
1828         boolean rVal = false;
1829         if (resumeMsg == null) {
1830             // null resumeMsg implies saving password silently
1831             mDatabase.setUsernamePassword(schemePlusHost, username, password);
1832         } else {
1833             if (mResumeMsg != null) {
1834                 Log.w(LOGTAG, "onSavePassword should not be called while dialog is up");
1835                 resumeMsg.sendToTarget();
1836                 return true;
1837             }
1838             mResumeMsg = resumeMsg;
1839             final Message remember = mPrivateHandler.obtainMessage(
1840                     REMEMBER_PASSWORD);
1841             remember.getData().putString("host", schemePlusHost);
1842             remember.getData().putString("username", username);
1843             remember.getData().putString("password", password);
1844             remember.obj = resumeMsg;
1845 
1846             final Message neverRemember = mPrivateHandler.obtainMessage(
1847                     NEVER_REMEMBER_PASSWORD);
1848             neverRemember.getData().putString("host", schemePlusHost);
1849             neverRemember.getData().putString("username", username);
1850             neverRemember.getData().putString("password", password);
1851             neverRemember.obj = resumeMsg;
1852 
1853             mSavePasswordDialog = new AlertDialog.Builder(mContext)
1854                     .setTitle(com.android.internal.R.string.save_password_label)
1855                     .setMessage(com.android.internal.R.string.save_password_message)
1856                     .setPositiveButton(com.android.internal.R.string.save_password_notnow,
1857                     new DialogInterface.OnClickListener() {
1858                         @Override
1859                         public void onClick(DialogInterface dialog, int which) {
1860                             if (mResumeMsg != null) {
1861                                 resumeMsg.sendToTarget();
1862                                 mResumeMsg = null;
1863                             }
1864                             mSavePasswordDialog = null;
1865                         }
1866                     })
1867                     .setNeutralButton(com.android.internal.R.string.save_password_remember,
1868                     new DialogInterface.OnClickListener() {
1869                         @Override
1870                         public void onClick(DialogInterface dialog, int which) {
1871                             if (mResumeMsg != null) {
1872                                 remember.sendToTarget();
1873                                 mResumeMsg = null;
1874                             }
1875                             mSavePasswordDialog = null;
1876                         }
1877                     })
1878                     .setNegativeButton(com.android.internal.R.string.save_password_never,
1879                     new DialogInterface.OnClickListener() {
1880                         @Override
1881                         public void onClick(DialogInterface dialog, int which) {
1882                             if (mResumeMsg != null) {
1883                                 neverRemember.sendToTarget();
1884                                 mResumeMsg = null;
1885                             }
1886                             mSavePasswordDialog = null;
1887                         }
1888                     })
1889                     .setOnDismissListener(new DialogInterface.OnDismissListener() {
1890                         @Override
1891                         public void onDismiss(DialogInterface dialog) {
1892                             if (mResumeMsg != null) {
1893                                 resumeMsg.sendToTarget();
1894                                 mResumeMsg = null;
1895                             }
1896                             mSavePasswordDialog = null;
1897                         }
1898                     }).show();
1899             // Return true so that WebViewCore will pause while the dialog is
1900             // up.
1901             rVal = true;
1902         }
1903         return rVal;
1904     }
1905 
1906     @Override
setScrollBarStyle(int style)1907     public void setScrollBarStyle(int style) {
1908         if (style == View.SCROLLBARS_INSIDE_INSET
1909                 || style == View.SCROLLBARS_OUTSIDE_INSET) {
1910             mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
1911         } else {
1912             mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
1913         }
1914     }
1915 
1916     /**
1917      * See {@link WebView#setHorizontalScrollbarOverlay(boolean)}
1918      */
1919     @Override
setHorizontalScrollbarOverlay(boolean overlay)1920     public void setHorizontalScrollbarOverlay(boolean overlay) {
1921         mOverlayHorizontalScrollbar = overlay;
1922     }
1923 
1924     /**
1925      * See {@link WebView#setVerticalScrollbarOverlay(boolean)
1926      */
1927     @Override
setVerticalScrollbarOverlay(boolean overlay)1928     public void setVerticalScrollbarOverlay(boolean overlay) {
1929         mOverlayVerticalScrollbar = overlay;
1930     }
1931 
1932     /**
1933      * See {@link WebView#overlayHorizontalScrollbar()}
1934      */
1935     @Override
overlayHorizontalScrollbar()1936     public boolean overlayHorizontalScrollbar() {
1937         return mOverlayHorizontalScrollbar;
1938     }
1939 
1940     /**
1941      * See {@link WebView#overlayVerticalScrollbar()}
1942      */
1943     @Override
overlayVerticalScrollbar()1944     public boolean overlayVerticalScrollbar() {
1945         return mOverlayVerticalScrollbar;
1946     }
1947 
1948     /*
1949      * Return the width of the view where the content of WebView should render
1950      * to.
1951      * Note: this can be called from WebCoreThread.
1952      */
getViewWidth()1953     /* package */ int getViewWidth() {
1954         if (!mWebView.isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
1955             return getWidth();
1956         } else {
1957             return Math.max(0, getWidth() - mWebView.getVerticalScrollbarWidth());
1958         }
1959     }
1960 
1961     // Interface to enable the browser to override title bar handling.
1962     public interface TitleBarDelegate {
getTitleHeight()1963         int getTitleHeight();
onSetEmbeddedTitleBar(final View title)1964         public void onSetEmbeddedTitleBar(final View title);
1965     }
1966 
1967     /**
1968      * Returns the height (in pixels) of the embedded title bar (if any). Does not care about
1969      * scrolling
1970      */
getTitleHeight()1971     protected int getTitleHeight() {
1972         if (mWebView instanceof TitleBarDelegate) {
1973             return ((TitleBarDelegate) mWebView).getTitleHeight();
1974         }
1975         return 0;
1976     }
1977 
1978     /**
1979      * See {@link WebView#getVisibleTitleHeight()}
1980      */
1981     @Override
1982     @Deprecated
getVisibleTitleHeight()1983     public int getVisibleTitleHeight() {
1984         // Actually, this method returns the height of the embedded title bar if one is set via the
1985         // hidden setEmbeddedTitleBar method.
1986         return getVisibleTitleHeightImpl();
1987     }
1988 
getVisibleTitleHeightImpl()1989     private int getVisibleTitleHeightImpl() {
1990         // need to restrict mScrollY due to over scroll
1991         return Math.max(getTitleHeight() - Math.max(0, getScrollY()),
1992                 getOverlappingActionModeHeight());
1993     }
1994 
1995     private int mCachedOverlappingActionModeHeight = -1;
1996 
getOverlappingActionModeHeight()1997     private int getOverlappingActionModeHeight() {
1998         if (mFindCallback == null) {
1999             return 0;
2000         }
2001         if (mCachedOverlappingActionModeHeight < 0) {
2002             mWebView.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
2003             mCachedOverlappingActionModeHeight = Math.max(0,
2004                     mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top);
2005         }
2006         return mCachedOverlappingActionModeHeight;
2007     }
2008 
2009     /*
2010      * Return the height of the view where the content of WebView should render
2011      * to.  Note that this excludes mTitleBar, if there is one.
2012      * Note: this can be called from WebCoreThread.
2013      */
getViewHeight()2014     /* package */ int getViewHeight() {
2015         return getViewHeightWithTitle() - getVisibleTitleHeightImpl();
2016     }
2017 
getViewHeightWithTitle()2018     int getViewHeightWithTitle() {
2019         int height = getHeight();
2020         if (mWebView.isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
2021             height -= mWebViewPrivate.getHorizontalScrollbarHeight();
2022         }
2023         return height;
2024     }
2025 
2026     /**
2027      * See {@link WebView#getCertificate()}
2028      */
2029     @Override
getCertificate()2030     public SslCertificate getCertificate() {
2031         return mCertificate;
2032     }
2033 
2034     /**
2035      * See {@link WebView#setCertificate(SslCertificate)}
2036      */
2037     @Override
setCertificate(SslCertificate certificate)2038     public void setCertificate(SslCertificate certificate) {
2039         if (DebugFlags.WEB_VIEW) {
2040             Log.v(LOGTAG, "setCertificate=" + certificate);
2041         }
2042         // here, the certificate can be null (if the site is not secure)
2043         mCertificate = certificate;
2044     }
2045 
2046     //-------------------------------------------------------------------------
2047     // Methods called by activity
2048     //-------------------------------------------------------------------------
2049 
2050     /**
2051      * See {@link WebView#savePassword(String, String, String)}
2052      */
2053     @Override
savePassword(String host, String username, String password)2054     public void savePassword(String host, String username, String password) {
2055         mDatabase.setUsernamePassword(host, username, password);
2056     }
2057 
2058     /**
2059      * See {@link WebView#setHttpAuthUsernamePassword(String, String, String, String)}
2060      */
2061     @Override
setHttpAuthUsernamePassword(String host, String realm, String username, String password)2062     public void setHttpAuthUsernamePassword(String host, String realm,
2063             String username, String password) {
2064         mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
2065     }
2066 
2067     /**
2068      * See {@link WebView#getHttpAuthUsernamePassword(String, String)}
2069      */
2070     @Override
getHttpAuthUsernamePassword(String host, String realm)2071     public String[] getHttpAuthUsernamePassword(String host, String realm) {
2072         return mDatabase.getHttpAuthUsernamePassword(host, realm);
2073     }
2074 
2075     /**
2076      * Remove Find or Select ActionModes, if active.
2077      */
clearActionModes()2078     private void clearActionModes() {
2079         if (mSelectCallback != null) {
2080             mSelectCallback.finish();
2081         }
2082         if (mFindCallback != null) {
2083             mFindCallback.finish();
2084         }
2085     }
2086 
2087     /**
2088      * Called to clear state when moving from one page to another, or changing
2089      * in some other way that makes elements associated with the current page
2090      * (such as ActionModes) no longer relevant.
2091      */
clearHelpers()2092     private void clearHelpers() {
2093         hideSoftKeyboard();
2094         clearActionModes();
2095         dismissFullScreenMode();
2096         cancelDialogs();
2097     }
2098 
cancelDialogs()2099     private void cancelDialogs() {
2100         if (mListBoxDialog != null) {
2101             mListBoxDialog.cancel();
2102             mListBoxDialog = null;
2103         }
2104         if (mSavePasswordDialog != null) {
2105             mSavePasswordDialog.dismiss();
2106             mSavePasswordDialog = null;
2107         }
2108     }
2109 
2110     /**
2111      * See {@link WebView#destroy()}
2112      */
2113     @Override
destroy()2114     public void destroy() {
2115         if (mWebView.getViewRootImpl() != null) {
2116             Log.e(LOGTAG, "Error: WebView.destroy() called while still attached!");
2117         }
2118         ensureFunctorDetached();
2119         destroyJava();
2120         destroyNative();
2121     }
2122 
ensureFunctorDetached()2123     private void ensureFunctorDetached() {
2124         if (mWebView.isHardwareAccelerated()) {
2125             int drawGLFunction = nativeGetDrawGLFunction(mNativeClass);
2126             ViewRootImpl viewRoot = mWebView.getViewRootImpl();
2127             if (drawGLFunction != 0 && viewRoot != null) {
2128                 viewRoot.detachFunctor(drawGLFunction);
2129             }
2130         }
2131     }
2132 
destroyJava()2133     private void destroyJava() {
2134         mCallbackProxy.blockMessages();
2135         if (mAccessibilityInjector != null) {
2136             mAccessibilityInjector.destroy();
2137             mAccessibilityInjector = null;
2138         }
2139         if (mWebViewCore != null) {
2140             // Tell WebViewCore to destroy itself
2141             synchronized (this) {
2142                 WebViewCore webViewCore = mWebViewCore;
2143                 mWebViewCore = null; // prevent using partial webViewCore
2144                 webViewCore.destroy();
2145             }
2146             // Remove any pending messages that might not be serviced yet.
2147             mPrivateHandler.removeCallbacksAndMessages(null);
2148         }
2149     }
2150 
destroyNative()2151     private void destroyNative() {
2152         if (mNativeClass == 0) return;
2153         int nptr = mNativeClass;
2154         mNativeClass = 0;
2155         if (Thread.currentThread() == mPrivateHandler.getLooper().getThread()) {
2156             // We are on the main thread and can safely delete
2157             nativeDestroy(nptr);
2158         } else {
2159             mPrivateHandler.post(new DestroyNativeRunnable(nptr));
2160         }
2161     }
2162 
2163     private static class DestroyNativeRunnable implements Runnable {
2164 
2165         private int mNativePtr;
2166 
DestroyNativeRunnable(int nativePtr)2167         public DestroyNativeRunnable(int nativePtr) {
2168             mNativePtr = nativePtr;
2169         }
2170 
2171         @Override
run()2172         public void run() {
2173             // nativeDestroy also does a stopGL()
2174             nativeDestroy(mNativePtr);
2175         }
2176 
2177     }
2178 
2179     /**
2180      * See {@link WebView#enablePlatformNotifications()}
2181      */
2182     @Deprecated
enablePlatformNotifications()2183     public static void enablePlatformNotifications() {
2184         synchronized (WebViewClassic.class) {
2185             sNotificationsEnabled = true;
2186             Context context = JniUtil.getContext();
2187             if (context != null)
2188                 setupProxyListener(context);
2189         }
2190     }
2191 
2192     /**
2193      * See {@link WebView#disablePlatformNotifications()}
2194      */
2195     @Deprecated
disablePlatformNotifications()2196     public static void disablePlatformNotifications() {
2197         synchronized (WebViewClassic.class) {
2198             sNotificationsEnabled = false;
2199             Context context = JniUtil.getContext();
2200             if (context != null)
2201                 disableProxyListener(context);
2202         }
2203     }
2204 
2205     /**
2206      * Sets JavaScript engine flags.
2207      *
2208      * @param flags JS engine flags in a String
2209      *
2210      * This is an implementation detail.
2211      */
setJsFlags(String flags)2212     public void setJsFlags(String flags) {
2213         mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
2214     }
2215 
2216     /**
2217      * See {@link WebView#setNetworkAvailable(boolean)}
2218      */
2219     @Override
setNetworkAvailable(boolean networkUp)2220     public void setNetworkAvailable(boolean networkUp) {
2221         mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
2222                 networkUp ? 1 : 0, 0);
2223     }
2224 
2225     /**
2226      * Inform WebView about the current network type.
2227      */
setNetworkType(String type, String subtype)2228     public void setNetworkType(String type, String subtype) {
2229         Map<String, String> map = new HashMap<String, String>();
2230         map.put("type", type);
2231         map.put("subtype", subtype);
2232         mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
2233     }
2234 
2235     /**
2236      * See {@link WebView#saveState(Bundle)}
2237      */
2238     @Override
saveState(Bundle outState)2239     public WebBackForwardList saveState(Bundle outState) {
2240         if (outState == null) {
2241             return null;
2242         }
2243         // We grab a copy of the back/forward list because a client of WebView
2244         // may have invalidated the history list by calling clearHistory.
2245         WebBackForwardListClassic list = copyBackForwardList();
2246         final int currentIndex = list.getCurrentIndex();
2247         final int size = list.getSize();
2248         // We should fail saving the state if the list is empty or the index is
2249         // not in a valid range.
2250         if (currentIndex < 0 || currentIndex >= size || size == 0) {
2251             return null;
2252         }
2253         outState.putInt("index", currentIndex);
2254         // FIXME: This should just be a byte[][] instead of ArrayList but
2255         // Parcel.java does not have the code to handle multi-dimensional
2256         // arrays.
2257         ArrayList<byte[]> history = new ArrayList<byte[]>(size);
2258         for (int i = 0; i < size; i++) {
2259             WebHistoryItemClassic item = list.getItemAtIndex(i);
2260             if (null == item) {
2261                 // FIXME: this shouldn't happen
2262                 // need to determine how item got set to null
2263                 Log.w(LOGTAG, "saveState: Unexpected null history item.");
2264                 return null;
2265             }
2266             byte[] data = item.getFlattenedData();
2267             if (data == null) {
2268                 // It would be very odd to not have any data for a given history
2269                 // item. And we will fail to rebuild the history list without
2270                 // flattened data.
2271                 return null;
2272             }
2273             history.add(data);
2274         }
2275         outState.putSerializable("history", history);
2276         if (mCertificate != null) {
2277             outState.putBundle("certificate",
2278                                SslCertificate.saveState(mCertificate));
2279         }
2280         outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
2281         mZoomManager.saveZoomState(outState);
2282         return list;
2283     }
2284 
2285     /**
2286      * See {@link WebView#savePicture(Bundle, File)}
2287      */
2288     @Override
2289     @Deprecated
savePicture(Bundle b, final File dest)2290     public boolean savePicture(Bundle b, final File dest) {
2291         if (dest == null || b == null) {
2292             return false;
2293         }
2294         final Picture p = capturePicture();
2295         // Use a temporary file while writing to ensure the destination file
2296         // contains valid data.
2297         final File temp = new File(dest.getPath() + ".writing");
2298         new Thread(new Runnable() {
2299             @Override
2300             public void run() {
2301                 FileOutputStream out = null;
2302                 try {
2303                     out = new FileOutputStream(temp);
2304                     p.writeToStream(out);
2305                     // Writing the picture succeeded, rename the temporary file
2306                     // to the destination.
2307                     temp.renameTo(dest);
2308                 } catch (Exception e) {
2309                     // too late to do anything about it.
2310                 } finally {
2311                     if (out != null) {
2312                         try {
2313                             out.close();
2314                         } catch (Exception e) {
2315                             // Can't do anything about that
2316                         }
2317                     }
2318                     temp.delete();
2319                 }
2320             }
2321         }).start();
2322         // now update the bundle
2323         b.putInt("scrollX", getScrollX());
2324         b.putInt("scrollY", getScrollY());
2325         mZoomManager.saveZoomState(b);
2326         return true;
2327     }
2328 
restoreHistoryPictureFields(Picture p, Bundle b)2329     private void restoreHistoryPictureFields(Picture p, Bundle b) {
2330         int sx = b.getInt("scrollX", 0);
2331         int sy = b.getInt("scrollY", 0);
2332 
2333         mDrawHistory = true;
2334         mHistoryPicture = p;
2335 
2336         setScrollXRaw(sx);
2337         setScrollYRaw(sy);
2338         mZoomManager.restoreZoomState(b);
2339         final float scale = mZoomManager.getScale();
2340         mHistoryWidth = Math.round(p.getWidth() * scale);
2341         mHistoryHeight = Math.round(p.getHeight() * scale);
2342 
2343         invalidate();
2344     }
2345 
2346     /**
2347      * See {@link WebView#restorePicture(Bundle, File)};
2348      */
2349     @Override
2350     @Deprecated
restorePicture(Bundle b, File src)2351     public boolean restorePicture(Bundle b, File src) {
2352         if (src == null || b == null) {
2353             return false;
2354         }
2355         if (!src.exists()) {
2356             return false;
2357         }
2358         try {
2359             final FileInputStream in = new FileInputStream(src);
2360             final Bundle copy = new Bundle(b);
2361             new Thread(new Runnable() {
2362                 @Override
2363                 public void run() {
2364                     try {
2365                         final Picture p = Picture.createFromStream(in);
2366                         if (p != null) {
2367                             // Post a runnable on the main thread to update the
2368                             // history picture fields.
2369                             mPrivateHandler.post(new Runnable() {
2370                                 @Override
2371                                 public void run() {
2372                                     restoreHistoryPictureFields(p, copy);
2373                                 }
2374                             });
2375                         }
2376                     } finally {
2377                         try {
2378                             in.close();
2379                         } catch (Exception e) {
2380                             // Nothing we can do now.
2381                         }
2382                     }
2383                 }
2384             }).start();
2385         } catch (FileNotFoundException e){
2386             e.printStackTrace();
2387         }
2388         return true;
2389     }
2390 
2391     /**
2392      * Saves the view data to the output stream. The output is highly
2393      * version specific, and may not be able to be loaded by newer versions
2394      * of WebView.
2395      * @param stream The {@link OutputStream} to save to
2396      * @param callback The {@link ValueCallback} to call with the result
2397      */
saveViewState(OutputStream stream, ValueCallback<Boolean> callback)2398     public void saveViewState(OutputStream stream, ValueCallback<Boolean> callback) {
2399         if (mWebViewCore == null) {
2400             callback.onReceiveValue(false);
2401             return;
2402         }
2403         mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SAVE_VIEW_STATE,
2404                 new WebViewCore.SaveViewStateRequest(stream, callback));
2405     }
2406 
2407     /**
2408      * Loads the view data from the input stream. See
2409      * {@link #saveViewState(java.io.OutputStream, ValueCallback)} for more information.
2410      * @param stream The {@link InputStream} to load from
2411      */
loadViewState(InputStream stream)2412     public void loadViewState(InputStream stream) {
2413         mBlockWebkitViewMessages = true;
2414         new AsyncTask<InputStream, Void, DrawData>() {
2415 
2416             @Override
2417             protected DrawData doInBackground(InputStream... params) {
2418                 try {
2419                     return ViewStateSerializer.deserializeViewState(params[0]);
2420                 } catch (IOException e) {
2421                     return null;
2422                 }
2423             }
2424 
2425             @Override
2426             protected void onPostExecute(DrawData draw) {
2427                 if (draw == null) {
2428                     Log.e(LOGTAG, "Failed to load view state!");
2429                     return;
2430                 }
2431                 int viewWidth = getViewWidth();
2432                 int viewHeight = getViewHeightWithTitle() - getTitleHeight();
2433                 draw.mViewSize = new Point(viewWidth, viewHeight);
2434                 draw.mViewState.mDefaultScale = getDefaultZoomScale();
2435                 mLoadedPicture = draw;
2436                 setNewPicture(mLoadedPicture, true);
2437                 mLoadedPicture.mViewState = null;
2438             }
2439 
2440         }.execute(stream);
2441     }
2442 
2443     /**
2444      * Clears the view state set with {@link #loadViewState(InputStream)}.
2445      * This WebView will then switch to showing the content from webkit
2446      */
clearViewState()2447     public void clearViewState() {
2448         mBlockWebkitViewMessages = false;
2449         mLoadedPicture = null;
2450         invalidate();
2451     }
2452 
2453     /**
2454      * See {@link WebView#restoreState(Bundle)}
2455      */
2456     @Override
restoreState(Bundle inState)2457     public WebBackForwardList restoreState(Bundle inState) {
2458         WebBackForwardListClassic returnList = null;
2459         if (inState == null) {
2460             return returnList;
2461         }
2462         if (inState.containsKey("index") && inState.containsKey("history")) {
2463             mCertificate = SslCertificate.restoreState(
2464                 inState.getBundle("certificate"));
2465 
2466             final WebBackForwardListClassic list = mCallbackProxy.getBackForwardList();
2467             final int index = inState.getInt("index");
2468             // We can't use a clone of the list because we need to modify the
2469             // shared copy, so synchronize instead to prevent concurrent
2470             // modifications.
2471             synchronized (list) {
2472                 final List<byte[]> history =
2473                         (List<byte[]>) inState.getSerializable("history");
2474                 final int size = history.size();
2475                 // Check the index bounds so we don't crash in native code while
2476                 // restoring the history index.
2477                 if (index < 0 || index >= size) {
2478                     return null;
2479                 }
2480                 for (int i = 0; i < size; i++) {
2481                     byte[] data = history.remove(0);
2482                     if (data == null) {
2483                         // If we somehow have null data, we cannot reconstruct
2484                         // the item and thus our history list cannot be rebuilt.
2485                         return null;
2486                     }
2487                     WebHistoryItem item = new WebHistoryItemClassic(data);
2488                     list.addHistoryItem(item);
2489                 }
2490                 // Grab the most recent copy to return to the caller.
2491                 returnList = copyBackForwardList();
2492                 // Update the copy to have the correct index.
2493                 returnList.setCurrentIndex(index);
2494             }
2495             // Restore private browsing setting.
2496             if (inState.getBoolean("privateBrowsingEnabled")) {
2497                 getSettings().setPrivateBrowsingEnabled(true);
2498             }
2499             mZoomManager.restoreZoomState(inState);
2500             // Remove all pending messages because we are restoring previous
2501             // state.
2502             mWebViewCore.removeMessages();
2503             if (isAccessibilityInjectionEnabled()) {
2504                 getAccessibilityInjector().addAccessibilityApisIfNecessary();
2505             }
2506             // Send a restore state message.
2507             mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
2508         }
2509         return returnList;
2510     }
2511 
2512     /**
2513      * See {@link WebView#loadUrl(String, Map)}
2514      */
2515     @Override
loadUrl(String url, Map<String, String> additionalHttpHeaders)2516     public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
2517         loadUrlImpl(url, additionalHttpHeaders);
2518     }
2519 
loadUrlImpl(String url, Map<String, String> extraHeaders)2520     private void loadUrlImpl(String url, Map<String, String> extraHeaders) {
2521         switchOutDrawHistory();
2522         WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
2523         arg.mUrl = url;
2524         arg.mExtraHeaders = extraHeaders;
2525         mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
2526         clearHelpers();
2527     }
2528 
2529     /**
2530      * See {@link WebView#loadUrl(String)}
2531      */
2532     @Override
loadUrl(String url)2533     public void loadUrl(String url) {
2534         loadUrlImpl(url);
2535     }
2536 
loadUrlImpl(String url)2537     private void loadUrlImpl(String url) {
2538         if (url == null) {
2539             return;
2540         }
2541         loadUrlImpl(url, null);
2542     }
2543 
2544     /**
2545      * See {@link WebView#postUrl(String, byte[])}
2546      */
2547     @Override
postUrl(String url, byte[] postData)2548     public void postUrl(String url, byte[] postData) {
2549         if (URLUtil.isNetworkUrl(url)) {
2550             switchOutDrawHistory();
2551             WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
2552             arg.mUrl = url;
2553             arg.mPostData = postData;
2554             mWebViewCore.sendMessage(EventHub.POST_URL, arg);
2555             clearHelpers();
2556         } else {
2557             loadUrlImpl(url);
2558         }
2559     }
2560 
2561     /**
2562      * See {@link WebView#loadData(String, String, String)}
2563      */
2564     @Override
loadData(String data, String mimeType, String encoding)2565     public void loadData(String data, String mimeType, String encoding) {
2566         loadDataImpl(data, mimeType, encoding);
2567     }
2568 
loadDataImpl(String data, String mimeType, String encoding)2569     private void loadDataImpl(String data, String mimeType, String encoding) {
2570         StringBuilder dataUrl = new StringBuilder("data:");
2571         dataUrl.append(mimeType);
2572         if ("base64".equals(encoding)) {
2573             dataUrl.append(";base64");
2574         }
2575         dataUrl.append(",");
2576         dataUrl.append(data);
2577         loadUrlImpl(dataUrl.toString());
2578     }
2579 
2580     /**
2581      * See {@link WebView#loadDataWithBaseURL(String, String, String, String, String)}
2582      */
2583     @Override
loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)2584     public void loadDataWithBaseURL(String baseUrl, String data,
2585             String mimeType, String encoding, String historyUrl) {
2586 
2587         if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
2588             loadDataImpl(data, mimeType, encoding);
2589             return;
2590         }
2591         switchOutDrawHistory();
2592         WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
2593         arg.mBaseUrl = baseUrl;
2594         arg.mData = data;
2595         arg.mMimeType = mimeType;
2596         arg.mEncoding = encoding;
2597         arg.mHistoryUrl = historyUrl;
2598         mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
2599         clearHelpers();
2600     }
2601 
2602     /**
2603      * See {@link WebView#saveWebArchive(String)}
2604      */
2605     @Override
saveWebArchive(String filename)2606     public void saveWebArchive(String filename) {
2607         saveWebArchiveImpl(filename, false, null);
2608     }
2609 
2610     /* package */ static class SaveWebArchiveMessage {
SaveWebArchiveMessage(String basename, boolean autoname, ValueCallback<String> callback)2611         SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) {
2612             mBasename = basename;
2613             mAutoname = autoname;
2614             mCallback = callback;
2615         }
2616 
2617         /* package */ final String mBasename;
2618         /* package */ final boolean mAutoname;
2619         /* package */ final ValueCallback<String> mCallback;
2620         /* package */ String mResultFile;
2621     }
2622 
2623     /**
2624      * See {@link WebView#saveWebArchive(String, boolean, ValueCallback)}
2625      */
2626     @Override
saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback)2627     public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
2628         saveWebArchiveImpl(basename, autoname, callback);
2629     }
2630 
saveWebArchiveImpl(String basename, boolean autoname, ValueCallback<String> callback)2631     private void saveWebArchiveImpl(String basename, boolean autoname,
2632             ValueCallback<String> callback) {
2633         mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
2634             new SaveWebArchiveMessage(basename, autoname, callback));
2635     }
2636 
2637     /**
2638      * See {@link WebView#stopLoading()}
2639      */
2640     @Override
stopLoading()2641     public void stopLoading() {
2642         // TODO: should we clear all the messages in the queue before sending
2643         // STOP_LOADING?
2644         switchOutDrawHistory();
2645         mWebViewCore.sendMessage(EventHub.STOP_LOADING);
2646     }
2647 
2648     /**
2649      * See {@link WebView#reload()}
2650      */
2651     @Override
reload()2652     public void reload() {
2653         clearHelpers();
2654         switchOutDrawHistory();
2655         mWebViewCore.sendMessage(EventHub.RELOAD);
2656     }
2657 
2658     /**
2659      * See {@link WebView#canGoBack()}
2660      */
2661     @Override
canGoBack()2662     public boolean canGoBack() {
2663         WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
2664         synchronized (l) {
2665             if (l.getClearPending()) {
2666                 return false;
2667             } else {
2668                 return l.getCurrentIndex() > 0;
2669             }
2670         }
2671     }
2672 
2673     /**
2674      * See {@link WebView#goBack()}
2675      */
2676     @Override
goBack()2677     public void goBack() {
2678         goBackOrForwardImpl(-1);
2679     }
2680 
2681     /**
2682      * See {@link WebView#canGoForward()}
2683      */
2684     @Override
canGoForward()2685     public boolean canGoForward() {
2686         WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
2687         synchronized (l) {
2688             if (l.getClearPending()) {
2689                 return false;
2690             } else {
2691                 return l.getCurrentIndex() < l.getSize() - 1;
2692             }
2693         }
2694     }
2695 
2696     /**
2697      * See {@link WebView#goForward()}
2698      */
2699     @Override
goForward()2700     public void goForward() {
2701         goBackOrForwardImpl(1);
2702     }
2703 
2704     /**
2705      * See {@link WebView#canGoBackOrForward(int)}
2706      */
2707     @Override
canGoBackOrForward(int steps)2708     public boolean canGoBackOrForward(int steps) {
2709         WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
2710         synchronized (l) {
2711             if (l.getClearPending()) {
2712                 return false;
2713             } else {
2714                 int newIndex = l.getCurrentIndex() + steps;
2715                 return newIndex >= 0 && newIndex < l.getSize();
2716             }
2717         }
2718     }
2719 
2720     /**
2721      * See {@link WebView#goBackOrForward(int)}
2722      */
2723     @Override
goBackOrForward(int steps)2724     public void goBackOrForward(int steps) {
2725         goBackOrForwardImpl(steps);
2726     }
2727 
goBackOrForwardImpl(int steps)2728     private void goBackOrForwardImpl(int steps) {
2729         goBackOrForward(steps, false);
2730     }
2731 
goBackOrForward(int steps, boolean ignoreSnapshot)2732     private void goBackOrForward(int steps, boolean ignoreSnapshot) {
2733         if (steps != 0) {
2734             clearHelpers();
2735             mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
2736                     ignoreSnapshot ? 1 : 0);
2737         }
2738     }
2739 
2740     /**
2741      * See {@link WebView#isPrivateBrowsingEnabled()}
2742      */
2743     @Override
isPrivateBrowsingEnabled()2744     public boolean isPrivateBrowsingEnabled() {
2745         WebSettingsClassic settings = getSettings();
2746         return (settings != null) ? settings.isPrivateBrowsingEnabled() : false;
2747     }
2748 
startPrivateBrowsing()2749     private void startPrivateBrowsing() {
2750         getSettings().setPrivateBrowsingEnabled(true);
2751     }
2752 
extendScroll(int y)2753     private boolean extendScroll(int y) {
2754         int finalY = mScroller.getFinalY();
2755         int newY = pinLocY(finalY + y);
2756         if (newY == finalY) return false;
2757         mScroller.setFinalY(newY);
2758         mScroller.extendDuration(computeDuration(0, y));
2759         return true;
2760     }
2761 
2762     /**
2763      * See {@link WebView#pageUp(boolean)}
2764      */
2765     @Override
pageUp(boolean top)2766     public boolean pageUp(boolean top) {
2767         if (mNativeClass == 0) {
2768             return false;
2769         }
2770         if (top) {
2771             // go to the top of the document
2772             return pinScrollTo(getScrollX(), 0, true, 0);
2773         }
2774         // Page up
2775         int h = getHeight();
2776         int y;
2777         if (h > 2 * PAGE_SCROLL_OVERLAP) {
2778             y = -h + PAGE_SCROLL_OVERLAP;
2779         } else {
2780             y = -h / 2;
2781         }
2782         return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
2783                 : extendScroll(y);
2784     }
2785 
2786     /**
2787      * See {@link WebView#pageDown(boolean)}
2788      */
2789     @Override
pageDown(boolean bottom)2790     public boolean pageDown(boolean bottom) {
2791         if (mNativeClass == 0) {
2792             return false;
2793         }
2794         if (bottom) {
2795             return pinScrollTo(getScrollX(), computeRealVerticalScrollRange(), true, 0);
2796         }
2797         // Page down.
2798         int h = getHeight();
2799         int y;
2800         if (h > 2 * PAGE_SCROLL_OVERLAP) {
2801             y = h - PAGE_SCROLL_OVERLAP;
2802         } else {
2803             y = h / 2;
2804         }
2805         return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
2806                 : extendScroll(y);
2807     }
2808 
2809     /**
2810      * See {@link WebView#clearView()}
2811      */
2812     @Override
clearView()2813     public void clearView() {
2814         mContentWidth = 0;
2815         mContentHeight = 0;
2816         setBaseLayer(0, false, false);
2817         mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
2818     }
2819 
2820     /**
2821      * See {@link WebView#capturePicture()}
2822      */
2823     @Override
capturePicture()2824     public Picture capturePicture() {
2825         if (mNativeClass == 0) return null;
2826         Picture result = new Picture();
2827         nativeCopyBaseContentToPicture(result);
2828         return result;
2829     }
2830 
2831     /**
2832      * See {@link WebView#getScale()}
2833      */
2834     @Override
getScale()2835     public float getScale() {
2836         return mZoomManager.getScale();
2837     }
2838 
2839     /**
2840      * Compute the reading level scale of the WebView
2841      * @param scale The current scale.
2842      * @return The reading level scale.
2843      */
computeReadingLevelScale(float scale)2844     /*package*/ float computeReadingLevelScale(float scale) {
2845         return mZoomManager.computeReadingLevelScale(scale);
2846     }
2847 
2848     /**
2849      * See {@link WebView#setInitialScale(int)}
2850      */
2851     @Override
setInitialScale(int scaleInPercent)2852     public void setInitialScale(int scaleInPercent) {
2853         mZoomManager.setInitialScaleInPercent(scaleInPercent);
2854     }
2855 
2856     /**
2857      * See {@link WebView#invokeZoomPicker()}
2858      */
2859     @Override
invokeZoomPicker()2860     public void invokeZoomPicker() {
2861         if (!getSettings().supportZoom()) {
2862             Log.w(LOGTAG, "This WebView doesn't support zoom.");
2863             return;
2864         }
2865         clearHelpers();
2866         mZoomManager.invokeZoomPicker();
2867     }
2868 
2869     /**
2870      * See {@link WebView#getHitTestResult()}
2871      */
2872     @Override
getHitTestResult()2873     public HitTestResult getHitTestResult() {
2874         return mInitialHitTestResult;
2875     }
2876 
2877     // No left edge for double-tap zoom alignment
2878     static final int NO_LEFTEDGE = -1;
2879 
getBlockLeftEdge(int x, int y, float readingScale)2880     int getBlockLeftEdge(int x, int y, float readingScale) {
2881         float invReadingScale = 1.0f / readingScale;
2882         int readingWidth = (int) (getViewWidth() * invReadingScale);
2883         int left = NO_LEFTEDGE;
2884         if (mFocusedNode != null) {
2885             final int length = mFocusedNode.mEnclosingParentRects.length;
2886             for (int i = 0; i < length; i++) {
2887                 Rect rect = mFocusedNode.mEnclosingParentRects[i];
2888                 if (rect.width() < mFocusedNode.mHitTestSlop) {
2889                     // ignore bounding boxes that are too small
2890                     continue;
2891                 } else if (rect.width() > readingWidth) {
2892                     // stop when bounding box doesn't fit the screen width
2893                     // at reading scale
2894                     break;
2895                 }
2896 
2897                 left = rect.left;
2898             }
2899         }
2900 
2901         return left;
2902     }
2903 
2904     /**
2905      * See {@link WebView#requestFocusNodeHref(Message)}
2906      */
2907     @Override
requestFocusNodeHref(Message hrefMsg)2908     public void requestFocusNodeHref(Message hrefMsg) {
2909         if (hrefMsg == null) {
2910             return;
2911         }
2912         int contentX = viewToContentX(mLastTouchX + getScrollX());
2913         int contentY = viewToContentY(mLastTouchY + getScrollY());
2914         if (mFocusedNode != null && mFocusedNode.mHitTestX == contentX
2915                 && mFocusedNode.mHitTestY == contentY) {
2916             hrefMsg.getData().putString(FocusNodeHref.URL, mFocusedNode.mLinkUrl);
2917             hrefMsg.getData().putString(FocusNodeHref.TITLE, mFocusedNode.mAnchorText);
2918             hrefMsg.getData().putString(FocusNodeHref.SRC, mFocusedNode.mImageUrl);
2919             hrefMsg.sendToTarget();
2920             return;
2921         }
2922         mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
2923                 contentX, contentY, hrefMsg);
2924     }
2925 
2926     /**
2927      * See {@link WebView#requestImageRef(Message)}
2928      */
2929     @Override
requestImageRef(Message msg)2930     public void requestImageRef(Message msg) {
2931         if (0 == mNativeClass) return; // client isn't initialized
2932         String url = mFocusedNode != null ? mFocusedNode.mImageUrl : null;
2933         Bundle data = msg.getData();
2934         data.putString("url", url);
2935         msg.setData(data);
2936         msg.sendToTarget();
2937     }
2938 
pinLoc(int x, int viewMax, int docMax)2939     static int pinLoc(int x, int viewMax, int docMax) {
2940 //        Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
2941         if (docMax < viewMax) {   // the doc has room on the sides for "blank"
2942             // pin the short document to the top/left of the screen
2943             x = 0;
2944 //            Log.d(LOGTAG, "--- center " + x);
2945         } else if (x < 0) {
2946             x = 0;
2947 //            Log.d(LOGTAG, "--- zero");
2948         } else if (x + viewMax > docMax) {
2949             x = docMax - viewMax;
2950 //            Log.d(LOGTAG, "--- pin " + x);
2951         }
2952         return x;
2953     }
2954 
2955     // Expects x in view coordinates
pinLocX(int x)2956     int pinLocX(int x) {
2957         if (mInOverScrollMode) return x;
2958         return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
2959     }
2960 
2961     // Expects y in view coordinates
pinLocY(int y)2962     int pinLocY(int y) {
2963         if (mInOverScrollMode) return y;
2964         return pinLoc(y, getViewHeightWithTitle(),
2965                       computeRealVerticalScrollRange() + getTitleHeight());
2966     }
2967 
2968     /**
2969      * Given a distance in view space, convert it to content space. Note: this
2970      * does not reflect translation, just scaling, so this should not be called
2971      * with coordinates, but should be called for dimensions like width or
2972      * height.
2973      */
viewToContentDimension(int d)2974     private int viewToContentDimension(int d) {
2975         return Math.round(d * mZoomManager.getInvScale());
2976     }
2977 
2978     /**
2979      * Given an x coordinate in view space, convert it to content space.  Also
2980      * may be used for absolute heights.
2981      */
viewToContentX(int x)2982     /*package*/ int viewToContentX(int x) {
2983         return viewToContentDimension(x);
2984     }
2985 
2986     /**
2987      * Given a y coordinate in view space, convert it to content space.
2988      * Takes into account the height of the title bar if there is one
2989      * embedded into the WebView.
2990      */
viewToContentY(int y)2991     /*package*/ int viewToContentY(int y) {
2992         return viewToContentDimension(y - getTitleHeight());
2993     }
2994 
2995     /**
2996      * Given a x coordinate in view space, convert it to content space.
2997      * Returns the result as a float.
2998      */
viewToContentXf(int x)2999     private float viewToContentXf(int x) {
3000         return x * mZoomManager.getInvScale();
3001     }
3002 
3003     /**
3004      * Given a y coordinate in view space, convert it to content space.
3005      * Takes into account the height of the title bar if there is one
3006      * embedded into the WebView. Returns the result as a float.
3007      */
viewToContentYf(int y)3008     private float viewToContentYf(int y) {
3009         return (y - getTitleHeight()) * mZoomManager.getInvScale();
3010     }
3011 
3012     /**
3013      * Given a distance in content space, convert it to view space. Note: this
3014      * does not reflect translation, just scaling, so this should not be called
3015      * with coordinates, but should be called for dimensions like width or
3016      * height.
3017      */
contentToViewDimension(int d)3018     /*package*/ int contentToViewDimension(int d) {
3019         return Math.round(d * mZoomManager.getScale());
3020     }
3021 
3022     /**
3023      * Given an x coordinate in content space, convert it to view
3024      * space.
3025      */
contentToViewX(int x)3026     /*package*/ int contentToViewX(int x) {
3027         return contentToViewDimension(x);
3028     }
3029 
3030     /**
3031      * Given a y coordinate in content space, convert it to view
3032      * space.  Takes into account the height of the title bar.
3033      */
contentToViewY(int y)3034     /*package*/ int contentToViewY(int y) {
3035         return contentToViewDimension(y) + getTitleHeight();
3036     }
3037 
contentToViewRect(Rect x)3038     private Rect contentToViewRect(Rect x) {
3039         return new Rect(contentToViewX(x.left), contentToViewY(x.top),
3040                         contentToViewX(x.right), contentToViewY(x.bottom));
3041     }
3042 
3043     /*  To invalidate a rectangle in content coordinates, we need to transform
3044         the rect into view coordinates, so we can then call invalidate(...).
3045 
3046         Normally, we would just call contentToView[XY](...), which eventually
3047         calls Math.round(coordinate * mActualScale). However, for invalidates,
3048         we need to account for the slop that occurs with antialiasing. To
3049         address that, we are a little more liberal in the size of the rect that
3050         we invalidate.
3051 
3052         This liberal calculation calls floor() for the top/left, and ceil() for
3053         the bottom/right coordinates. This catches the possible extra pixels of
3054         antialiasing that we might have missed with just round().
3055      */
3056 
3057     // Called by JNI to invalidate the View, given rectangle coordinates in
3058     // content space
viewInvalidate(int l, int t, int r, int b)3059     private void viewInvalidate(int l, int t, int r, int b) {
3060         final float scale = mZoomManager.getScale();
3061         final int dy = getTitleHeight();
3062         mWebView.invalidate((int)Math.floor(l * scale),
3063                 (int)Math.floor(t * scale) + dy,
3064                 (int)Math.ceil(r * scale),
3065                 (int)Math.ceil(b * scale) + dy);
3066     }
3067 
3068     // Called by JNI to invalidate the View after a delay, given rectangle
3069     // coordinates in content space
viewInvalidateDelayed(long delay, int l, int t, int r, int b)3070     private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
3071         final float scale = mZoomManager.getScale();
3072         final int dy = getTitleHeight();
3073         mWebView.postInvalidateDelayed(delay,
3074                 (int)Math.floor(l * scale),
3075                 (int)Math.floor(t * scale) + dy,
3076                 (int)Math.ceil(r * scale),
3077                 (int)Math.ceil(b * scale) + dy);
3078     }
3079 
invalidateContentRect(Rect r)3080     private void invalidateContentRect(Rect r) {
3081         viewInvalidate(r.left, r.top, r.right, r.bottom);
3082     }
3083 
3084     // stop the scroll animation, and don't let a subsequent fling add
3085     // to the existing velocity
abortAnimation()3086     private void abortAnimation() {
3087         mScroller.abortAnimation();
3088         mLastVelocity = 0;
3089     }
3090 
3091     /* call from webcoreview.draw(), so we're still executing in the UI thread
3092     */
recordNewContentSize(int w, int h, boolean updateLayout)3093     private void recordNewContentSize(int w, int h, boolean updateLayout) {
3094 
3095         // premature data from webkit, ignore
3096         if ((w | h) == 0) {
3097             invalidate();
3098             return;
3099         }
3100 
3101         // don't abort a scroll animation if we didn't change anything
3102         if (mContentWidth != w || mContentHeight != h) {
3103             // record new dimensions
3104             mContentWidth = w;
3105             mContentHeight = h;
3106             // If history Picture is drawn, don't update scroll. They will be
3107             // updated when we get out of that mode.
3108             if (!mDrawHistory) {
3109                 // repin our scroll, taking into account the new content size
3110                 updateScrollCoordinates(pinLocX(getScrollX()), pinLocY(getScrollY()));
3111                 if (!mScroller.isFinished()) {
3112                     // We are in the middle of a scroll.  Repin the final scroll
3113                     // position.
3114                     mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
3115                     mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
3116                 }
3117             }
3118             invalidate();
3119         }
3120         contentSizeChanged(updateLayout);
3121     }
3122 
3123     // Used to avoid sending many visible rect messages.
3124     private Rect mLastVisibleRectSent = new Rect();
3125     private Rect mLastGlobalRect = new Rect();
3126     private Rect mVisibleRect = new Rect();
3127     private Rect mGlobalVisibleRect = new Rect();
3128     private Point mScrollOffset = new Point();
3129 
sendOurVisibleRect()3130     Rect sendOurVisibleRect() {
3131         if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent;
3132         calcOurContentVisibleRect(mVisibleRect);
3133         // Rect.equals() checks for null input.
3134         if (!mVisibleRect.equals(mLastVisibleRectSent)) {
3135             if (!mBlockWebkitViewMessages) {
3136                 mScrollOffset.set(mVisibleRect.left, mVisibleRect.top);
3137                 mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET);
3138                 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
3139                         mSendScrollEvent ? 1 : 0, mScrollOffset);
3140             }
3141             mLastVisibleRectSent.set(mVisibleRect);
3142             mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
3143         }
3144         if (mWebView.getGlobalVisibleRect(mGlobalVisibleRect)
3145                 && !mGlobalVisibleRect.equals(mLastGlobalRect)) {
3146             if (DebugFlags.WEB_VIEW) {
3147                 Log.v(LOGTAG, "sendOurVisibleRect=(" + mGlobalVisibleRect.left + ","
3148                         + mGlobalVisibleRect.top + ",r=" + mGlobalVisibleRect.right + ",b="
3149                         + mGlobalVisibleRect.bottom);
3150             }
3151             // TODO: the global offset is only used by windowRect()
3152             // in ChromeClientAndroid ; other clients such as touch
3153             // and mouse events could return view + screen relative points.
3154             if (!mBlockWebkitViewMessages) {
3155                 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, mGlobalVisibleRect);
3156             }
3157             mLastGlobalRect.set(mGlobalVisibleRect);
3158         }
3159         return mVisibleRect;
3160     }
3161 
3162     private Point mGlobalVisibleOffset = new Point();
3163     // Sets r to be the visible rectangle of our webview in view coordinates
calcOurVisibleRect(Rect r)3164     private void calcOurVisibleRect(Rect r) {
3165         mWebView.getGlobalVisibleRect(r, mGlobalVisibleOffset);
3166         r.offset(-mGlobalVisibleOffset.x, -mGlobalVisibleOffset.y);
3167     }
3168 
3169     // Sets r to be our visible rectangle in content coordinates
calcOurContentVisibleRect(Rect r)3170     private void calcOurContentVisibleRect(Rect r) {
3171         calcOurVisibleRect(r);
3172         r.left = viewToContentX(r.left);
3173         // viewToContentY will remove the total height of the title bar.  Add
3174         // the visible height back in to account for the fact that if the title
3175         // bar is partially visible, the part of the visible rect which is
3176         // displaying our content is displaced by that amount.
3177         r.top = viewToContentY(r.top + getVisibleTitleHeightImpl());
3178         r.right = viewToContentX(r.right);
3179         r.bottom = viewToContentY(r.bottom);
3180     }
3181 
3182     private final Rect mTempContentVisibleRect = new Rect();
3183     // Sets r to be our visible rectangle in content coordinates. We use this
3184     // method on the native side to compute the position of the fixed layers.
3185     // Uses floating coordinates (necessary to correctly place elements when
3186     // the scale factor is not 1)
calcOurContentVisibleRectF(RectF r)3187     private void calcOurContentVisibleRectF(RectF r) {
3188         calcOurVisibleRect(mTempContentVisibleRect);
3189         viewToContentVisibleRect(r, mTempContentVisibleRect);
3190     }
3191 
3192     static class ViewSizeData {
3193         int mWidth;
3194         int mHeight;
3195         float mHeightWidthRatio;
3196         int mActualViewHeight;
3197         int mTextWrapWidth;
3198         int mAnchorX;
3199         int mAnchorY;
3200         float mScale;
3201         boolean mIgnoreHeight;
3202     }
3203 
3204     /**
3205      * Compute unzoomed width and height, and if they differ from the last
3206      * values we sent, send them to webkit (to be used as new viewport)
3207      *
3208      * @param force ensures that the message is sent to webkit even if the width
3209      * or height has not changed since the last message
3210      *
3211      * @return true if new values were sent
3212      */
sendViewSizeZoom(boolean force)3213     boolean sendViewSizeZoom(boolean force) {
3214         if (mBlockWebkitViewMessages) return false;
3215         if (mZoomManager.isPreventingWebkitUpdates()) return false;
3216 
3217         int viewWidth = getViewWidth();
3218         int newWidth = Math.round(viewWidth * mZoomManager.getInvScale());
3219         // This height could be fixed and be different from actual visible height.
3220         int viewHeight = getViewHeightWithTitle() - getTitleHeight();
3221         int newHeight = Math.round(viewHeight * mZoomManager.getInvScale());
3222         // Make the ratio more accurate than (newHeight / newWidth), since the
3223         // latter both are calculated and rounded.
3224         float heightWidthRatio = (float) viewHeight / viewWidth;
3225         /*
3226          * Because the native side may have already done a layout before the
3227          * View system was able to measure us, we have to send a height of 0 to
3228          * remove excess whitespace when we grow our width. This will trigger a
3229          * layout and a change in content size. This content size change will
3230          * mean that contentSizeChanged will either call this method directly or
3231          * indirectly from onSizeChanged.
3232          */
3233         if (newWidth > mLastWidthSent && mWrapContent) {
3234             newHeight = 0;
3235             heightWidthRatio = 0;
3236         }
3237         // Actual visible content height.
3238         int actualViewHeight = Math.round(getViewHeight() * mZoomManager.getInvScale());
3239         // Avoid sending another message if the dimensions have not changed.
3240         if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force ||
3241                 actualViewHeight != mLastActualHeightSent) {
3242             ViewSizeData data = new ViewSizeData();
3243             data.mWidth = newWidth;
3244             data.mHeight = newHeight;
3245             data.mHeightWidthRatio = heightWidthRatio;
3246             data.mActualViewHeight = actualViewHeight;
3247             data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale());
3248             data.mScale = mZoomManager.getScale();
3249             data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress()
3250                     && !mHeightCanMeasure;
3251             data.mAnchorX = mZoomManager.getDocumentAnchorX();
3252             data.mAnchorY = mZoomManager.getDocumentAnchorY();
3253             mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
3254             mLastWidthSent = newWidth;
3255             mLastHeightSent = newHeight;
3256             mLastActualHeightSent = actualViewHeight;
3257             mZoomManager.clearDocumentAnchor();
3258             return true;
3259         }
3260         return false;
3261     }
3262 
3263     /**
3264      * Update the double-tap zoom.
3265      */
updateDoubleTapZoom(int doubleTapZoom)3266     /* package */ void updateDoubleTapZoom(int doubleTapZoom) {
3267         mZoomManager.updateDoubleTapZoom(doubleTapZoom);
3268     }
3269 
computeRealHorizontalScrollRange()3270     private int computeRealHorizontalScrollRange() {
3271         if (mDrawHistory) {
3272             return mHistoryWidth;
3273         } else {
3274             // to avoid rounding error caused unnecessary scrollbar, use floor
3275             return (int) Math.floor(mContentWidth * mZoomManager.getScale());
3276         }
3277     }
3278 
3279     @Override
computeHorizontalScrollRange()3280     public int computeHorizontalScrollRange() {
3281         int range = computeRealHorizontalScrollRange();
3282 
3283         // Adjust reported range if overscrolled to compress the scroll bars
3284         final int scrollX = getScrollX();
3285         final int overscrollRight = computeMaxScrollX();
3286         if (scrollX < 0) {
3287             range -= scrollX;
3288         } else if (scrollX > overscrollRight) {
3289             range += scrollX - overscrollRight;
3290         }
3291 
3292         return range;
3293     }
3294 
3295     @Override
computeHorizontalScrollOffset()3296     public int computeHorizontalScrollOffset() {
3297         return Math.max(getScrollX(), 0);
3298     }
3299 
computeRealVerticalScrollRange()3300     private int computeRealVerticalScrollRange() {
3301         if (mDrawHistory) {
3302             return mHistoryHeight;
3303         } else {
3304             // to avoid rounding error caused unnecessary scrollbar, use floor
3305             return (int) Math.floor(mContentHeight * mZoomManager.getScale());
3306         }
3307     }
3308 
3309     @Override
computeVerticalScrollRange()3310     public int computeVerticalScrollRange() {
3311         int range = computeRealVerticalScrollRange();
3312 
3313         // Adjust reported range if overscrolled to compress the scroll bars
3314         final int scrollY = getScrollY();
3315         final int overscrollBottom = computeMaxScrollY();
3316         if (scrollY < 0) {
3317             range -= scrollY;
3318         } else if (scrollY > overscrollBottom) {
3319             range += scrollY - overscrollBottom;
3320         }
3321 
3322         return range;
3323     }
3324 
3325     @Override
computeVerticalScrollOffset()3326     public int computeVerticalScrollOffset() {
3327         return Math.max(getScrollY() - getTitleHeight(), 0);
3328     }
3329 
3330     @Override
computeVerticalScrollExtent()3331     public int computeVerticalScrollExtent() {
3332         return getViewHeight();
3333     }
3334 
3335     @Override
onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar, int l, int t, int r, int b)3336     public void onDrawVerticalScrollBar(Canvas canvas,
3337                                            Drawable scrollBar,
3338                                            int l, int t, int r, int b) {
3339         if (getScrollY() < 0) {
3340             t -= getScrollY();
3341         }
3342         scrollBar.setBounds(l, t + getVisibleTitleHeightImpl(), r, b);
3343         scrollBar.draw(canvas);
3344     }
3345 
3346     @Override
onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)3347     public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
3348             boolean clampedY) {
3349         // Special-case layer scrolling so that we do not trigger normal scroll
3350         // updating.
3351         if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
3352             scrollEditText(scrollX, scrollY);
3353             return;
3354         }
3355         if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
3356             scrollLayerTo(scrollX, scrollY);
3357             animateHandles();
3358             return;
3359         }
3360         mInOverScrollMode = false;
3361         int maxX = computeMaxScrollX();
3362         int maxY = computeMaxScrollY();
3363         if (maxX == 0) {
3364             // do not over scroll x if the page just fits the screen
3365             scrollX = pinLocX(scrollX);
3366         } else if (scrollX < 0 || scrollX > maxX) {
3367             mInOverScrollMode = true;
3368         }
3369         if (scrollY < 0 || scrollY > maxY) {
3370             mInOverScrollMode = true;
3371         }
3372 
3373         int oldX = getScrollX();
3374         int oldY = getScrollY();
3375 
3376         mWebViewPrivate.super_scrollTo(scrollX, scrollY);
3377 
3378         animateHandles();
3379 
3380         if (mOverScrollGlow != null) {
3381             mOverScrollGlow.pullGlow(getScrollX(), getScrollY(), oldX, oldY, maxX, maxY);
3382         }
3383     }
3384 
3385     /**
3386      * See {@link WebView#getUrl()}
3387      */
3388     @Override
getUrl()3389     public String getUrl() {
3390         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
3391         return h != null ? h.getUrl() : null;
3392     }
3393 
3394     /**
3395      * See {@link WebView#getOriginalUrl()}
3396      */
3397     @Override
getOriginalUrl()3398     public String getOriginalUrl() {
3399         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
3400         return h != null ? h.getOriginalUrl() : null;
3401     }
3402 
3403     /**
3404      * See {@link WebView#getTitle()}
3405      */
3406     @Override
getTitle()3407     public String getTitle() {
3408         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
3409         return h != null ? h.getTitle() : null;
3410     }
3411 
3412     /**
3413      * See {@link WebView#getFavicon()}
3414      */
3415     @Override
getFavicon()3416     public Bitmap getFavicon() {
3417         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
3418         return h != null ? h.getFavicon() : null;
3419     }
3420 
3421     /**
3422      * See {@link WebView#getTouchIconUrl()}
3423      */
3424     @Override
getTouchIconUrl()3425     public String getTouchIconUrl() {
3426         WebHistoryItemClassic h = mCallbackProxy.getBackForwardList().getCurrentItem();
3427         return h != null ? h.getTouchIconUrl() : null;
3428     }
3429 
3430     /**
3431      * See {@link WebView#getProgress()}
3432      */
3433     @Override
getProgress()3434     public int getProgress() {
3435         return mCallbackProxy.getProgress();
3436     }
3437 
3438     /**
3439      * See {@link WebView#getContentHeight()}
3440      */
3441     @Override
getContentHeight()3442     public int getContentHeight() {
3443         return mContentHeight;
3444     }
3445 
3446     /**
3447      * See {@link WebView#getContentWidth()}
3448      */
3449     @Override
getContentWidth()3450     public int getContentWidth() {
3451         return mContentWidth;
3452     }
3453 
getPageBackgroundColor()3454     public int getPageBackgroundColor() {
3455         if (mNativeClass == 0) return Color.WHITE;
3456         return nativeGetBackgroundColor(mNativeClass);
3457     }
3458 
3459     /**
3460      * See {@link WebView#pauseTimers()}
3461      */
3462     @Override
pauseTimers()3463     public void pauseTimers() {
3464         mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
3465     }
3466 
3467     /**
3468      * See {@link WebView#resumeTimers()}
3469      */
3470     @Override
resumeTimers()3471     public void resumeTimers() {
3472         mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
3473     }
3474 
3475     /**
3476      * See {@link WebView#onPause()}
3477      */
3478     @Override
onPause()3479     public void onPause() {
3480         if (!mIsPaused) {
3481             mIsPaused = true;
3482             mWebViewCore.sendMessage(EventHub.ON_PAUSE);
3483             // We want to pause the current playing video when switching out
3484             // from the current WebView/tab.
3485             if (mHTML5VideoViewProxy != null) {
3486                 mHTML5VideoViewProxy.pauseAndDispatch();
3487             }
3488             if (mNativeClass != 0) {
3489                 nativeSetPauseDrawing(mNativeClass, true);
3490             }
3491 
3492             cancelDialogs();
3493             WebCoreThreadWatchdog.pause();
3494         }
3495     }
3496 
3497     @Override
onWindowVisibilityChanged(int visibility)3498     public void onWindowVisibilityChanged(int visibility) {
3499         updateDrawingState();
3500     }
3501 
updateDrawingState()3502     void updateDrawingState() {
3503         if (mNativeClass == 0 || mIsPaused) return;
3504         if (mWebView.getWindowVisibility() != View.VISIBLE) {
3505             nativeSetPauseDrawing(mNativeClass, true);
3506         } else if (mWebView.getVisibility() != View.VISIBLE) {
3507             nativeSetPauseDrawing(mNativeClass, true);
3508         } else {
3509             nativeSetPauseDrawing(mNativeClass, false);
3510         }
3511     }
3512 
3513     /**
3514      * See {@link WebView#onResume()}
3515      */
3516     @Override
onResume()3517     public void onResume() {
3518         if (mIsPaused) {
3519             mIsPaused = false;
3520             mWebViewCore.sendMessage(EventHub.ON_RESUME);
3521             if (mNativeClass != 0) {
3522                 nativeSetPauseDrawing(mNativeClass, false);
3523             }
3524         }
3525         // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need
3526         // to ensure that the Watchdog thread is running for the new WebView, so call
3527         // it outside the if block above.
3528         WebCoreThreadWatchdog.resume();
3529     }
3530 
3531     /**
3532      * See {@link WebView#isPaused()}
3533      */
3534     @Override
isPaused()3535     public boolean isPaused() {
3536         return mIsPaused;
3537     }
3538 
3539     /**
3540      * See {@link WebView#freeMemory()}
3541      */
3542     @Override
freeMemory()3543     public void freeMemory() {
3544         mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
3545     }
3546 
3547     /**
3548      * See {@link WebView#clearCache(boolean)}
3549      */
3550     @Override
clearCache(boolean includeDiskFiles)3551     public void clearCache(boolean includeDiskFiles) {
3552         // Note: this really needs to be a static method as it clears cache for all
3553         // WebView. But we need mWebViewCore to send message to WebCore thread, so
3554         // we can't make this static.
3555         mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
3556                 includeDiskFiles ? 1 : 0, 0);
3557     }
3558 
3559     /**
3560      * See {@link WebView#clearFormData()}
3561      */
3562     @Override
clearFormData()3563     public void clearFormData() {
3564         if (mAutoCompletePopup != null) {
3565             mAutoCompletePopup.clearAdapter();
3566         }
3567     }
3568 
3569     /**
3570      * See {@link WebView#clearHistory()}
3571      */
3572     @Override
clearHistory()3573     public void clearHistory() {
3574         mCallbackProxy.getBackForwardList().setClearPending();
3575         mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
3576     }
3577 
3578     /**
3579      * See {@link WebView#clearSslPreferences()}
3580      */
3581     @Override
clearSslPreferences()3582     public void clearSslPreferences() {
3583         mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
3584     }
3585 
3586     /**
3587      * See {@link WebView#copyBackForwardList()}
3588      */
3589     @Override
copyBackForwardList()3590     public WebBackForwardListClassic copyBackForwardList() {
3591         return mCallbackProxy.getBackForwardList().clone();
3592     }
3593 
3594     /**
3595      * See {@link WebView#setFindListener(WebView.FindListener)}.
3596      * @hide
3597      */
3598      @Override
setFindListener(WebView.FindListener listener)3599     public void setFindListener(WebView.FindListener listener) {
3600          mFindListener = listener;
3601      }
3602 
3603     /**
3604      * See {@link WebView#findNext(boolean)}
3605      */
3606     @Override
findNext(boolean forward)3607     public void findNext(boolean forward) {
3608         if (0 == mNativeClass) return; // client isn't initialized
3609         if (mFindRequest != null) {
3610             mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0, mFindRequest);
3611         }
3612     }
3613 
3614     /**
3615      * See {@link WebView#findAll(String)}
3616      */
3617     @Override
findAll(String find)3618     public int findAll(String find) {
3619         return findAllBody(find, false);
3620     }
3621 
3622     @Override
findAllAsync(String find)3623     public void findAllAsync(String find) {
3624         findAllBody(find, true);
3625     }
3626 
findAllBody(String find, boolean isAsync)3627     private int findAllBody(String find, boolean isAsync) {
3628         if (0 == mNativeClass) return 0; // client isn't initialized
3629         mFindRequest = null;
3630         if (find == null) return 0;
3631         mWebViewCore.removeMessages(EventHub.FIND_ALL);
3632         mFindRequest = new WebViewCore.FindAllRequest(find);
3633         if (isAsync) {
3634             mWebViewCore.sendMessage(EventHub.FIND_ALL, mFindRequest);
3635             return 0; // no need to wait for response
3636         }
3637         synchronized(mFindRequest) {
3638             try {
3639                 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL, mFindRequest);
3640                 while (mFindRequest.mMatchCount == -1) {
3641                     mFindRequest.wait();
3642                 }
3643             }
3644             catch (InterruptedException e) {
3645                 return 0;
3646             }
3647             return mFindRequest.mMatchCount;
3648         }
3649     }
3650 
3651     /**
3652      * Start an ActionMode for finding text in this WebView.  Only works if this
3653      *              WebView is attached to the view system.
3654      * @param text If non-null, will be the initial text to search for.
3655      *             Otherwise, the last String searched for in this WebView will
3656      *             be used to start.
3657      * @param showIme If true, show the IME, assuming the user will begin typing.
3658      *             If false and text is non-null, perform a find all.
3659      * @return boolean True if the find dialog is shown, false otherwise.
3660      */
3661     @Override
showFindDialog(String text, boolean showIme)3662     public boolean showFindDialog(String text, boolean showIme) {
3663         FindActionModeCallback callback = new FindActionModeCallback(mContext);
3664         if (mWebView.getParent() == null || mWebView.startActionMode(callback) == null) {
3665             // Could not start the action mode, so end Find on page
3666             return false;
3667         }
3668         mCachedOverlappingActionModeHeight = -1;
3669         mFindCallback = callback;
3670         setFindIsUp(true);
3671         mFindCallback.setWebView(this);
3672         if (showIme) {
3673             mFindCallback.showSoftInput();
3674         } else if (text != null) {
3675             mFindCallback.setText(text);
3676             mFindCallback.findAll();
3677             return true;
3678         }
3679         if (text == null) {
3680             text = mFindRequest == null ? null : mFindRequest.mSearchText;
3681         }
3682         if (text != null) {
3683             mFindCallback.setText(text);
3684             mFindCallback.findAll();
3685         }
3686         return true;
3687     }
3688 
3689     /**
3690      * Keep track of the find callback so that we can remove its titlebar if
3691      * necessary.
3692      */
3693     private FindActionModeCallback mFindCallback;
3694 
3695     /**
3696      * Toggle whether the find dialog is showing, for both native and Java.
3697      */
setFindIsUp(boolean isUp)3698     private void setFindIsUp(boolean isUp) {
3699         mFindIsUp = isUp;
3700     }
3701 
3702     // Used to know whether the find dialog is open.  Affects whether
3703     // or not we draw the highlights for matches.
3704     private boolean mFindIsUp;
3705 
3706     // Keep track of the last find request sent.
3707     private WebViewCore.FindAllRequest mFindRequest = null;
3708 
3709     /**
3710      * Return the first substring consisting of the address of a physical
3711      * location. Currently, only addresses in the United States are detected,
3712      * and consist of:
3713      * - a house number
3714      * - a street name
3715      * - a street type (Road, Circle, etc), either spelled out or abbreviated
3716      * - a city name
3717      * - a state or territory, either spelled out or two-letter abbr.
3718      * - an optional 5 digit or 9 digit zip code.
3719      *
3720      * All names must be correctly capitalized, and the zip code, if present,
3721      * must be valid for the state. The street type must be a standard USPS
3722      * spelling or abbreviation. The state or territory must also be spelled
3723      * or abbreviated using USPS standards. The house number may not exceed
3724      * five digits.
3725      * @param addr The string to search for addresses.
3726      *
3727      * @return the address, or if no address is found, return null.
3728      */
findAddress(String addr)3729     public static String findAddress(String addr) {
3730         return findAddress(addr, false);
3731     }
3732 
3733     /**
3734      * Return the first substring consisting of the address of a physical
3735      * location. Currently, only addresses in the United States are detected,
3736      * and consist of:
3737      * - a house number
3738      * - a street name
3739      * - a street type (Road, Circle, etc), either spelled out or abbreviated
3740      * - a city name
3741      * - a state or territory, either spelled out or two-letter abbr.
3742      * - an optional 5 digit or 9 digit zip code.
3743      *
3744      * Names are optionally capitalized, and the zip code, if present,
3745      * must be valid for the state. The street type must be a standard USPS
3746      * spelling or abbreviation. The state or territory must also be spelled
3747      * or abbreviated using USPS standards. The house number may not exceed
3748      * five digits.
3749      * @param addr The string to search for addresses.
3750      * @param caseInsensitive addr Set to true to make search ignore case.
3751      *
3752      * @return the address, or if no address is found, return null.
3753      */
findAddress(String addr, boolean caseInsensitive)3754     public static String findAddress(String addr, boolean caseInsensitive) {
3755         return WebViewCore.nativeFindAddress(addr, caseInsensitive);
3756     }
3757 
3758     /**
3759      * See {@link WebView#clearMatches()}
3760      */
3761     @Override
clearMatches()3762     public void clearMatches() {
3763         if (mNativeClass == 0)
3764             return;
3765         mWebViewCore.removeMessages(EventHub.FIND_ALL);
3766         mWebViewCore.sendMessage(EventHub.FIND_ALL, null);
3767     }
3768 
3769 
3770     /**
3771      * Called when the find ActionMode ends.
3772      */
notifyFindDialogDismissed()3773     void notifyFindDialogDismissed() {
3774         mFindCallback = null;
3775         mCachedOverlappingActionModeHeight = -1;
3776         if (mWebViewCore == null) {
3777             return;
3778         }
3779         clearMatches();
3780         setFindIsUp(false);
3781         // Now that the dialog has been removed, ensure that we scroll to a
3782         // location that is not beyond the end of the page.
3783         pinScrollTo(getScrollX(), getScrollY(), false, 0);
3784         invalidate();
3785     }
3786 
3787     /**
3788      * See {@link WebView#documentHasImages(Message)}
3789      */
3790     @Override
documentHasImages(Message response)3791     public void documentHasImages(Message response) {
3792         if (response == null) {
3793             return;
3794         }
3795         mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
3796     }
3797 
3798     /**
3799      * Request the scroller to abort any ongoing animation
3800      */
stopScroll()3801     public void stopScroll() {
3802         mScroller.forceFinished(true);
3803         mLastVelocity = 0;
3804     }
3805 
3806     @Override
computeScroll()3807     public void computeScroll() {
3808         if (mScroller.computeScrollOffset()) {
3809             int oldX = getScrollX();
3810             int oldY = getScrollY();
3811             int x = mScroller.getCurrX();
3812             int y = mScroller.getCurrY();
3813             invalidate();  // So we draw again
3814 
3815             if (!mScroller.isFinished()) {
3816                 int rangeX = computeMaxScrollX();
3817                 int rangeY = computeMaxScrollY();
3818                 int overflingDistance = mOverflingDistance;
3819 
3820                 // Use the layer's scroll data if needed.
3821                 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
3822                     oldX = mScrollingLayerRect.left;
3823                     oldY = mScrollingLayerRect.top;
3824                     rangeX = mScrollingLayerRect.right;
3825                     rangeY = mScrollingLayerRect.bottom;
3826                     // No overscrolling for layers.
3827                     overflingDistance = 0;
3828                 } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
3829                     oldX = getTextScrollX();
3830                     oldY = getTextScrollY();
3831                     rangeX = getMaxTextScrollX();
3832                     rangeY = getMaxTextScrollY();
3833                     overflingDistance = 0;
3834                 }
3835 
3836                 mWebViewPrivate.overScrollBy(x - oldX, y - oldY, oldX, oldY,
3837                         rangeX, rangeY,
3838                         overflingDistance, overflingDistance, false);
3839 
3840                 if (mOverScrollGlow != null) {
3841                     mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
3842                 }
3843             } else {
3844                 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
3845                     // Update the layer position instead of WebView.
3846                     scrollLayerTo(x, y);
3847                 } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
3848                     scrollEditText(x, y);
3849                 } else {
3850                     setScrollXRaw(x);
3851                     setScrollYRaw(y);
3852                 }
3853                 abortAnimation();
3854                 nativeSetIsScrolling(false);
3855                 if (!mBlockWebkitViewMessages) {
3856                     WebViewCore.resumePriority();
3857                     if (!mSelectingText) {
3858                         WebViewCore.resumeUpdatePicture(mWebViewCore);
3859                     }
3860                 }
3861                 if (oldX != getScrollX() || oldY != getScrollY()) {
3862                     sendOurVisibleRect();
3863                 }
3864             }
3865         } else {
3866             mWebViewPrivate.super_computeScroll();
3867         }
3868     }
3869 
scrollLayerTo(int x, int y)3870     private void scrollLayerTo(int x, int y) {
3871         int dx = mScrollingLayerRect.left - x;
3872         int dy = mScrollingLayerRect.top - y;
3873         if ((dx == 0 && dy == 0) || mNativeClass == 0) {
3874             return;
3875         }
3876         if (mSelectingText) {
3877             if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) {
3878                 mSelectCursorBase.offset(dx, dy);
3879                 mSelectCursorBaseTextQuad.offset(dx, dy);
3880             }
3881             if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) {
3882                 mSelectCursorExtent.offset(dx, dy);
3883                 mSelectCursorExtentTextQuad.offset(dx, dy);
3884             }
3885         }
3886         if (mAutoCompletePopup != null &&
3887                 mCurrentScrollingLayerId == mEditTextLayerId) {
3888             mEditTextContentBounds.offset(dx, dy);
3889             mAutoCompletePopup.resetRect();
3890         }
3891         nativeScrollLayer(mNativeClass, mCurrentScrollingLayerId, x, y);
3892         mScrollingLayerRect.left = x;
3893         mScrollingLayerRect.top = y;
3894         mWebViewCore.sendMessage(WebViewCore.EventHub.SCROLL_LAYER, mCurrentScrollingLayerId,
3895                 mScrollingLayerRect);
3896         mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());
3897         invalidate();
3898     }
3899 
computeDuration(int dx, int dy)3900     private static int computeDuration(int dx, int dy) {
3901         int distance = Math.max(Math.abs(dx), Math.abs(dy));
3902         int duration = distance * 1000 / STD_SPEED;
3903         return Math.min(duration, MAX_DURATION);
3904     }
3905 
3906     // helper to pin the scrollBy parameters (already in view coordinates)
3907     // returns true if the scroll was changed
pinScrollBy(int dx, int dy, boolean animate, int animationDuration)3908     private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
3909         return pinScrollTo(getScrollX() + dx, getScrollY() + dy, animate, animationDuration);
3910     }
3911     // helper to pin the scrollTo parameters (already in view coordinates)
3912     // returns true if the scroll was changed
pinScrollTo(int x, int y, boolean animate, int animationDuration)3913     private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
3914         abortAnimation();
3915         x = pinLocX(x);
3916         y = pinLocY(y);
3917         int dx = x - getScrollX();
3918         int dy = y - getScrollY();
3919 
3920         if ((dx | dy) == 0) {
3921             return false;
3922         }
3923         if (animate) {
3924             //        Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
3925             mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
3926                     animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
3927             invalidate();
3928         } else {
3929             mWebView.scrollTo(x, y);
3930         }
3931         return true;
3932     }
3933 
3934     // Scale from content to view coordinates, and pin.
3935     // Also called by jni webview.cpp
setContentScrollBy(int cx, int cy, boolean animate)3936     private boolean setContentScrollBy(int cx, int cy, boolean animate) {
3937         if (mDrawHistory) {
3938             // disallow WebView to change the scroll position as History Picture
3939             // is used in the view system.
3940             // TODO: as we switchOutDrawHistory when trackball or navigation
3941             // keys are hit, this should be safe. Right?
3942             return false;
3943         }
3944         cx = contentToViewDimension(cx);
3945         cy = contentToViewDimension(cy);
3946         if (mHeightCanMeasure) {
3947             // move our visible rect according to scroll request
3948             if (cy != 0) {
3949                 Rect tempRect = new Rect();
3950                 calcOurVisibleRect(tempRect);
3951                 tempRect.offset(cx, cy);
3952                 mWebView.requestRectangleOnScreen(tempRect);
3953             }
3954             // FIXME: We scroll horizontally no matter what because currently
3955             // ScrollView and ListView will not scroll horizontally.
3956             // FIXME: Why do we only scroll horizontally if there is no
3957             // vertical scroll?
3958 //                Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
3959             return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
3960         } else {
3961             return pinScrollBy(cx, cy, animate, 0);
3962         }
3963     }
3964 
3965     /**
3966      * Called by CallbackProxy when the page starts loading.
3967      * @param url The URL of the page which has started loading.
3968      */
onPageStarted(String url)3969     /* package */ void onPageStarted(String url) {
3970         // every time we start a new page, we want to reset the
3971         // WebView certificate:  if the new site is secure, we
3972         // will reload it and get a new certificate set;
3973         // if the new site is not secure, the certificate must be
3974         // null, and that will be the case
3975         mWebView.setCertificate(null);
3976 
3977         if (isAccessibilityInjectionEnabled()) {
3978             getAccessibilityInjector().onPageStarted(url);
3979         }
3980 
3981         // Don't start out editing.
3982         mIsEditingText = false;
3983     }
3984 
3985     /**
3986      * Called by CallbackProxy when the page finishes loading.
3987      * @param url The URL of the page which has finished loading.
3988      */
onPageFinished(String url)3989     /* package */ void onPageFinished(String url) {
3990         mZoomManager.onPageFinished(url);
3991 
3992         if (isAccessibilityInjectionEnabled()) {
3993             getAccessibilityInjector().onPageFinished(url);
3994         }
3995     }
3996 
3997     // scale from content to view coordinates, and pin
contentScrollTo(int cx, int cy, boolean animate)3998     private void contentScrollTo(int cx, int cy, boolean animate) {
3999         if (mDrawHistory) {
4000             // disallow WebView to change the scroll position as History Picture
4001             // is used in the view system.
4002             return;
4003         }
4004         int vx = contentToViewX(cx);
4005         int vy = contentToViewY(cy);
4006         pinScrollTo(vx, vy, animate, 0);
4007     }
4008 
4009     /**
4010      * These are from webkit, and are in content coordinate system (unzoomed)
4011      */
contentSizeChanged(boolean updateLayout)4012     private void contentSizeChanged(boolean updateLayout) {
4013         // suppress 0,0 since we usually see real dimensions soon after
4014         // this avoids drawing the prev content in a funny place. If we find a
4015         // way to consolidate these notifications, this check may become
4016         // obsolete
4017         if ((mContentWidth | mContentHeight) == 0) {
4018             return;
4019         }
4020 
4021         if (mHeightCanMeasure) {
4022             if (mWebView.getMeasuredHeight() != contentToViewDimension(mContentHeight)
4023                     || updateLayout) {
4024                 mWebView.requestLayout();
4025             }
4026         } else if (mWidthCanMeasure) {
4027             if (mWebView.getMeasuredWidth() != contentToViewDimension(mContentWidth)
4028                     || updateLayout) {
4029                 mWebView.requestLayout();
4030             }
4031         } else {
4032             // If we don't request a layout, try to send our view size to the
4033             // native side to ensure that WebCore has the correct dimensions.
4034             sendViewSizeZoom(false);
4035         }
4036     }
4037 
4038     /**
4039      * See {@link WebView#setWebViewClient(WebViewClient)}
4040      */
4041     @Override
setWebViewClient(WebViewClient client)4042     public void setWebViewClient(WebViewClient client) {
4043         mCallbackProxy.setWebViewClient(client);
4044     }
4045 
4046     /**
4047      * Gets the WebViewClient
4048      * @return the current WebViewClient instance.
4049      *
4050      * This is an implementation detail.
4051      */
getWebViewClient()4052     public WebViewClient getWebViewClient() {
4053         return mCallbackProxy.getWebViewClient();
4054     }
4055 
4056     /**
4057      * See {@link WebView#setDownloadListener(DownloadListener)}
4058      */
4059     @Override
setDownloadListener(DownloadListener listener)4060     public void setDownloadListener(DownloadListener listener) {
4061         mCallbackProxy.setDownloadListener(listener);
4062     }
4063 
4064     /**
4065      * See {@link WebView#setWebChromeClient(WebChromeClient)}
4066      */
4067     @Override
setWebChromeClient(WebChromeClient client)4068     public void setWebChromeClient(WebChromeClient client) {
4069         mCallbackProxy.setWebChromeClient(client);
4070     }
4071 
4072     /**
4073      * Gets the chrome handler.
4074      * @return the current WebChromeClient instance.
4075      *
4076      * This is an implementation detail.
4077      */
getWebChromeClient()4078     public WebChromeClient getWebChromeClient() {
4079         return mCallbackProxy.getWebChromeClient();
4080     }
4081 
4082     /**
4083      * Set the back/forward list client. This is an implementation of
4084      * WebBackForwardListClient for handling new items and changes in the
4085      * history index.
4086      * @param client An implementation of WebBackForwardListClient.
4087      */
setWebBackForwardListClient(WebBackForwardListClient client)4088     public void setWebBackForwardListClient(WebBackForwardListClient client) {
4089         mCallbackProxy.setWebBackForwardListClient(client);
4090     }
4091 
4092     /**
4093      * Gets the WebBackForwardListClient.
4094      */
getWebBackForwardListClient()4095     public WebBackForwardListClient getWebBackForwardListClient() {
4096         return mCallbackProxy.getWebBackForwardListClient();
4097     }
4098 
4099     /**
4100      * See {@link WebView#setPictureListener(PictureListener)}
4101      */
4102     @Override
4103     @Deprecated
setPictureListener(PictureListener listener)4104     public void setPictureListener(PictureListener listener) {
4105         mPictureListener = listener;
4106     }
4107 
4108     /* FIXME: Debug only! Remove for SDK! */
externalRepresentation(Message callback)4109     public void externalRepresentation(Message callback) {
4110         mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
4111     }
4112 
4113     /* FIXME: Debug only! Remove for SDK! */
documentAsText(Message callback)4114     public void documentAsText(Message callback) {
4115         mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
4116     }
4117 
4118     /**
4119      * See {@link WebView#addJavascriptInterface(Object, String)}
4120      */
4121     @Override
addJavascriptInterface(Object object, String name)4122     public void addJavascriptInterface(Object object, String name) {
4123 
4124         if (object == null) {
4125             return;
4126         }
4127         WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
4128 
4129         arg.mObject = object;
4130         arg.mInterfaceName = name;
4131 
4132         // starting with JELLY_BEAN_MR1, annotations are mandatory for enabling access to
4133         // methods that are accessible from JS.
4134         if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
4135             arg.mRequireAnnotation = true;
4136         } else {
4137             arg.mRequireAnnotation = false;
4138         }
4139         mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
4140     }
4141 
4142     /**
4143      * See {@link WebView#removeJavascriptInterface(String)}
4144      */
4145     @Override
removeJavascriptInterface(String interfaceName)4146     public void removeJavascriptInterface(String interfaceName) {
4147         if (mWebViewCore != null) {
4148             WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
4149             arg.mInterfaceName = interfaceName;
4150             mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
4151         }
4152     }
4153 
4154     /**
4155      * See {@link WebView#getSettings()}
4156      * Note this returns WebSettingsClassic, a sub-class of WebSettings, which can be used
4157      * to access extension APIs.
4158      */
4159     @Override
getSettings()4160     public WebSettingsClassic getSettings() {
4161         return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
4162     }
4163 
4164     /**
4165      * See {@link WebView#getPluginList()}
4166      */
4167     @Deprecated
getPluginList()4168     public static synchronized PluginList getPluginList() {
4169         return new PluginList();
4170     }
4171 
4172     /**
4173      * See {@link WebView#refreshPlugins(boolean)}
4174      */
4175     @Deprecated
refreshPlugins(boolean reloadOpenPages)4176     public void refreshPlugins(boolean reloadOpenPages) {
4177     }
4178 
4179     //-------------------------------------------------------------------------
4180     // Override View methods
4181     //-------------------------------------------------------------------------
4182 
4183     @Override
finalize()4184     protected void finalize() throws Throwable {
4185         try {
4186             destroy();
4187         } finally {
4188             super.finalize();
4189         }
4190     }
4191 
drawContent(Canvas canvas)4192     private void drawContent(Canvas canvas) {
4193         if (mDrawHistory) {
4194             canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
4195             canvas.drawPicture(mHistoryPicture);
4196             return;
4197         }
4198         if (mNativeClass == 0) return;
4199 
4200         boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
4201         boolean animateScroll = ((!mScroller.isFinished()
4202                 || mVelocityTracker != null)
4203                 && (mTouchMode != TOUCH_DRAG_MODE ||
4204                 mHeldMotionless != MOTIONLESS_TRUE));
4205         if (mTouchMode == TOUCH_DRAG_MODE) {
4206             if (mHeldMotionless == MOTIONLESS_PENDING) {
4207                 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
4208                 mHeldMotionless = MOTIONLESS_FALSE;
4209             }
4210             if (mHeldMotionless == MOTIONLESS_FALSE) {
4211                 mPrivateHandler.sendMessageDelayed(mPrivateHandler
4212                         .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
4213                 mHeldMotionless = MOTIONLESS_PENDING;
4214             }
4215         }
4216         int saveCount = canvas.save();
4217         if (animateZoom) {
4218             mZoomManager.animateZoom(canvas);
4219         } else if (!canvas.isHardwareAccelerated()) {
4220             canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
4221         }
4222 
4223         boolean UIAnimationsRunning = false;
4224         // Currently for each draw we compute the animation values;
4225         // We may in the future decide to do that independently.
4226         if (mNativeClass != 0 && !canvas.isHardwareAccelerated()
4227                 && nativeEvaluateLayersAnimations(mNativeClass)) {
4228             UIAnimationsRunning = true;
4229             // If we have unfinished (or unstarted) animations,
4230             // we ask for a repaint. We only need to do this in software
4231             // rendering (with hardware rendering we already have a different
4232             // method of requesting a repaint)
4233             mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
4234             invalidate();
4235         }
4236 
4237         // decide which adornments to draw
4238         int extras = DRAW_EXTRAS_NONE;
4239         if (!mFindIsUp && mShowTextSelectionExtra) {
4240             extras = DRAW_EXTRAS_SELECTION;
4241         }
4242 
4243         calcOurContentVisibleRectF(mVisibleContentRect);
4244         if (canvas.isHardwareAccelerated()) {
4245             Rect invScreenRect = mIsWebViewVisible ? mInvScreenRect : null;
4246             Rect screenRect = mIsWebViewVisible ? mScreenRect : null;
4247 
4248             int functor = nativeCreateDrawGLFunction(mNativeClass, invScreenRect,
4249                     screenRect, mVisibleContentRect, getScale(), extras);
4250             ((HardwareCanvas) canvas).callDrawGLFunction(functor);
4251             if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) {
4252                 mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled();
4253                 nativeUseHardwareAccelSkia(mHardwareAccelSkia);
4254             }
4255 
4256         } else {
4257             DrawFilter df = null;
4258             if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
4259                 df = mZoomFilter;
4260             } else if (animateScroll) {
4261                 df = mScrollFilter;
4262             }
4263             canvas.setDrawFilter(df);
4264             nativeDraw(canvas, mVisibleContentRect, mBackgroundColor, extras);
4265             canvas.setDrawFilter(null);
4266         }
4267 
4268         canvas.restoreToCount(saveCount);
4269         drawTextSelectionHandles(canvas);
4270 
4271         if (extras == DRAW_EXTRAS_CURSOR_RING) {
4272             if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
4273                 mTouchMode = TOUCH_SHORTPRESS_MODE;
4274             }
4275         }
4276     }
4277 
4278     /**
4279      * Draw the background when beyond bounds
4280      * @param canvas Canvas to draw into
4281      */
drawOverScrollBackground(Canvas canvas)4282     private void drawOverScrollBackground(Canvas canvas) {
4283         if (mOverScrollBackground == null) {
4284             mOverScrollBackground = new Paint();
4285             Bitmap bm = BitmapFactory.decodeResource(
4286                     mContext.getResources(),
4287                     com.android.internal.R.drawable.status_bar_background);
4288             mOverScrollBackground.setShader(new BitmapShader(bm,
4289                     Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
4290             mOverScrollBorder = new Paint();
4291             mOverScrollBorder.setStyle(Paint.Style.STROKE);
4292             mOverScrollBorder.setStrokeWidth(0);
4293             mOverScrollBorder.setColor(0xffbbbbbb);
4294         }
4295 
4296         int top = 0;
4297         int right = computeRealHorizontalScrollRange();
4298         int bottom = top + computeRealVerticalScrollRange();
4299         // first draw the background and anchor to the top of the view
4300         canvas.save();
4301         canvas.translate(getScrollX(), getScrollY());
4302         canvas.clipRect(-getScrollX(), top - getScrollY(), right - getScrollX(), bottom
4303                 - getScrollY(), Region.Op.DIFFERENCE);
4304         canvas.drawPaint(mOverScrollBackground);
4305         canvas.restore();
4306         // then draw the border
4307         canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
4308         // next clip the region for the content
4309         canvas.clipRect(0, top, right, bottom);
4310     }
4311 
4312     @Override
onDraw(Canvas canvas)4313     public void onDraw(Canvas canvas) {
4314         if (inFullScreenMode()) {
4315             return; // no need to draw anything if we aren't visible.
4316         }
4317         // if mNativeClass is 0, the WebView is either destroyed or not
4318         // initialized. In either case, just draw the background color and return
4319         if (mNativeClass == 0) {
4320             canvas.drawColor(mBackgroundColor);
4321             return;
4322         }
4323 
4324         // if both mContentWidth and mContentHeight are 0, it means there is no
4325         // valid Picture passed to WebView yet. This can happen when WebView
4326         // just starts. Draw the background and return.
4327         if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) {
4328             canvas.drawColor(mBackgroundColor);
4329             return;
4330         }
4331 
4332         if (canvas.isHardwareAccelerated()) {
4333             mZoomManager.setHardwareAccelerated();
4334         } else {
4335             mWebViewCore.resumeWebKitDraw();
4336         }
4337 
4338         int saveCount = canvas.save();
4339         if (mInOverScrollMode && !getSettings()
4340                 .getUseWebViewBackgroundForOverscrollBackground()) {
4341             drawOverScrollBackground(canvas);
4342         }
4343 
4344         canvas.translate(0, getTitleHeight());
4345         drawContent(canvas);
4346         canvas.restoreToCount(saveCount);
4347 
4348         if (AUTO_REDRAW_HACK && mAutoRedraw) {
4349             invalidate();
4350         }
4351         mWebViewCore.signalRepaintDone();
4352 
4353         if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) {
4354             invalidate();
4355         }
4356 
4357         if (mFocusTransition != null) {
4358             mFocusTransition.draw(canvas);
4359         } else if (shouldDrawHighlightRect()) {
4360             RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
4361             Rect r = new Rect();
4362             while (iter.next(r)) {
4363                 canvas.drawRect(r, mTouchHightlightPaint);
4364             }
4365         }
4366         if (DEBUG_TOUCH_HIGHLIGHT) {
4367             if (getSettings().getNavDump()) {
4368                 if ((mTouchHighlightX | mTouchHighlightY) != 0) {
4369                     if (mTouchCrossHairColor == null) {
4370                         mTouchCrossHairColor = new Paint();
4371                         mTouchCrossHairColor.setColor(Color.RED);
4372                     }
4373                     canvas.drawLine(mTouchHighlightX - mNavSlop,
4374                             mTouchHighlightY - mNavSlop, mTouchHighlightX
4375                                     + mNavSlop + 1, mTouchHighlightY + mNavSlop
4376                                     + 1, mTouchCrossHairColor);
4377                     canvas.drawLine(mTouchHighlightX + mNavSlop + 1,
4378                             mTouchHighlightY - mNavSlop, mTouchHighlightX
4379                                     - mNavSlop,
4380                             mTouchHighlightY + mNavSlop + 1,
4381                             mTouchCrossHairColor);
4382                 }
4383             }
4384         }
4385     }
4386 
removeTouchHighlight()4387     private void removeTouchHighlight() {
4388         setTouchHighlightRects(null);
4389     }
4390 
4391     @Override
setLayoutParams(ViewGroup.LayoutParams params)4392     public void setLayoutParams(ViewGroup.LayoutParams params) {
4393         if (params.height == AbsoluteLayout.LayoutParams.WRAP_CONTENT) {
4394             mWrapContent = true;
4395         }
4396         mWebViewPrivate.super_setLayoutParams(params);
4397     }
4398 
4399     @Override
performLongClick()4400     public boolean performLongClick() {
4401         // performLongClick() is the result of a delayed message. If we switch
4402         // to windows overview, the WebView will be temporarily removed from the
4403         // view system. In that case, do nothing.
4404         if (mWebView.getParent() == null) return false;
4405 
4406         // A multi-finger gesture can look like a long press; make sure we don't take
4407         // long press actions if we're scaling.
4408         final ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
4409         if (detector != null && detector.isInProgress()) {
4410             return false;
4411         }
4412 
4413         if (mSelectingText) return false; // long click does nothing on selection
4414         /* if long click brings up a context menu, the super function
4415          * returns true and we're done. Otherwise, nothing happened when
4416          * the user clicked. */
4417         if (mWebViewPrivate.super_performLongClick()) {
4418             return true;
4419         }
4420         /* In the case where the application hasn't already handled the long
4421          * click action, look for a word under the  click. If one is found,
4422          * animate the text selection into view.
4423          * FIXME: no animation code yet */
4424         final boolean isSelecting = selectText();
4425         if (isSelecting) {
4426             mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
4427         } else if (focusCandidateIsEditableText()) {
4428             mSelectCallback = new SelectActionModeCallback();
4429             mSelectCallback.setWebView(this);
4430             mSelectCallback.setTextSelected(false);
4431             mWebView.startActionMode(mSelectCallback);
4432         }
4433         return isSelecting;
4434     }
4435 
4436     /**
4437      * Select the word at the last click point.
4438      *
4439      * This is an implementation detail.
4440      */
selectText()4441     public boolean selectText() {
4442         int x = viewToContentX(mLastTouchX + getScrollX());
4443         int y = viewToContentY(mLastTouchY + getScrollY());
4444         return selectText(x, y);
4445     }
4446 
4447     /**
4448      * Select the word at the indicated content coordinates.
4449      */
selectText(int x, int y)4450     boolean selectText(int x, int y) {
4451         if (mWebViewCore == null) {
4452             return false;
4453         }
4454         mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y);
4455         return true;
4456     }
4457 
4458     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
4459 
4460     @Override
onConfigurationChanged(Configuration newConfig)4461     public void onConfigurationChanged(Configuration newConfig) {
4462         mCachedOverlappingActionModeHeight = -1;
4463         if (mSelectingText && mOrientation != newConfig.orientation) {
4464             selectionDone();
4465         }
4466         mOrientation = newConfig.orientation;
4467         if (mWebViewCore != null && !mBlockWebkitViewMessages) {
4468             mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
4469         }
4470     }
4471 
4472     /**
4473      * Keep track of the Callback so we can end its ActionMode or remove its
4474      * titlebar.
4475      */
4476     private SelectActionModeCallback mSelectCallback;
4477 
setBaseLayer(int layer, boolean showVisualIndicator, boolean isPictureAfterFirstLayout)4478     void setBaseLayer(int layer, boolean showVisualIndicator,
4479             boolean isPictureAfterFirstLayout) {
4480         if (mNativeClass == 0)
4481             return;
4482         boolean queueFull;
4483         final int scrollingLayer = (mTouchMode == TOUCH_DRAG_LAYER_MODE)
4484                 ? mCurrentScrollingLayerId : 0;
4485         queueFull = nativeSetBaseLayer(mNativeClass, layer,
4486                                        showVisualIndicator, isPictureAfterFirstLayout,
4487                                        scrollingLayer);
4488 
4489         if (queueFull) {
4490             mWebViewCore.pauseWebKitDraw();
4491         } else {
4492             mWebViewCore.resumeWebKitDraw();
4493         }
4494 
4495         if (mHTML5VideoViewProxy != null) {
4496             mHTML5VideoViewProxy.setBaseLayer(layer);
4497         }
4498     }
4499 
getBaseLayer()4500     int getBaseLayer() {
4501         if (mNativeClass == 0) {
4502             return 0;
4503         }
4504         return nativeGetBaseLayer(mNativeClass);
4505     }
4506 
onZoomAnimationStart()4507     private void onZoomAnimationStart() {
4508     }
4509 
onZoomAnimationEnd()4510     private void onZoomAnimationEnd() {
4511         mPrivateHandler.sendEmptyMessage(RELOCATE_AUTO_COMPLETE_POPUP);
4512     }
4513 
onFixedLengthZoomAnimationStart()4514     void onFixedLengthZoomAnimationStart() {
4515         WebViewCore.pauseUpdatePicture(getWebViewCore());
4516         onZoomAnimationStart();
4517     }
4518 
onFixedLengthZoomAnimationEnd()4519     void onFixedLengthZoomAnimationEnd() {
4520         if (!mBlockWebkitViewMessages && !mSelectingText) {
4521             WebViewCore.resumeUpdatePicture(mWebViewCore);
4522         }
4523         onZoomAnimationEnd();
4524     }
4525 
4526     private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
4527                                          Paint.DITHER_FLAG |
4528                                          Paint.SUBPIXEL_TEXT_FLAG;
4529     private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
4530                                            Paint.DITHER_FLAG;
4531 
4532     private final DrawFilter mZoomFilter =
4533             new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
4534     // If we need to trade better quality for speed, set mScrollFilter to null
4535     private final DrawFilter mScrollFilter =
4536             new PaintFlagsDrawFilter(SCROLL_BITS, 0);
4537 
4538     private class SelectionHandleAlpha {
4539         private int mAlpha = 0;
4540         private int mTargetAlpha = 0;
4541 
setAlpha(int alpha)4542         public void setAlpha(int alpha) {
4543             mAlpha = alpha;
4544             // TODO: Use partial invalidate
4545             invalidate();
4546         }
4547 
getAlpha()4548         public int getAlpha() {
4549             return mAlpha;
4550         }
4551 
setTargetAlpha(int alpha)4552         public void setTargetAlpha(int alpha) {
4553             mTargetAlpha = alpha;
4554         }
4555 
getTargetAlpha()4556         public int getTargetAlpha() {
4557             return mTargetAlpha;
4558         }
4559 
4560     }
4561 
startSelectingText()4562     private void startSelectingText() {
4563         mSelectingText = true;
4564         mShowTextSelectionExtra = true;
4565         animateHandles();
4566     }
4567 
animateHandle(boolean canShow, ObjectAnimator animator, Point selectionPoint, int selectionLayerId, SelectionHandleAlpha alpha)4568     private void animateHandle(boolean canShow, ObjectAnimator animator,
4569             Point selectionPoint, int selectionLayerId,
4570             SelectionHandleAlpha alpha) {
4571         boolean isVisible = canShow && mSelectingText
4572                 && ((mSelectionStarted && mSelectDraggingCursor == selectionPoint)
4573                 || isHandleVisible(selectionPoint, selectionLayerId));
4574         int targetValue = isVisible ? 255 : 0;
4575         if (targetValue != alpha.getTargetAlpha()) {
4576             alpha.setTargetAlpha(targetValue);
4577             animator.setIntValues(targetValue);
4578             animator.setDuration(SELECTION_HANDLE_ANIMATION_MS);
4579             animator.start();
4580         }
4581     }
4582 
animateHandles()4583     private void animateHandles() {
4584         boolean canShowBase = mSelectingText;
4585         boolean canShowExtent = mSelectingText && !mIsCaretSelection;
4586         animateHandle(canShowBase, mBaseHandleAlphaAnimator, mSelectCursorBase,
4587                 mSelectCursorBaseLayerId, mBaseAlpha);
4588         animateHandle(canShowExtent, mExtentHandleAlphaAnimator,
4589                 mSelectCursorExtent, mSelectCursorExtentLayerId,
4590                 mExtentAlpha);
4591     }
4592 
endSelectingText()4593     private void endSelectingText() {
4594         mSelectingText = false;
4595         mShowTextSelectionExtra = false;
4596         animateHandles();
4597     }
4598 
ensureSelectionHandles()4599     private void ensureSelectionHandles() {
4600         if (mSelectHandleCenter == null) {
4601             mSelectHandleCenter = mContext.getResources().getDrawable(
4602                     com.android.internal.R.drawable.text_select_handle_middle).mutate();
4603             mSelectHandleLeft = mContext.getResources().getDrawable(
4604                     com.android.internal.R.drawable.text_select_handle_left).mutate();
4605             mSelectHandleRight = mContext.getResources().getDrawable(
4606                     com.android.internal.R.drawable.text_select_handle_right).mutate();
4607             // All handles have the same height, so we can save effort with
4608             // this assumption.
4609             mSelectOffset = new Point(0,
4610                     -mSelectHandleLeft.getIntrinsicHeight());
4611         }
4612     }
4613 
drawHandle(Point point, int handleId, Rect bounds, int alpha, Canvas canvas)4614     private void drawHandle(Point point, int handleId, Rect bounds,
4615             int alpha, Canvas canvas) {
4616         int offset;
4617         int width;
4618         int height;
4619         Drawable drawable;
4620         boolean isLeft = nativeIsHandleLeft(mNativeClass, handleId);
4621         if (isLeft) {
4622             drawable = mSelectHandleLeft;
4623             width = mSelectHandleLeft.getIntrinsicWidth();
4624             height = mSelectHandleLeft.getIntrinsicHeight();
4625             // Magic formula copied from TextView
4626             offset = (width * 3) / 4;
4627         } else {
4628             drawable = mSelectHandleRight;
4629             width = mSelectHandleRight.getIntrinsicWidth();
4630             height = mSelectHandleRight.getIntrinsicHeight();
4631             // Magic formula copied from TextView
4632             offset = width / 4;
4633         }
4634         int x = contentToViewDimension(point.x);
4635         int y = contentToViewDimension(point.y);
4636         bounds.set(x - offset, y, x - offset + width, y + height);
4637         drawable.setBounds(bounds);
4638         drawable.setAlpha(alpha);
4639         drawable.draw(canvas);
4640     }
4641 
drawTextSelectionHandles(Canvas canvas)4642     private void drawTextSelectionHandles(Canvas canvas) {
4643         if (mBaseAlpha.getAlpha() == 0 && mExtentAlpha.getAlpha() == 0) {
4644             return;
4645         }
4646         ensureSelectionHandles();
4647         if (mIsCaretSelection) {
4648             // Caret handle is centered
4649             int x = contentToViewDimension(mSelectCursorBase.x) -
4650                     (mSelectHandleCenter.getIntrinsicWidth() / 2);
4651             int y = contentToViewDimension(mSelectCursorBase.y);
4652             mSelectHandleBaseBounds.set(x, y,
4653                     x + mSelectHandleCenter.getIntrinsicWidth(),
4654                     y + mSelectHandleCenter.getIntrinsicHeight());
4655             mSelectHandleCenter.setBounds(mSelectHandleBaseBounds);
4656             mSelectHandleCenter.setAlpha(mBaseAlpha.getAlpha());
4657             mSelectHandleCenter.draw(canvas);
4658         } else {
4659             drawHandle(mSelectCursorBase, HANDLE_ID_BASE,
4660                     mSelectHandleBaseBounds, mBaseAlpha.getAlpha(), canvas);
4661             drawHandle(mSelectCursorExtent, HANDLE_ID_EXTENT,
4662                     mSelectHandleExtentBounds, mExtentAlpha.getAlpha(), canvas);
4663         }
4664     }
4665 
isHandleVisible(Point selectionPoint, int layerId)4666     private boolean isHandleVisible(Point selectionPoint, int layerId) {
4667         boolean isVisible = true;
4668         if (mIsEditingText) {
4669             isVisible = mEditTextContentBounds.contains(selectionPoint.x,
4670                     selectionPoint.y);
4671         }
4672         if (isVisible) {
4673             isVisible = nativeIsPointVisible(mNativeClass, layerId,
4674                     selectionPoint.x, selectionPoint.y);
4675         }
4676         return isVisible;
4677     }
4678 
4679     /**
4680      * Takes an int[4] array as an output param with the values being
4681      * startX, startY, endX, endY
4682      */
getSelectionHandles(int[] handles)4683     private void getSelectionHandles(int[] handles) {
4684         handles[0] = mSelectCursorBase.x;
4685         handles[1] = mSelectCursorBase.y;
4686         handles[2] = mSelectCursorExtent.x;
4687         handles[3] = mSelectCursorExtent.y;
4688     }
4689 
4690     // draw history
4691     private boolean mDrawHistory = false;
4692     private Picture mHistoryPicture = null;
4693     private int mHistoryWidth = 0;
4694     private int mHistoryHeight = 0;
4695 
4696     // Only check the flag, can be called from WebCore thread
drawHistory()4697     boolean drawHistory() {
4698         return mDrawHistory;
4699     }
4700 
getHistoryPictureWidth()4701     int getHistoryPictureWidth() {
4702         return (mHistoryPicture != null) ? mHistoryPicture.getWidth() : 0;
4703     }
4704 
4705     // Should only be called in UI thread
switchOutDrawHistory()4706     void switchOutDrawHistory() {
4707         if (null == mWebViewCore) return; // CallbackProxy may trigger this
4708         if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) {
4709             mDrawHistory = false;
4710             mHistoryPicture = null;
4711             invalidate();
4712             int oldScrollX = getScrollX();
4713             int oldScrollY = getScrollY();
4714             setScrollXRaw(pinLocX(getScrollX()));
4715             setScrollYRaw(pinLocY(getScrollY()));
4716             if (oldScrollX != getScrollX() || oldScrollY != getScrollY()) {
4717                 mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldScrollX, oldScrollY);
4718             } else {
4719                 sendOurVisibleRect();
4720             }
4721         }
4722     }
4723 
4724     /**
4725      *  Delete text from start to end in the focused textfield. If there is no
4726      *  focus, or if start == end, silently fail.  If start and end are out of
4727      *  order, swap them.
4728      *  @param  start   Beginning of selection to delete.
4729      *  @param  end     End of selection to delete.
4730      */
deleteSelection(int start, int end)4731     /* package */ void deleteSelection(int start, int end) {
4732         mTextGeneration++;
4733         WebViewCore.TextSelectionData data
4734                 = new WebViewCore.TextSelectionData(start, end, 0);
4735         mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
4736                 data);
4737     }
4738 
4739     /**
4740      *  Set the selection to (start, end) in the focused textfield. If start and
4741      *  end are out of order, swap them.
4742      *  @param  start   Beginning of selection.
4743      *  @param  end     End of selection.
4744      */
setSelection(int start, int end)4745     /* package */ void setSelection(int start, int end) {
4746         if (mWebViewCore != null) {
4747             mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
4748         }
4749     }
4750 
4751     @Override
onCreateInputConnection(EditorInfo outAttrs)4752     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
4753         if (mInputConnection == null) {
4754             mInputConnection = new WebViewInputConnection();
4755             mAutoCompletePopup = new AutoCompletePopup(this, mInputConnection);
4756         }
4757         mInputConnection.setupEditorInfo(outAttrs);
4758         return mInputConnection;
4759     }
4760 
relocateAutoCompletePopup()4761     private void relocateAutoCompletePopup() {
4762         if (mAutoCompletePopup != null) {
4763             mAutoCompletePopup.resetRect();
4764             mAutoCompletePopup.setText(mInputConnection.getEditable());
4765         }
4766     }
4767 
4768     /**
4769      * Called in response to a message from webkit telling us that the soft
4770      * keyboard should be launched.
4771      */
displaySoftKeyboard(boolean isTextView)4772     private void displaySoftKeyboard(boolean isTextView) {
4773         InputMethodManager imm = (InputMethodManager)
4774                 mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
4775 
4776         // bring it back to the default level scale so that user can enter text
4777         boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
4778         if (zoom) {
4779             mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
4780             mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false);
4781         }
4782         // Used by plugins and contentEditable.
4783         // Also used if the navigation cache is out of date, and
4784         // does not recognize that a textfield is in focus.  In that
4785         // case, use WebView as the targeted view.
4786         // see http://b/issue?id=2457459
4787         imm.showSoftInput(mWebView, 0);
4788     }
4789 
4790     // Called by WebKit to instruct the UI to hide the keyboard
4791     private void hideSoftKeyboard() {
4792         InputMethodManager imm = InputMethodManager.peekInstance();
4793         if (imm != null && (imm.isActive(mWebView))) {
4794             imm.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
4795         }
4796     }
4797 
4798     /**
4799      * Called by AutoCompletePopup to find saved form data associated with the
4800      * textfield
4801      * @param name Name of the textfield.
4802      * @param nodePointer Pointer to the node of the textfield, so it can be
4803      *          compared to the currently focused textfield when the data is
4804      *          retrieved.
4805      * @param autoFillable true if WebKit has determined this field is part of
4806      *          a form that can be auto filled.
4807      * @param autoComplete true if the attribute "autocomplete" is set to true
4808      *          on the textfield.
4809      */
4810     /* package */ void requestFormData(String name, int nodePointer,
4811             boolean autoFillable, boolean autoComplete) {
4812         if (mWebViewCore.getSettings().getSaveFormData()) {
4813             Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
4814             update.arg1 = nodePointer;
4815             RequestFormData updater = new RequestFormData(name, getUrl(),
4816                     update, autoFillable, autoComplete);
4817             Thread t = new Thread(updater);
4818             t.start();
4819         }
4820     }
4821 
4822     /*
4823      * This class requests an Adapter for the AutoCompletePopup which shows past
4824      * entries stored in the database.  It is a Runnable so that it can be done
4825      * in its own thread, without slowing down the UI.
4826      */
4827     private class RequestFormData implements Runnable {
4828         private String mName;
4829         private String mUrl;
4830         private Message mUpdateMessage;
4831         private boolean mAutoFillable;
4832         private boolean mAutoComplete;
4833         private WebSettingsClassic mWebSettings;
4834 
4835         public RequestFormData(String name, String url, Message msg,
4836                 boolean autoFillable, boolean autoComplete) {
4837             mName = name;
4838             mUrl = WebTextView.urlForAutoCompleteData(url);
4839             mUpdateMessage = msg;
4840             mAutoFillable = autoFillable;
4841             mAutoComplete = autoComplete;
4842             mWebSettings = getSettings();
4843         }
4844 
4845         @Override
4846         public void run() {
4847             ArrayList<String> pastEntries = new ArrayList<String>();
4848 
4849             if (mAutoFillable) {
4850                 // Note that code inside the adapter click handler in AutoCompletePopup depends
4851                 // on the AutoFill item being at the top of the drop down list. If you change
4852                 // the order, make sure to do it there too!
4853                 if (mWebSettings != null && mWebSettings.getAutoFillProfile() != null) {
4854                     pastEntries.add(mWebView.getResources().getText(
4855                             com.android.internal.R.string.autofill_this_form).toString() +
4856                             " " +
4857                     mAutoFillData.getPreviewString());
4858                     mAutoCompletePopup.setIsAutoFillProfileSet(true);
4859                 } else {
4860                     // There is no autofill profile set up yet, so add an option that
4861                     // will invite the user to set their profile up.
4862                     pastEntries.add(mWebView.getResources().getText(
4863                             com.android.internal.R.string.setup_autofill).toString());
4864                     mAutoCompletePopup.setIsAutoFillProfileSet(false);
4865                 }
4866             }
4867 
4868             if (mAutoComplete) {
4869                 pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
4870             }
4871 
4872             if (pastEntries.size() > 0) {
4873                 ArrayAdapter<String> adapter = new ArrayAdapter<String>(
4874                         mContext,
4875                         com.android.internal.R.layout.web_text_view_dropdown,
4876                         pastEntries);
4877                 mUpdateMessage.obj = adapter;
4878                 mUpdateMessage.sendToTarget();
4879             }
4880         }
4881     }
4882 
4883     /**
4884      * Dump the display tree to "/sdcard/displayTree.txt"
4885      *
4886      * debug only
4887      */
4888     public void dumpDisplayTree() {
4889         nativeDumpDisplayTree(getUrl());
4890     }
4891 
4892     /**
4893      * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
4894      * "/sdcard/domTree.txt"
4895      *
4896      * debug only
4897      */
4898     public void dumpDomTree(boolean toFile) {
4899         mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
4900     }
4901 
4902     /**
4903      * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
4904      * to "/sdcard/renderTree.txt"
4905      *
4906      * debug only
4907      */
4908     public void dumpRenderTree(boolean toFile) {
4909         mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
4910     }
4911 
4912     /**
4913      * Called by DRT on UI thread, need to proxy to WebCore thread.
4914      *
4915      * debug only
4916      */
4917     public void setUseMockDeviceOrientation() {
4918         mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_DEVICE_ORIENTATION);
4919     }
4920 
4921     /**
4922      * Sets use of the Geolocation mock client. Also resets that client. Called
4923      * by DRT on UI thread, need to proxy to WebCore thread.
4924      *
4925      * debug only
4926      */
4927     public void setUseMockGeolocation() {
4928         mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_GEOLOCATION);
4929     }
4930 
4931     /**
4932      * Called by DRT on WebCore thread.
4933      *
4934      * debug only
4935      */
4936     public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
4937         mWebViewCore.setMockGeolocationPosition(latitude, longitude, accuracy);
4938     }
4939 
4940     /**
4941      * Called by DRT on WebCore thread.
4942      *
4943      * debug only
4944      */
4945     public void setMockGeolocationError(int code, String message) {
4946         mWebViewCore.setMockGeolocationError(code, message);
4947     }
4948 
4949     /**
4950      * Called by DRT on WebCore thread.
4951      *
4952      * debug only
4953      */
4954     public void setMockGeolocationPermission(boolean allow) {
4955         mWebViewCore.setMockGeolocationPermission(allow);
4956     }
4957 
4958     /**
4959      * Called by DRT on WebCore thread.
4960      *
4961      * debug only
4962      */
4963     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
4964             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
4965         mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
4966                 canProvideGamma, gamma);
4967     }
4968 
4969     // This is used to determine long press with the center key.  Does not
4970     // affect long press with the trackball/touch.
4971     private boolean mGotCenterDown = false;
4972 
4973     @Override
onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)4974     public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
4975         if (mBlockWebkitViewMessages) {
4976             return false;
4977         }
4978         // send complex characters to webkit for use by JS and plugins
4979         if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) {
4980             // pass the key to DOM
4981             sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event);
4982             sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event);
4983             // return true as DOM handles the key
4984             return true;
4985         }
4986         return false;
4987     }
4988 
isEnterActionKey(int keyCode)4989     private boolean isEnterActionKey(int keyCode) {
4990         return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
4991                 || keyCode == KeyEvent.KEYCODE_ENTER
4992                 || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
4993     }
4994 
onKeyPreIme(int keyCode, KeyEvent event)4995     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
4996         if (mAutoCompletePopup != null) {
4997             return mAutoCompletePopup.onKeyPreIme(keyCode, event);
4998         }
4999         return false;
5000     }
5001 
5002     @Override
onKeyDown(int keyCode, KeyEvent event)5003     public boolean onKeyDown(int keyCode, KeyEvent event) {
5004         if (DebugFlags.WEB_VIEW) {
5005             Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
5006                     + "keyCode=" + keyCode
5007                     + ", " + event + ", unicode=" + event.getUnicodeChar());
5008         }
5009         if (mIsCaretSelection) {
5010             selectionDone();
5011         }
5012         if (mBlockWebkitViewMessages) {
5013             return false;
5014         }
5015 
5016         // don't implement accelerator keys here; defer to host application
5017         if (event.isCtrlPressed()) {
5018             return false;
5019         }
5020 
5021         if (mNativeClass == 0) {
5022             return false;
5023         }
5024 
5025         // do this hack up front, so it always works, regardless of touch-mode
5026         if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
5027             mAutoRedraw = !mAutoRedraw;
5028             if (mAutoRedraw) {
5029                 invalidate();
5030             }
5031             return true;
5032         }
5033 
5034         // Bubble up the key event if
5035         // 1. it is a system key; or
5036         // 2. the host application wants to handle it;
5037         if (event.isSystem()
5038                 || mCallbackProxy.uiOverrideKeyEvent(event)) {
5039             return false;
5040         }
5041 
5042         // See if the accessibility injector needs to handle this event.
5043         if (isAccessibilityInjectionEnabled()
5044                 && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
5045             return true;
5046         }
5047 
5048         if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
5049             if (event.hasNoModifiers()) {
5050                 pageUp(false);
5051                 return true;
5052             } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
5053                 pageUp(true);
5054                 return true;
5055             }
5056         }
5057 
5058         if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
5059             if (event.hasNoModifiers()) {
5060                 pageDown(false);
5061                 return true;
5062             } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
5063                 pageDown(true);
5064                 return true;
5065             }
5066         }
5067 
5068         if (keyCode == KeyEvent.KEYCODE_MOVE_HOME && event.hasNoModifiers()) {
5069             pageUp(true);
5070             return true;
5071         }
5072 
5073         if (keyCode == KeyEvent.KEYCODE_MOVE_END && event.hasNoModifiers()) {
5074             pageDown(true);
5075             return true;
5076         }
5077 
5078         if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
5079                 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
5080             switchOutDrawHistory();
5081         }
5082 
5083         if (isEnterActionKey(keyCode)) {
5084             switchOutDrawHistory();
5085             if (event.getRepeatCount() == 0) {
5086                 if (mSelectingText) {
5087                     return true; // discard press if copy in progress
5088                 }
5089                 mGotCenterDown = true;
5090                 mPrivateHandler.sendMessageDelayed(mPrivateHandler
5091                         .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
5092             }
5093         }
5094 
5095         if (getSettings().getNavDump()) {
5096             switch (keyCode) {
5097                 case KeyEvent.KEYCODE_4:
5098                     dumpDisplayTree();
5099                     break;
5100                 case KeyEvent.KEYCODE_5:
5101                 case KeyEvent.KEYCODE_6:
5102                     dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
5103                     break;
5104                 case KeyEvent.KEYCODE_7:
5105                 case KeyEvent.KEYCODE_8:
5106                     dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
5107                     break;
5108             }
5109         }
5110 
5111         // pass the key to DOM
5112         sendKeyEvent(event);
5113         // return true as DOM handles the key
5114         return true;
5115     }
5116 
5117     @Override
onKeyUp(int keyCode, KeyEvent event)5118     public boolean onKeyUp(int keyCode, KeyEvent event) {
5119         if (DebugFlags.WEB_VIEW) {
5120             Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
5121                     + ", " + event + ", unicode=" + event.getUnicodeChar());
5122         }
5123         if (mBlockWebkitViewMessages) {
5124             return false;
5125         }
5126 
5127         if (mNativeClass == 0) {
5128             return false;
5129         }
5130 
5131         // special CALL handling when cursor node's href is "tel:XXX"
5132         if (keyCode == KeyEvent.KEYCODE_CALL
5133                 && mInitialHitTestResult != null
5134                 && mInitialHitTestResult.getType() == HitTestResult.PHONE_TYPE) {
5135             String text = mInitialHitTestResult.getExtra();
5136             Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
5137             mContext.startActivity(intent);
5138             return true;
5139         }
5140 
5141         // Bubble up the key event if
5142         // 1. it is a system key; or
5143         // 2. the host application wants to handle it;
5144         if (event.isSystem()
5145                 || mCallbackProxy.uiOverrideKeyEvent(event)) {
5146             return false;
5147         }
5148 
5149         // See if the accessibility injector needs to handle this event.
5150         if (isAccessibilityInjectionEnabled()
5151                 && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
5152             return true;
5153         }
5154 
5155         if (isEnterActionKey(keyCode)) {
5156             // remove the long press message first
5157             mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
5158             mGotCenterDown = false;
5159 
5160             if (mSelectingText) {
5161                 copySelection();
5162                 selectionDone();
5163                 return true; // discard press if copy in progress
5164             }
5165         }
5166 
5167         // pass the key to DOM
5168         sendKeyEvent(event);
5169         // return true as DOM handles the key
5170         return true;
5171     }
5172 
startSelectActionMode()5173     private boolean startSelectActionMode() {
5174         mSelectCallback = new SelectActionModeCallback();
5175         mSelectCallback.setTextSelected(!mIsCaretSelection);
5176         mSelectCallback.setWebView(this);
5177         if (mWebView.startActionMode(mSelectCallback) == null) {
5178             // There is no ActionMode, so do not allow the user to modify a
5179             // selection.
5180             selectionDone();
5181             return false;
5182         }
5183         mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
5184         return true;
5185     }
5186 
showPasteWindow()5187     private void showPasteWindow() {
5188         ClipboardManager cm = (ClipboardManager)(mContext
5189                 .getSystemService(Context.CLIPBOARD_SERVICE));
5190         if (cm.hasPrimaryClip()) {
5191             Point cursorPoint = new Point(contentToViewX(mSelectCursorBase.x),
5192                     contentToViewY(mSelectCursorBase.y));
5193             Point cursorTop = calculateBaseCaretTop();
5194             cursorTop.set(contentToViewX(cursorTop.x),
5195                     contentToViewY(cursorTop.y));
5196 
5197             int[] location = new int[2];
5198             mWebView.getLocationInWindow(location);
5199             int offsetX = location[0] - getScrollX();
5200             int offsetY = location[1] - getScrollY();
5201             cursorPoint.offset(offsetX, offsetY);
5202             cursorTop.offset(offsetX, offsetY);
5203             if (mPasteWindow == null) {
5204                 mPasteWindow = new PastePopupWindow();
5205             }
5206             mPasteWindow.show(cursorPoint, cursorTop, location[0], location[1]);
5207         }
5208     }
5209 
5210     /**
5211      * Given segment AB, this finds the point C along AB that is closest to
5212      * point and then returns it scale along AB. The scale factor is AC/AB.
5213      *
5214      * @param x The x coordinate of the point near segment AB that determines
5215      * the scale factor.
5216      * @param y The y coordinate of the point near segment AB that determines
5217      * the scale factor.
5218      * @param a The first point of the line segment.
5219      * @param b The second point of the line segment.
5220      * @return The scale factor AC/AB, where C is the point on AB closest to
5221      *         point.
5222      */
scaleAlongSegment(int x, int y, PointF a, PointF b)5223     private static float scaleAlongSegment(int x, int y, PointF a, PointF b) {
5224         // The bottom line of the text box is line AB
5225         float abX = b.x - a.x;
5226         float abY = b.y - a.y;
5227         float ab2 = (abX * abX) + (abY * abY);
5228 
5229         // The line from first point in text bounds to bottom is AP
5230         float apX = x - a.x;
5231         float apY = y - a.y;
5232         float abDotAP = (apX * abX) + (apY * abY);
5233         float scale = abDotAP / ab2;
5234         return scale;
5235     }
5236 
calculateBaseCaretTop()5237     private Point calculateBaseCaretTop() {
5238         return calculateCaretTop(mSelectCursorBase, mSelectCursorBaseTextQuad);
5239     }
5240 
calculateDraggingCaretTop()5241     private Point calculateDraggingCaretTop() {
5242         return calculateCaretTop(mSelectDraggingCursor, mSelectDraggingTextQuad);
5243     }
5244 
5245     /**
5246      * Assuming arbitrary shape of a quadralateral forming text bounds, this
5247      * calculates the top of a caret.
5248      */
calculateCaretTop(Point base, QuadF quad)5249     private static Point calculateCaretTop(Point base, QuadF quad) {
5250         float scale = scaleAlongSegment(base.x, base.y, quad.p4, quad.p3);
5251         int x = Math.round(scaleCoordinate(scale, quad.p1.x, quad.p2.x));
5252         int y = Math.round(scaleCoordinate(scale, quad.p1.y, quad.p2.y));
5253         return new Point(x, y);
5254     }
5255 
hidePasteButton()5256     private void hidePasteButton() {
5257         if (mPasteWindow != null) {
5258             mPasteWindow.hide();
5259         }
5260     }
5261 
syncSelectionCursors()5262     private void syncSelectionCursors() {
5263         mSelectCursorBaseLayerId =
5264                 nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE,
5265                         mSelectCursorBase, mSelectCursorBaseTextQuad);
5266         mSelectCursorExtentLayerId =
5267                 nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT,
5268                         mSelectCursorExtent, mSelectCursorExtentTextQuad);
5269     }
5270 
setupWebkitSelect()5271     private boolean setupWebkitSelect() {
5272         syncSelectionCursors();
5273         if (!mIsCaretSelection && !startSelectActionMode()) {
5274             selectionDone();
5275             return false;
5276         }
5277         startSelectingText();
5278         mTouchMode = TOUCH_DRAG_MODE;
5279         return true;
5280     }
5281 
updateWebkitSelection(boolean isSnapped)5282     private void updateWebkitSelection(boolean isSnapped) {
5283         int handleId = (mSelectDraggingCursor == mSelectCursorBase)
5284                 ? HANDLE_ID_BASE : HANDLE_ID_EXTENT;
5285         int x = mSelectDraggingCursor.x;
5286         int y = mSelectDraggingCursor.y;
5287         if (isSnapped) {
5288             // "center" the cursor in the snapping quad
5289             Point top = calculateDraggingCaretTop();
5290             x = Math.round((top.x + x) / 2);
5291             y = Math.round((top.y + y) / 2);
5292         }
5293         mWebViewCore.removeMessages(EventHub.SELECT_TEXT);
5294         mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT,
5295                 x, y, (Integer)handleId);
5296     }
5297 
resetCaretTimer()5298     private void resetCaretTimer() {
5299         mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
5300         if (!mSelectionStarted) {
5301             mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE,
5302                     CARET_HANDLE_STAMINA_MS);
5303         }
5304     }
5305 
5306     /**
5307      * Select all of the text in this WebView.
5308      *
5309      * This is an implementation detail.
5310      */
selectAll()5311     public void selectAll() {
5312         mWebViewCore.sendMessage(EventHub.SELECT_ALL);
5313     }
5314 
5315     /**
5316      * Called when the selection has been removed.
5317      */
selectionDone()5318     void selectionDone() {
5319         if (mSelectingText) {
5320             hidePasteButton();
5321             endSelectingText();
5322             // finish is idempotent, so this is fine even if selectionDone was
5323             // called by mSelectCallback.onDestroyActionMode
5324             if (mSelectCallback != null) {
5325                 mSelectCallback.finish();
5326                 mSelectCallback = null;
5327             }
5328             invalidate(); // redraw without selection
5329             mAutoScrollX = 0;
5330             mAutoScrollY = 0;
5331             mSentAutoScrollMessage = false;
5332         }
5333     }
5334 
5335     /**
5336      * Copy the selection to the clipboard
5337      *
5338      * This is an implementation detail.
5339      */
copySelection()5340     public boolean copySelection() {
5341         boolean copiedSomething = false;
5342         String selection = getSelection();
5343         if (selection != null && selection != "") {
5344             if (DebugFlags.WEB_VIEW) {
5345                 Log.v(LOGTAG, "copySelection \"" + selection + "\"");
5346             }
5347             Toast.makeText(mContext
5348                     , com.android.internal.R.string.text_copied
5349                     , Toast.LENGTH_SHORT).show();
5350             copiedSomething = true;
5351             ClipboardManager cm = (ClipboardManager)mContext
5352                     .getSystemService(Context.CLIPBOARD_SERVICE);
5353             cm.setText(selection);
5354             int[] handles = new int[4];
5355             getSelectionHandles(handles);
5356             mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles);
5357         }
5358         invalidate(); // remove selection region and pointer
5359         return copiedSomething;
5360     }
5361 
5362     /**
5363      * Cut the selected text into the clipboard
5364      *
5365      * This is an implementation detail
5366      */
cutSelection()5367     public void cutSelection() {
5368         copySelection();
5369         int[] handles = new int[4];
5370         getSelectionHandles(handles);
5371         mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
5372     }
5373 
5374     /**
5375      * Paste text from the clipboard to the cursor position.
5376      *
5377      * This is an implementation detail
5378      */
pasteFromClipboard()5379     public void pasteFromClipboard() {
5380         ClipboardManager cm = (ClipboardManager)mContext
5381                 .getSystemService(Context.CLIPBOARD_SERVICE);
5382         ClipData clipData = cm.getPrimaryClip();
5383         if (clipData != null) {
5384             ClipData.Item clipItem = clipData.getItemAt(0);
5385             CharSequence pasteText = clipItem.getText();
5386             if (mInputConnection != null) {
5387                 mInputConnection.replaceSelection(pasteText);
5388             }
5389         }
5390     }
5391 
5392     /**
5393      * Returns the currently highlighted text as a string.
5394      */
getSelection()5395     String getSelection() {
5396         if (mNativeClass == 0) return "";
5397         return nativeGetSelection();
5398     }
5399 
5400     @Override
onAttachedToWindow()5401     public void onAttachedToWindow() {
5402         if (mWebView.hasWindowFocus()) setActive(true);
5403 
5404         if (isAccessibilityInjectionEnabled()) {
5405             getAccessibilityInjector().toggleAccessibilityFeedback(true);
5406         }
5407 
5408         updateHwAccelerated();
5409     }
5410 
5411     @Override
onDetachedFromWindow()5412     public void onDetachedFromWindow() {
5413         clearHelpers();
5414         mZoomManager.dismissZoomPicker();
5415         if (mWebView.hasWindowFocus()) setActive(false);
5416 
5417         if (isAccessibilityInjectionEnabled()) {
5418             getAccessibilityInjector().toggleAccessibilityFeedback(false);
5419         }
5420 
5421         updateHwAccelerated();
5422 
5423         ensureFunctorDetached();
5424     }
5425 
5426     @Override
onVisibilityChanged(View changedView, int visibility)5427     public void onVisibilityChanged(View changedView, int visibility) {
5428         // The zoomManager may be null if the webview is created from XML that
5429         // specifies the view's visibility param as not visible (see http://b/2794841)
5430         if (visibility != View.VISIBLE && mZoomManager != null) {
5431             mZoomManager.dismissZoomPicker();
5432         }
5433         updateDrawingState();
5434     }
5435 
setActive(boolean active)5436     void setActive(boolean active) {
5437         if (active) {
5438             if (mWebView.hasFocus()) {
5439                 // If our window regained focus, and we have focus, then begin
5440                 // drawing the cursor ring
5441                 mDrawCursorRing = true;
5442                 setFocusControllerActive(true);
5443             } else {
5444                 mDrawCursorRing = false;
5445                 setFocusControllerActive(false);
5446             }
5447         } else {
5448             if (!mZoomManager.isZoomPickerVisible()) {
5449                 /*
5450                  * The external zoom controls come in their own window, so our
5451                  * window loses focus. Our policy is to not draw the cursor ring
5452                  * if our window is not focused, but this is an exception since
5453                  * the user can still navigate the web page with the zoom
5454                  * controls showing.
5455                  */
5456                 mDrawCursorRing = false;
5457             }
5458             mKeysPressed.clear();
5459             mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
5460             mTouchMode = TOUCH_DONE_MODE;
5461             setFocusControllerActive(false);
5462         }
5463         invalidate();
5464     }
5465 
5466     // To avoid drawing the cursor ring, and remove the TextView when our window
5467     // loses focus.
5468     @Override
onWindowFocusChanged(boolean hasWindowFocus)5469     public void onWindowFocusChanged(boolean hasWindowFocus) {
5470         setActive(hasWindowFocus);
5471         if (hasWindowFocus) {
5472             JWebCoreJavaBridge.setActiveWebView(this);
5473             if (mPictureUpdatePausedForFocusChange) {
5474                 WebViewCore.resumeUpdatePicture(mWebViewCore);
5475                 mPictureUpdatePausedForFocusChange = false;
5476             }
5477         } else {
5478             JWebCoreJavaBridge.removeActiveWebView(this);
5479             final WebSettings settings = getSettings();
5480             if (settings != null && settings.enableSmoothTransition() &&
5481                     mWebViewCore != null && !WebViewCore.isUpdatePicturePaused(mWebViewCore)) {
5482                 WebViewCore.pauseUpdatePicture(mWebViewCore);
5483                 mPictureUpdatePausedForFocusChange = true;
5484             }
5485         }
5486     }
5487 
5488     /*
5489      * Pass a message to WebCore Thread, telling the WebCore::Page's
5490      * FocusController to be  "inactive" so that it will
5491      * not draw the blinking cursor.  It gets set to "active" to draw the cursor
5492      * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
5493      */
setFocusControllerActive(boolean active)5494     /* package */ void setFocusControllerActive(boolean active) {
5495         if (mWebViewCore == null) return;
5496         mWebViewCore.sendMessage(EventHub.SET_ACTIVE, active ? 1 : 0, 0);
5497         // Need to send this message after the document regains focus.
5498         if (active && mListBoxMessage != null) {
5499             mWebViewCore.sendMessage(mListBoxMessage);
5500             mListBoxMessage = null;
5501         }
5502     }
5503 
5504     @Override
onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)5505     public void onFocusChanged(boolean focused, int direction,
5506             Rect previouslyFocusedRect) {
5507         if (DebugFlags.WEB_VIEW) {
5508             Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
5509         }
5510         if (focused) {
5511             mDrawCursorRing = true;
5512             setFocusControllerActive(true);
5513         } else {
5514             mDrawCursorRing = false;
5515             setFocusControllerActive(false);
5516             mKeysPressed.clear();
5517         }
5518         if (!mTouchHighlightRegion.isEmpty()) {
5519             mWebView.invalidate(mTouchHighlightRegion.getBounds());
5520         }
5521     }
5522 
5523     // updateRectsForGL() happens almost every draw call, in order to avoid creating
5524     // any object in this code path, we move the local variable out to be a private
5525     // final member, and we marked them as mTemp*.
5526     private final Point mTempVisibleRectOffset = new Point();
5527     private final Rect mTempVisibleRect = new Rect();
5528 
updateRectsForGL()5529     void updateRectsForGL() {
5530         // Use the getGlobalVisibleRect() to get the intersection among the parents
5531         // visible == false means we're clipped - send a null rect down to indicate that
5532         // we should not draw
5533         boolean visible = mWebView.getGlobalVisibleRect(mTempVisibleRect, mTempVisibleRectOffset);
5534         mInvScreenRect.set(mTempVisibleRect);
5535         if (visible) {
5536             // Then need to invert the Y axis, just for GL
5537             View rootView = mWebView.getRootView();
5538             int rootViewHeight = rootView.getHeight();
5539             mScreenRect.set(mInvScreenRect);
5540             int savedWebViewBottom = mInvScreenRect.bottom;
5541             mInvScreenRect.bottom = rootViewHeight - mInvScreenRect.top - getVisibleTitleHeightImpl();
5542             mInvScreenRect.top = rootViewHeight - savedWebViewBottom;
5543             mIsWebViewVisible = true;
5544         } else {
5545             mIsWebViewVisible = false;
5546         }
5547 
5548         mTempVisibleRect.offset(-mTempVisibleRectOffset.x, -mTempVisibleRectOffset.y);
5549         viewToContentVisibleRect(mVisibleContentRect, mTempVisibleRect);
5550 
5551         nativeUpdateDrawGLFunction(mNativeClass, mIsWebViewVisible ? mInvScreenRect : null,
5552                 mIsWebViewVisible ? mScreenRect : null,
5553                 mVisibleContentRect, getScale());
5554     }
5555 
5556     // Input : viewRect, rect in view/screen coordinate.
5557     // Output: contentRect, rect in content/document coordinate.
viewToContentVisibleRect(RectF contentRect, Rect viewRect)5558     private void viewToContentVisibleRect(RectF contentRect, Rect viewRect) {
5559         contentRect.left = viewToContentXf(viewRect.left) / mWebView.getScaleX();
5560         // viewToContentY will remove the total height of the title bar.  Add
5561         // the visible height back in to account for the fact that if the title
5562         // bar is partially visible, the part of the visible rect which is
5563         // displaying our content is displaced by that amount.
5564         contentRect.top = viewToContentYf(viewRect.top + getVisibleTitleHeightImpl())
5565                 / mWebView.getScaleY();
5566         contentRect.right = viewToContentXf(viewRect.right) / mWebView.getScaleX();
5567         contentRect.bottom = viewToContentYf(viewRect.bottom) / mWebView.getScaleY();
5568     }
5569 
5570     @Override
setFrame(int left, int top, int right, int bottom)5571     public boolean setFrame(int left, int top, int right, int bottom) {
5572         boolean changed = mWebViewPrivate.super_setFrame(left, top, right, bottom);
5573         if (!changed && mHeightCanMeasure) {
5574             // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
5575             // in WebViewCore after we get the first layout. We do call
5576             // requestLayout() when we get contentSizeChanged(). But the View
5577             // system won't call onSizeChanged if the dimension is not changed.
5578             // In this case, we need to call sendViewSizeZoom() explicitly to
5579             // notify the WebKit about the new dimensions.
5580             sendViewSizeZoom(false);
5581         }
5582         updateRectsForGL();
5583         return changed;
5584     }
5585 
5586     @Override
onSizeChanged(int w, int h, int ow, int oh)5587     public void onSizeChanged(int w, int h, int ow, int oh) {
5588         // adjust the max viewport width depending on the view dimensions. This
5589         // is to ensure the scaling is not going insane. So do not shrink it if
5590         // the view size is temporarily smaller, e.g. when soft keyboard is up.
5591         int newMaxViewportWidth = (int) (Math.max(w, h) / mZoomManager.getDefaultMinZoomScale());
5592         if (newMaxViewportWidth > sMaxViewportWidth) {
5593             sMaxViewportWidth = newMaxViewportWidth;
5594         }
5595 
5596         mZoomManager.onSizeChanged(w, h, ow, oh);
5597 
5598         if (mLoadedPicture != null && mDelaySetPicture == null) {
5599             // Size changes normally result in a new picture
5600             // Re-set the loaded picture to simulate that
5601             // However, do not update the base layer as that hasn't changed
5602             setNewPicture(mLoadedPicture, false);
5603         }
5604         if (mIsEditingText) {
5605             scrollEditIntoView();
5606         }
5607         relocateAutoCompletePopup();
5608     }
5609 
5610     /**
5611      * Scrolls the edit field into view using the minimum scrolling necessary.
5612      * If the edit field is too large to fit in the visible window, the caret
5613      * dimensions are used so that at least the caret is visible.
5614      * A buffer of EDIT_RECT_BUFFER in view pixels is used to offset the
5615      * edit rectangle to ensure a margin with the edge of the screen.
5616      */
scrollEditIntoView()5617     private void scrollEditIntoView() {
5618         Rect visibleRect = new Rect(viewToContentX(getScrollX()),
5619                 viewToContentY(getScrollY()),
5620                 viewToContentX(getScrollX() + getWidth()),
5621                 viewToContentY(getScrollY() + getViewHeightWithTitle()));
5622         if (visibleRect.contains(mEditTextContentBounds)) {
5623             return; // no need to scroll
5624         }
5625         syncSelectionCursors();
5626         nativeFindMaxVisibleRect(mNativeClass, mEditTextLayerId, visibleRect);
5627         final int buffer = Math.max(1, viewToContentDimension(EDIT_RECT_BUFFER));
5628         Rect showRect = new Rect(
5629                 Math.max(0, mEditTextContentBounds.left - buffer),
5630                 Math.max(0, mEditTextContentBounds.top - buffer),
5631                 mEditTextContentBounds.right + buffer,
5632                 mEditTextContentBounds.bottom + buffer);
5633         Point caretTop = calculateBaseCaretTop();
5634         if (visibleRect.width() < mEditTextContentBounds.width()) {
5635             // The whole edit won't fit in the width, so use the caret rect
5636             if (mSelectCursorBase.x < caretTop.x) {
5637                 showRect.left = Math.max(0, mSelectCursorBase.x - buffer);
5638                 showRect.right = caretTop.x + buffer;
5639             } else {
5640                 showRect.left = Math.max(0, caretTop.x - buffer);
5641                 showRect.right = mSelectCursorBase.x + buffer;
5642             }
5643         }
5644         if (visibleRect.height() < mEditTextContentBounds.height()) {
5645             // The whole edit won't fit in the height, so use the caret rect
5646             if (mSelectCursorBase.y > caretTop.y) {
5647                 showRect.top = Math.max(0, caretTop.y - buffer);
5648                 showRect.bottom = mSelectCursorBase.y + buffer;
5649             } else {
5650                 showRect.top = Math.max(0, mSelectCursorBase.y - buffer);
5651                 showRect.bottom = caretTop.y + buffer;
5652             }
5653         }
5654 
5655         if (visibleRect.contains(showRect)) {
5656             return; // no need to scroll
5657         }
5658 
5659         int scrollX = viewToContentX(getScrollX());
5660         if (visibleRect.left > showRect.left) {
5661             // We are scrolled too far
5662             scrollX += showRect.left - visibleRect.left;
5663         } else if (visibleRect.right < showRect.right) {
5664             // We aren't scrolled enough to include the right
5665             scrollX += showRect.right - visibleRect.right;
5666         }
5667         int scrollY = viewToContentY(getScrollY());
5668         if (visibleRect.top > showRect.top) {
5669             scrollY += showRect.top - visibleRect.top;
5670         } else if (visibleRect.bottom < showRect.bottom) {
5671             scrollY += showRect.bottom - visibleRect.bottom;
5672         }
5673 
5674         contentScrollTo(scrollX, scrollY, false);
5675     }
5676 
5677     @Override
onScrollChanged(int l, int t, int oldl, int oldt)5678     public void onScrollChanged(int l, int t, int oldl, int oldt) {
5679         if (!mInOverScrollMode) {
5680             sendOurVisibleRect();
5681             // update WebKit if visible title bar height changed. The logic is same
5682             // as getVisibleTitleHeightImpl.
5683             int titleHeight = getTitleHeight();
5684             if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
5685                 sendViewSizeZoom(false);
5686             }
5687         }
5688     }
5689 
5690     @Override
dispatchKeyEvent(KeyEvent event)5691     public boolean dispatchKeyEvent(KeyEvent event) {
5692         switch (event.getAction()) {
5693             case KeyEvent.ACTION_DOWN:
5694                 mKeysPressed.add(Integer.valueOf(event.getKeyCode()));
5695                 break;
5696             case KeyEvent.ACTION_MULTIPLE:
5697                 // Always accept the action.
5698                 break;
5699             case KeyEvent.ACTION_UP:
5700                 int location = mKeysPressed.indexOf(Integer.valueOf(event.getKeyCode()));
5701                 if (location == -1) {
5702                     // We did not receive the key down for this key, so do not
5703                     // handle the key up.
5704                     return false;
5705                 } else {
5706                     // We did receive the key down.  Handle the key up, and
5707                     // remove it from our pressed keys.
5708                     mKeysPressed.remove(location);
5709                 }
5710                 break;
5711             default:
5712                 // Accept the action.  This should not happen, unless a new
5713                 // action is added to KeyEvent.
5714                 break;
5715         }
5716         return mWebViewPrivate.super_dispatchKeyEvent(event);
5717     }
5718 
5719     /*
5720      * Here is the snap align logic:
5721      * 1. If it starts nearly horizontally or vertically, snap align;
5722      * 2. If there is a dramitic direction change, let it go;
5723      *
5724      * Adjustable parameters. Angle is the radians on a unit circle, limited
5725      * to quadrant 1. Values range from 0f (horizontal) to PI/2 (vertical)
5726      */
5727     private static final float HSLOPE_TO_START_SNAP = .25f;
5728     private static final float HSLOPE_TO_BREAK_SNAP = .4f;
5729     private static final float VSLOPE_TO_START_SNAP = 1.25f;
5730     private static final float VSLOPE_TO_BREAK_SNAP = .95f;
5731     /*
5732      *  These values are used to influence the average angle when entering
5733      *  snap mode. If is is the first movement entering snap, we set the average
5734      *  to the appropriate ideal. If the user is entering into snap after the
5735      *  first movement, then we average the average angle with these values.
5736      */
5737     private static final float ANGLE_VERT = 2f;
5738     private static final float ANGLE_HORIZ = 0f;
5739     /*
5740      *  The modified moving average weight.
5741      *  Formula: MAV[t]=MAV[t-1] + (P[t]-MAV[t-1])/n
5742      */
5743     private static final float MMA_WEIGHT_N = 5;
5744 
inFullScreenMode()5745     private boolean inFullScreenMode() {
5746         return mFullScreenHolder != null;
5747     }
5748 
dismissFullScreenMode()5749     private void dismissFullScreenMode() {
5750         if (inFullScreenMode()) {
5751             mFullScreenHolder.hide();
5752             mFullScreenHolder = null;
5753             invalidate();
5754         }
5755     }
5756 
onPinchToZoomAnimationStart()5757     void onPinchToZoomAnimationStart() {
5758         // cancel the single touch handling
5759         cancelTouch();
5760         onZoomAnimationStart();
5761     }
5762 
onPinchToZoomAnimationEnd(ScaleGestureDetector detector)5763     void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) {
5764         onZoomAnimationEnd();
5765         // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as
5766         // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE
5767         // as it may trigger the unwanted fling.
5768         mTouchMode = TOUCH_PINCH_DRAG;
5769         mConfirmMove = true;
5770         startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
5771     }
5772 
5773     // See if there is a layer at x, y and switch to TOUCH_DRAG_LAYER_MODE if a
5774     // layer is found.
startScrollingLayer(float x, float y)5775     private void startScrollingLayer(float x, float y) {
5776         if (mNativeClass == 0)
5777             return;
5778 
5779         int contentX = viewToContentX((int) x + getScrollX());
5780         int contentY = viewToContentY((int) y + getScrollY());
5781         mCurrentScrollingLayerId = nativeScrollableLayer(mNativeClass,
5782                 contentX, contentY, mScrollingLayerRect, mScrollingLayerBounds);
5783         if (mCurrentScrollingLayerId != 0) {
5784             mTouchMode = TOUCH_DRAG_LAYER_MODE;
5785         }
5786     }
5787 
5788     // 1/(density * density) used to compute the distance between points.
5789     // Computed in init().
5790     private float DRAG_LAYER_INVERSE_DENSITY_SQUARED;
5791 
5792     // The distance between two points reported in onTouchEvent scaled by the
5793     // density of the screen.
5794     private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
5795 
5796     @Override
onHoverEvent(MotionEvent event)5797     public boolean onHoverEvent(MotionEvent event) {
5798         if (mNativeClass == 0) {
5799             return false;
5800         }
5801         int x = viewToContentX((int) event.getX() + getScrollX());
5802         int y = viewToContentY((int) event.getY() + getScrollY());
5803         mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, x, y);
5804         mWebViewPrivate.super_onHoverEvent(event);
5805         return true;
5806     }
5807 
5808     @Override
onTouchEvent(MotionEvent ev)5809     public boolean onTouchEvent(MotionEvent ev) {
5810         if (mNativeClass == 0 || (!mWebView.isClickable() && !mWebView.isLongClickable())) {
5811             return false;
5812         }
5813 
5814         if (mInputDispatcher == null) {
5815             return false;
5816         }
5817 
5818         if (mWebView.isFocusable() && mWebView.isFocusableInTouchMode()
5819                 && !mWebView.isFocused()) {
5820             mWebView.requestFocus();
5821         }
5822 
5823         if (mInputDispatcher.postPointerEvent(ev, getScrollX(),
5824                 getScrollY() - getTitleHeight(), mZoomManager.getInvScale())) {
5825             mInputDispatcher.dispatchUiEvents();
5826             return true;
5827         } else {
5828             Log.w(LOGTAG, "mInputDispatcher rejected the event!");
5829             return false;
5830         }
5831     }
5832 
calculateDragAngle(int dx, int dy)5833     private float calculateDragAngle(int dx, int dy) {
5834         dx = Math.abs(dx);
5835         dy = Math.abs(dy);
5836         return (float) Math.atan2(dy, dx);
5837     }
5838 
5839     /*
5840     * Common code for single touch and multi-touch.
5841     * (x, y) denotes current focus point, which is the touch point for single touch
5842     * and the middle point for multi-touch.
5843     */
handleTouchEventCommon(MotionEvent event, int action, int x, int y)5844     private void handleTouchEventCommon(MotionEvent event, int action, int x, int y) {
5845         ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
5846 
5847         long eventTime = event.getEventTime();
5848 
5849         // Due to the touch screen edge effect, a touch closer to the edge
5850         // always snapped to the edge. As getViewWidth() can be different from
5851         // getWidth() due to the scrollbar, adjusting the point to match
5852         // getViewWidth(). Same applied to the height.
5853         x = Math.min(x, getViewWidth() - 1);
5854         y = Math.min(y, getViewHeightWithTitle() - 1);
5855 
5856         int deltaX = mLastTouchX - x;
5857         int deltaY = mLastTouchY - y;
5858         int contentX = viewToContentX(x + getScrollX());
5859         int contentY = viewToContentY(y + getScrollY());
5860 
5861         switch (action) {
5862             case MotionEvent.ACTION_DOWN: {
5863                 mConfirmMove = false;
5864                 if (!mEditTextScroller.isFinished()) {
5865                     mEditTextScroller.abortAnimation();
5866                 }
5867                 if (!mScroller.isFinished()) {
5868                     // stop the current scroll animation, but if this is
5869                     // the start of a fling, allow it to add to the current
5870                     // fling's velocity
5871                     mScroller.abortAnimation();
5872                     mTouchMode = TOUCH_DRAG_START_MODE;
5873                     mConfirmMove = true;
5874                     nativeSetIsScrolling(false);
5875                 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
5876                     mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
5877                     removeTouchHighlight();
5878                     if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
5879                         mTouchMode = TOUCH_DOUBLE_TAP_MODE;
5880                     } else {
5881                         mTouchMode = TOUCH_INIT_MODE;
5882                     }
5883                 } else { // the normal case
5884                     mTouchMode = TOUCH_INIT_MODE;
5885                     if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
5886                         EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
5887                                 (eventTime - mLastTouchUpTime), eventTime);
5888                     }
5889                     mSelectionStarted = false;
5890                     if (mSelectingText) {
5891                         ensureSelectionHandles();
5892                         int shiftedY = y - getTitleHeight() + getScrollY();
5893                         int shiftedX = x + getScrollX();
5894                         if (mSelectHandleBaseBounds.contains(shiftedX, shiftedY)) {
5895                             mSelectionStarted = true;
5896                             mSelectDraggingCursor = mSelectCursorBase;
5897                             mSelectDraggingTextQuad = mSelectCursorBaseTextQuad;
5898                             if (mIsCaretSelection) {
5899                                 mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
5900                                 hidePasteButton();
5901                             }
5902                         } else if (mSelectHandleExtentBounds
5903                                 .contains(shiftedX, shiftedY)) {
5904                             mSelectionStarted = true;
5905                             mSelectDraggingCursor = mSelectCursorExtent;
5906                             mSelectDraggingTextQuad = mSelectCursorExtentTextQuad;
5907                         } else if (mIsCaretSelection) {
5908                             selectionDone();
5909                         }
5910                         if (DebugFlags.WEB_VIEW) {
5911                             Log.v(LOGTAG, "select=" + contentX + "," + contentY);
5912                         }
5913                     }
5914                 }
5915                 // Trigger the link
5916                 if (!mSelectingText && (mTouchMode == TOUCH_INIT_MODE
5917                         || mTouchMode == TOUCH_DOUBLE_TAP_MODE)) {
5918                     mPrivateHandler.sendEmptyMessageDelayed(
5919                             SWITCH_TO_SHORTPRESS, TAP_TIMEOUT);
5920                     mPrivateHandler.sendEmptyMessageDelayed(
5921                             SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT);
5922                 }
5923                 startTouch(x, y, eventTime);
5924                 if (mIsEditingText) {
5925                     mTouchInEditText = mEditTextContentBounds
5926                             .contains(contentX, contentY);
5927                 }
5928                 break;
5929             }
5930             case MotionEvent.ACTION_MOVE: {
5931                 if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY)
5932                         >= mTouchSlopSquare) {
5933                     mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
5934                     mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
5935                     mConfirmMove = true;
5936                     if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
5937                         mTouchMode = TOUCH_INIT_MODE;
5938                     }
5939                     removeTouchHighlight();
5940                 }
5941                 if (mSelectingText && mSelectionStarted) {
5942                     if (DebugFlags.WEB_VIEW) {
5943                         Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
5944                     }
5945                     ViewParent parent = mWebView.getParent();
5946                     if (parent != null) {
5947                         parent.requestDisallowInterceptTouchEvent(true);
5948                     }
5949                     if (deltaX != 0 || deltaY != 0) {
5950                         int handleX = contentX +
5951                                 viewToContentDimension(mSelectOffset.x);
5952                         int handleY = contentY +
5953                                 viewToContentDimension(mSelectOffset.y);
5954                         mSelectDraggingCursor.set(handleX, handleY);
5955                         boolean inCursorText =
5956                                 mSelectDraggingTextQuad.containsPoint(handleX, handleY);
5957                         boolean inEditBounds = mEditTextContentBounds
5958                                 .contains(handleX, handleY);
5959                         if (mIsEditingText && !inEditBounds) {
5960                             beginScrollEdit();
5961                         } else {
5962                             endScrollEdit();
5963                         }
5964                         boolean snapped = false;
5965                         if (inCursorText || (mIsEditingText && !inEditBounds)) {
5966                             snapDraggingCursor();
5967                             snapped = true;
5968                         }
5969                         updateWebkitSelection(snapped);
5970                         if (!inCursorText && mIsEditingText && inEditBounds) {
5971                             // Visually snap even if we have moved the handle.
5972                             snapDraggingCursor();
5973                         }
5974                         mLastTouchX = x;
5975                         mLastTouchY = y;
5976                         invalidate();
5977                     }
5978                     break;
5979                 }
5980 
5981                 if (mTouchMode == TOUCH_DONE_MODE) {
5982                     // no dragging during scroll zoom animation, or when prevent
5983                     // default is yes
5984                     break;
5985                 }
5986                 if (mVelocityTracker == null) {
5987                     Log.e(LOGTAG, "Got null mVelocityTracker when "
5988                             + " mTouchMode = " + mTouchMode);
5989                 } else {
5990                     mVelocityTracker.addMovement(event);
5991                 }
5992 
5993                 if (mTouchMode != TOUCH_DRAG_MODE &&
5994                         mTouchMode != TOUCH_DRAG_LAYER_MODE &&
5995                         mTouchMode != TOUCH_DRAG_TEXT_MODE) {
5996 
5997                     if (!mConfirmMove) {
5998                         break;
5999                     }
6000 
6001                     // Only lock dragging to one axis if we don't have a scale in progress.
6002                     // Scaling implies free-roaming movement. Note this is only ever a question
6003                     // if mZoomManager.supportsPanDuringZoom() is true.
6004                     mAverageAngle = calculateDragAngle(deltaX, deltaY);
6005                     if (detector == null || !detector.isInProgress()) {
6006                         // if it starts nearly horizontal or vertical, enforce it
6007                         if (mAverageAngle < HSLOPE_TO_START_SNAP) {
6008                             mSnapScrollMode = SNAP_X;
6009                             mSnapPositive = deltaX > 0;
6010                             mAverageAngle = ANGLE_HORIZ;
6011                         } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
6012                             mSnapScrollMode = SNAP_Y;
6013                             mSnapPositive = deltaY > 0;
6014                             mAverageAngle = ANGLE_VERT;
6015                         }
6016                     }
6017 
6018                     mTouchMode = TOUCH_DRAG_MODE;
6019                     mLastTouchX = x;
6020                     mLastTouchY = y;
6021                     deltaX = 0;
6022                     deltaY = 0;
6023 
6024                     startScrollingLayer(x, y);
6025                     startDrag();
6026                 }
6027 
6028                 // do pan
6029                 boolean keepScrollBarsVisible = false;
6030                 if (deltaX == 0 && deltaY == 0) {
6031                     keepScrollBarsVisible = true;
6032                 } else {
6033                     mAverageAngle +=
6034                         (calculateDragAngle(deltaX, deltaY) - mAverageAngle)
6035                         / MMA_WEIGHT_N;
6036                     if (mSnapScrollMode != SNAP_NONE) {
6037                         if (mSnapScrollMode == SNAP_Y) {
6038                             // radical change means getting out of snap mode
6039                             if (mAverageAngle < VSLOPE_TO_BREAK_SNAP) {
6040                                 mSnapScrollMode = SNAP_NONE;
6041                             }
6042                         }
6043                         if (mSnapScrollMode == SNAP_X) {
6044                             // radical change means getting out of snap mode
6045                             if (mAverageAngle > HSLOPE_TO_BREAK_SNAP) {
6046                                 mSnapScrollMode = SNAP_NONE;
6047                             }
6048                         }
6049                     } else {
6050                         if (mAverageAngle < HSLOPE_TO_START_SNAP) {
6051                             mSnapScrollMode = SNAP_X;
6052                             mSnapPositive = deltaX > 0;
6053                             mAverageAngle = (mAverageAngle + ANGLE_HORIZ) / 2;
6054                         } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
6055                             mSnapScrollMode = SNAP_Y;
6056                             mSnapPositive = deltaY > 0;
6057                             mAverageAngle = (mAverageAngle + ANGLE_VERT) / 2;
6058                         }
6059                     }
6060                     if (mSnapScrollMode != SNAP_NONE) {
6061                         if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
6062                             deltaY = 0;
6063                         } else {
6064                             deltaX = 0;
6065                         }
6066                     }
6067                     if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) {
6068                         mHeldMotionless = MOTIONLESS_FALSE;
6069                     } else {
6070                         mHeldMotionless = MOTIONLESS_TRUE;
6071                         keepScrollBarsVisible = true;
6072                     }
6073 
6074                     mLastTouchTime = eventTime;
6075                     boolean allDrag = doDrag(deltaX, deltaY);
6076                     if (allDrag) {
6077                         mLastTouchX = x;
6078                         mLastTouchY = y;
6079                     } else {
6080                         int contentDeltaX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
6081                         int roundedDeltaX = contentToViewDimension(contentDeltaX);
6082                         int contentDeltaY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
6083                         int roundedDeltaY = contentToViewDimension(contentDeltaY);
6084                         mLastTouchX -= roundedDeltaX;
6085                         mLastTouchY -= roundedDeltaY;
6086                     }
6087                 }
6088 
6089                 break;
6090             }
6091             case MotionEvent.ACTION_UP: {
6092                 if (mIsEditingText && mSelectionStarted) {
6093                     endScrollEdit();
6094                     mPrivateHandler.sendEmptyMessageDelayed(SCROLL_HANDLE_INTO_VIEW,
6095                             TEXT_SCROLL_FIRST_SCROLL_MS);
6096                     if (!mConfirmMove && mIsCaretSelection) {
6097                         showPasteWindow();
6098                         stopTouch();
6099                         break;
6100                     }
6101                 }
6102                 mLastTouchUpTime = eventTime;
6103                 if (mSentAutoScrollMessage) {
6104                     mAutoScrollX = mAutoScrollY = 0;
6105                 }
6106                 switch (mTouchMode) {
6107                     case TOUCH_DOUBLE_TAP_MODE: // double tap
6108                         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
6109                         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
6110                         mTouchMode = TOUCH_DONE_MODE;
6111                         break;
6112                     case TOUCH_INIT_MODE: // tap
6113                     case TOUCH_SHORTPRESS_START_MODE:
6114                     case TOUCH_SHORTPRESS_MODE:
6115                         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
6116                         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
6117                         if (!mConfirmMove) {
6118                             if (mSelectingText) {
6119                                 // tapping on selection or controls does nothing
6120                                 if (!mSelectionStarted) {
6121                                     selectionDone();
6122                                 }
6123                                 break;
6124                             }
6125                             // only trigger double tap if the WebView is
6126                             // scalable
6127                             if (mTouchMode == TOUCH_INIT_MODE
6128                                     && (canZoomIn() || canZoomOut())) {
6129                                 mPrivateHandler.sendEmptyMessageDelayed(
6130                                         RELEASE_SINGLE_TAP, ViewConfiguration
6131                                                 .getDoubleTapTimeout());
6132                             }
6133                             break;
6134                         }
6135                     case TOUCH_DRAG_MODE:
6136                     case TOUCH_DRAG_LAYER_MODE:
6137                     case TOUCH_DRAG_TEXT_MODE:
6138                         mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
6139                         // if the user waits a while w/o moving before the
6140                         // up, we don't want to do a fling
6141                         if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
6142                             if (mVelocityTracker == null) {
6143                                 Log.e(LOGTAG, "Got null mVelocityTracker");
6144                             } else {
6145                                 mVelocityTracker.addMovement(event);
6146                             }
6147                             // set to MOTIONLESS_IGNORE so that it won't keep
6148                             // removing and sending message in
6149                             // drawCoreAndCursorRing()
6150                             mHeldMotionless = MOTIONLESS_IGNORE;
6151                             doFling();
6152                             break;
6153                         } else {
6154                             if (mScroller.springBack(getScrollX(), getScrollY(), 0,
6155                                     computeMaxScrollX(), 0,
6156                                     computeMaxScrollY())) {
6157                                 invalidate();
6158                             }
6159                         }
6160                         // redraw in high-quality, as we're done dragging
6161                         mHeldMotionless = MOTIONLESS_TRUE;
6162                         invalidate();
6163                         // fall through
6164                     case TOUCH_DRAG_START_MODE:
6165                         // TOUCH_DRAG_START_MODE should not happen for the real
6166                         // device as we almost certain will get a MOVE. But this
6167                         // is possible on emulator.
6168                         mLastVelocity = 0;
6169                         WebViewCore.resumePriority();
6170                         if (!mSelectingText) {
6171                             WebViewCore.resumeUpdatePicture(mWebViewCore);
6172                         }
6173                         break;
6174                 }
6175                 stopTouch();
6176                 break;
6177             }
6178             case MotionEvent.ACTION_CANCEL: {
6179                 if (mTouchMode == TOUCH_DRAG_MODE) {
6180                     mScroller.springBack(getScrollX(), getScrollY(), 0,
6181                             computeMaxScrollX(), 0, computeMaxScrollY());
6182                     invalidate();
6183                 }
6184                 cancelTouch();
6185                 break;
6186             }
6187         }
6188     }
6189 
6190     /**
6191      * Returns the text scroll speed in content pixels per millisecond based on
6192      * the touch location.
6193      * @param coordinate The x or y touch coordinate in content space
6194      * @param min The minimum coordinate (x or y) of the edit content bounds
6195      * @param max The maximum coordinate (x or y) of the edit content bounds
6196      */
getTextScrollSpeed(int coordinate, int min, int max)6197     private static float getTextScrollSpeed(int coordinate, int min, int max) {
6198         if (coordinate < min) {
6199             return (coordinate - min) * TEXT_SCROLL_RATE;
6200         } else if (coordinate >= max) {
6201             return (coordinate - max + 1) * TEXT_SCROLL_RATE;
6202         } else {
6203             return 0.0f;
6204         }
6205     }
6206 
getSelectionCoordinate(int coordinate, int min, int max)6207     private static int getSelectionCoordinate(int coordinate, int min, int max) {
6208         return Math.max(Math.min(coordinate, max), min);
6209     }
6210 
beginScrollEdit()6211     private void beginScrollEdit() {
6212         if (mLastEditScroll == 0) {
6213             mLastEditScroll = SystemClock.uptimeMillis() -
6214                     TEXT_SCROLL_FIRST_SCROLL_MS;
6215             scrollEditWithCursor();
6216         }
6217     }
6218 
scrollDraggedSelectionHandleIntoView()6219     private void scrollDraggedSelectionHandleIntoView() {
6220         if (mSelectDraggingCursor == null) {
6221             return;
6222         }
6223         int x = mSelectDraggingCursor.x;
6224         int y = mSelectDraggingCursor.y;
6225         if (!mEditTextContentBounds.contains(x,y)) {
6226             int left = Math.min(0, x - mEditTextContentBounds.left - EDIT_RECT_BUFFER);
6227             int right = Math.max(0, x - mEditTextContentBounds.right + EDIT_RECT_BUFFER);
6228             int deltaX = left + right;
6229             int above = Math.min(0, y - mEditTextContentBounds.top - EDIT_RECT_BUFFER);
6230             int below = Math.max(0, y - mEditTextContentBounds.bottom + EDIT_RECT_BUFFER);
6231             int deltaY = above + below;
6232             if (deltaX != 0 || deltaY != 0) {
6233                 int scrollX = getTextScrollX() + deltaX;
6234                 int scrollY = getTextScrollY() + deltaY;
6235                 scrollX = clampBetween(scrollX, 0, getMaxTextScrollX());
6236                 scrollY = clampBetween(scrollY, 0, getMaxTextScrollY());
6237                 scrollEditText(scrollX, scrollY);
6238             }
6239         }
6240     }
6241 
endScrollEdit()6242     private void endScrollEdit() {
6243         mLastEditScroll = 0;
6244     }
6245 
clampBetween(int value, int min, int max)6246     private static int clampBetween(int value, int min, int max) {
6247         return Math.max(min, Math.min(value, max));
6248     }
6249 
getTextScrollDelta(float speed, long deltaT)6250     private static int getTextScrollDelta(float speed, long deltaT) {
6251         float distance = speed * deltaT;
6252         int intDistance = (int)Math.floor(distance);
6253         float probability = distance - intDistance;
6254         if (Math.random() < probability) {
6255             intDistance++;
6256         }
6257         return intDistance;
6258     }
6259     /**
6260      * Scrolls edit text a distance based on the last touch point,
6261      * the last scroll time, and the edit text content bounds.
6262      */
scrollEditWithCursor()6263     private void scrollEditWithCursor() {
6264         if (mLastEditScroll != 0) {
6265             int x = viewToContentX(mLastTouchX + getScrollX() + mSelectOffset.x);
6266             float scrollSpeedX = getTextScrollSpeed(x, mEditTextContentBounds.left,
6267                     mEditTextContentBounds.right);
6268             int y = viewToContentY(mLastTouchY + getScrollY() + mSelectOffset.y);
6269             float scrollSpeedY = getTextScrollSpeed(y, mEditTextContentBounds.top,
6270                     mEditTextContentBounds.bottom);
6271             if (scrollSpeedX == 0.0f && scrollSpeedY == 0.0f) {
6272                 endScrollEdit();
6273             } else {
6274                 long currentTime = SystemClock.uptimeMillis();
6275                 long timeSinceLastUpdate = currentTime - mLastEditScroll;
6276                 int deltaX = getTextScrollDelta(scrollSpeedX, timeSinceLastUpdate);
6277                 int deltaY = getTextScrollDelta(scrollSpeedY, timeSinceLastUpdate);
6278                 int scrollX = getTextScrollX() + deltaX;
6279                 scrollX = clampBetween(scrollX, 0, getMaxTextScrollX());
6280                 int scrollY = getTextScrollY() + deltaY;
6281                 scrollY = clampBetween(scrollY, 0, getMaxTextScrollY());
6282 
6283                 mLastEditScroll = currentTime;
6284                 if (scrollX == getTextScrollX() && scrollY == getTextScrollY()) {
6285                     // By probability no text scroll this time. Try again later.
6286                     mPrivateHandler.sendEmptyMessageDelayed(SCROLL_EDIT_TEXT,
6287                             TEXT_SCROLL_FIRST_SCROLL_MS);
6288                 } else {
6289                     int selectionX = getSelectionCoordinate(x,
6290                             mEditTextContentBounds.left, mEditTextContentBounds.right);
6291                     int selectionY = getSelectionCoordinate(y,
6292                             mEditTextContentBounds.top, mEditTextContentBounds.bottom);
6293                     int oldX = mSelectDraggingCursor.x;
6294                     int oldY = mSelectDraggingCursor.y;
6295                     mSelectDraggingCursor.set(selectionX, selectionY);
6296                     updateWebkitSelection(false);
6297                     scrollEditText(scrollX, scrollY);
6298                     mSelectDraggingCursor.set(oldX, oldY);
6299                 }
6300             }
6301         }
6302     }
6303 
startTouch(float x, float y, long eventTime)6304     private void startTouch(float x, float y, long eventTime) {
6305         // Remember where the motion event started
6306         mStartTouchX = mLastTouchX = Math.round(x);
6307         mStartTouchY = mLastTouchY = Math.round(y);
6308         mLastTouchTime = eventTime;
6309         mVelocityTracker = VelocityTracker.obtain();
6310         mSnapScrollMode = SNAP_NONE;
6311     }
6312 
startDrag()6313     private void startDrag() {
6314         WebViewCore.reducePriority();
6315         // to get better performance, pause updating the picture
6316         WebViewCore.pauseUpdatePicture(mWebViewCore);
6317         nativeSetIsScrolling(true);
6318 
6319         if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
6320                 || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) {
6321             mZoomManager.invokeZoomPicker();
6322         }
6323     }
6324 
doDrag(int deltaX, int deltaY)6325     private boolean doDrag(int deltaX, int deltaY) {
6326         boolean allDrag = true;
6327         if ((deltaX | deltaY) != 0) {
6328             int oldX = getScrollX();
6329             int oldY = getScrollY();
6330             int rangeX = computeMaxScrollX();
6331             int rangeY = computeMaxScrollY();
6332             final int contentX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
6333             final int contentY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
6334 
6335             // Assume page scrolling and change below if we're wrong
6336             mTouchMode = TOUCH_DRAG_MODE;
6337 
6338             // Check special scrolling before going to main page scrolling.
6339             if (mIsEditingText && mTouchInEditText && canTextScroll(deltaX, deltaY)) {
6340                 // Edit text scrolling
6341                 oldX = getTextScrollX();
6342                 rangeX = getMaxTextScrollX();
6343                 deltaX = contentX;
6344                 oldY = getTextScrollY();
6345                 rangeY = getMaxTextScrollY();
6346                 deltaY = contentY;
6347                 mTouchMode = TOUCH_DRAG_TEXT_MODE;
6348                 allDrag = false;
6349             } else if (mCurrentScrollingLayerId != 0) {
6350                 // Check the scrolling bounds to see if we will actually do any
6351                 // scrolling.  The rectangle is in document coordinates.
6352                 final int maxX = mScrollingLayerRect.right;
6353                 final int maxY = mScrollingLayerRect.bottom;
6354                 final int resultX = clampBetween(maxX, 0,
6355                         mScrollingLayerRect.left + contentX);
6356                 final int resultY = clampBetween(maxY, 0,
6357                         mScrollingLayerRect.top + contentY);
6358 
6359                 if (resultX != mScrollingLayerRect.left
6360                         || resultY != mScrollingLayerRect.top
6361                         || (contentX | contentY) == 0) {
6362                     // In case we switched to dragging the page.
6363                     mTouchMode = TOUCH_DRAG_LAYER_MODE;
6364                     deltaX = contentX;
6365                     deltaY = contentY;
6366                     oldX = mScrollingLayerRect.left;
6367                     oldY = mScrollingLayerRect.top;
6368                     rangeX = maxX;
6369                     rangeY = maxY;
6370                     allDrag = false;
6371                 }
6372             }
6373 
6374             if (mOverScrollGlow != null) {
6375                 mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
6376             }
6377 
6378             mWebViewPrivate.overScrollBy(deltaX, deltaY, oldX, oldY,
6379                     rangeX, rangeY,
6380                     mOverscrollDistance, mOverscrollDistance, true);
6381             if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
6382                 invalidate();
6383             }
6384         }
6385         mZoomManager.keepZoomPickerVisible();
6386         return allDrag;
6387     }
6388 
stopTouch()6389     private void stopTouch() {
6390         if (mScroller.isFinished() && !mSelectingText
6391                 && (mTouchMode == TOUCH_DRAG_MODE
6392                 || mTouchMode == TOUCH_DRAG_LAYER_MODE)) {
6393             WebViewCore.resumePriority();
6394             WebViewCore.resumeUpdatePicture(mWebViewCore);
6395             nativeSetIsScrolling(false);
6396         }
6397 
6398         // we also use mVelocityTracker == null to tell us that we are
6399         // not "moving around", so we can take the slower/prettier
6400         // mode in the drawing code
6401         if (mVelocityTracker != null) {
6402             mVelocityTracker.recycle();
6403             mVelocityTracker = null;
6404         }
6405 
6406         // Release any pulled glows
6407         if (mOverScrollGlow != null) {
6408             mOverScrollGlow.releaseAll();
6409         }
6410 
6411         if (mSelectingText) {
6412             mSelectionStarted = false;
6413             syncSelectionCursors();
6414             if (mIsCaretSelection) {
6415                 resetCaretTimer();
6416             }
6417             invalidate();
6418         }
6419     }
6420 
cancelTouch()6421     private void cancelTouch() {
6422         // we also use mVelocityTracker == null to tell us that we are
6423         // not "moving around", so we can take the slower/prettier
6424         // mode in the drawing code
6425         if (mVelocityTracker != null) {
6426             mVelocityTracker.recycle();
6427             mVelocityTracker = null;
6428         }
6429 
6430         if ((mTouchMode == TOUCH_DRAG_MODE
6431                 || mTouchMode == TOUCH_DRAG_LAYER_MODE) && !mSelectingText) {
6432             WebViewCore.resumePriority();
6433             WebViewCore.resumeUpdatePicture(mWebViewCore);
6434             nativeSetIsScrolling(false);
6435         }
6436         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
6437         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
6438         mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
6439         removeTouchHighlight();
6440         mHeldMotionless = MOTIONLESS_TRUE;
6441         mTouchMode = TOUCH_DONE_MODE;
6442     }
6443 
snapDraggingCursor()6444     private void snapDraggingCursor() {
6445         float scale = scaleAlongSegment(
6446                 mSelectDraggingCursor.x, mSelectDraggingCursor.y,
6447                 mSelectDraggingTextQuad.p4, mSelectDraggingTextQuad.p3);
6448         // clamp scale to ensure point is on the bottom segment
6449         scale = Math.max(0.0f, scale);
6450         scale = Math.min(scale, 1.0f);
6451         float newX = scaleCoordinate(scale,
6452                 mSelectDraggingTextQuad.p4.x, mSelectDraggingTextQuad.p3.x);
6453         float newY = scaleCoordinate(scale,
6454                 mSelectDraggingTextQuad.p4.y, mSelectDraggingTextQuad.p3.y);
6455         int x = Math.round(newX);
6456         int y = Math.round(newY);
6457         if (mIsEditingText) {
6458             x = clampBetween(x, mEditTextContentBounds.left,
6459                     mEditTextContentBounds.right);
6460             y = clampBetween(y, mEditTextContentBounds.top,
6461                     mEditTextContentBounds.bottom);
6462         }
6463         mSelectDraggingCursor.set(x, y);
6464     }
6465 
scaleCoordinate(float scale, float coord1, float coord2)6466     private static float scaleCoordinate(float scale, float coord1, float coord2) {
6467         float diff = coord2 - coord1;
6468         return coord1 + (scale * diff);
6469     }
6470 
6471     @Override
onGenericMotionEvent(MotionEvent event)6472     public boolean onGenericMotionEvent(MotionEvent event) {
6473         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
6474             switch (event.getAction()) {
6475                 case MotionEvent.ACTION_SCROLL: {
6476                     final float vscroll;
6477                     final float hscroll;
6478                     if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
6479                         vscroll = 0;
6480                         hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
6481                     } else {
6482                         vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
6483                         hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
6484                     }
6485                     if (hscroll != 0 || vscroll != 0) {
6486                         final int vdelta = (int) (vscroll *
6487                                 mWebViewPrivate.getVerticalScrollFactor());
6488                         final int hdelta = (int) (hscroll *
6489                                 mWebViewPrivate.getHorizontalScrollFactor());
6490 
6491                         abortAnimation();
6492                         int oldTouchMode = mTouchMode;
6493                         startScrollingLayer(event.getX(), event.getY());
6494                         doDrag(hdelta, vdelta);
6495                         mTouchMode = oldTouchMode;
6496                         return true;
6497                     }
6498                 }
6499             }
6500         }
6501         return mWebViewPrivate.super_onGenericMotionEvent(event);
6502     }
6503 
6504     private long mTrackballFirstTime = 0;
6505     private long mTrackballLastTime = 0;
6506     private float mTrackballRemainsX = 0.0f;
6507     private float mTrackballRemainsY = 0.0f;
6508     private int mTrackballXMove = 0;
6509     private int mTrackballYMove = 0;
6510     private boolean mSelectingText = false;
6511     private boolean mShowTextSelectionExtra = false;
6512     private boolean mSelectionStarted = false;
6513     private static final int TRACKBALL_KEY_TIMEOUT = 1000;
6514     private static final int TRACKBALL_TIMEOUT = 200;
6515     private static final int TRACKBALL_WAIT = 100;
6516     private static final int TRACKBALL_SCALE = 400;
6517     private static final int TRACKBALL_SCROLL_COUNT = 5;
6518     private static final int TRACKBALL_MOVE_COUNT = 10;
6519     private static final int TRACKBALL_MULTIPLIER = 3;
6520     private static final int SELECT_CURSOR_OFFSET = 16;
6521     private static final int SELECT_SCROLL = 5;
6522     private int mSelectX = 0;
6523     private int mSelectY = 0;
6524     private boolean mTrackballDown = false;
6525     private long mTrackballUpTime = 0;
6526     private long mLastCursorTime = 0;
6527     private Rect mLastCursorBounds;
6528     private SelectionHandleAlpha mBaseAlpha = new SelectionHandleAlpha();
6529     private SelectionHandleAlpha mExtentAlpha = new SelectionHandleAlpha();
6530     private ObjectAnimator mBaseHandleAlphaAnimator =
6531             ObjectAnimator.ofInt(mBaseAlpha, "alpha", 0);
6532     private ObjectAnimator mExtentHandleAlphaAnimator =
6533             ObjectAnimator.ofInt(mExtentAlpha, "alpha", 0);
6534 
6535     // Set by default; BrowserActivity clears to interpret trackball data
6536     // directly for movement. Currently, the framework only passes
6537     // arrow key events, not trackball events, from one child to the next
6538     private boolean mMapTrackballToArrowKeys = true;
6539 
6540     private DrawData mDelaySetPicture;
6541     private DrawData mLoadedPicture;
6542 
6543     @Override
setMapTrackballToArrowKeys(boolean setMap)6544     public void setMapTrackballToArrowKeys(boolean setMap) {
6545         mMapTrackballToArrowKeys = setMap;
6546     }
6547 
resetTrackballTime()6548     void resetTrackballTime() {
6549         mTrackballLastTime = 0;
6550     }
6551 
6552     @Override
onTrackballEvent(MotionEvent ev)6553     public boolean onTrackballEvent(MotionEvent ev) {
6554         long time = ev.getEventTime();
6555         if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
6556             if (ev.getY() > 0) pageDown(true);
6557             if (ev.getY() < 0) pageUp(true);
6558             return true;
6559         }
6560         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
6561             if (mSelectingText) {
6562                 return true; // discard press if copy in progress
6563             }
6564             mTrackballDown = true;
6565             if (mNativeClass == 0) {
6566                 return false;
6567             }
6568             if (DebugFlags.WEB_VIEW) {
6569                 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
6570                         + " time=" + time
6571                         + " mLastCursorTime=" + mLastCursorTime);
6572             }
6573             if (mWebView.isInTouchMode()) mWebView.requestFocusFromTouch();
6574             return false; // let common code in onKeyDown at it
6575         }
6576         if (ev.getAction() == MotionEvent.ACTION_UP) {
6577             // LONG_PRESS_CENTER is set in common onKeyDown
6578             mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
6579             mTrackballDown = false;
6580             mTrackballUpTime = time;
6581             if (mSelectingText) {
6582                 copySelection();
6583                 selectionDone();
6584                 return true; // discard press if copy in progress
6585             }
6586             if (DebugFlags.WEB_VIEW) {
6587                 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
6588                         + " time=" + time
6589                 );
6590             }
6591             return false; // let common code in onKeyUp at it
6592         }
6593         if ((mMapTrackballToArrowKeys && (ev.getMetaState() & KeyEvent.META_SHIFT_ON) == 0) ||
6594                 AccessibilityManager.getInstance(mContext).isEnabled()) {
6595             if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
6596             return false;
6597         }
6598         if (mTrackballDown) {
6599             if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
6600             return true; // discard move if trackball is down
6601         }
6602         if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
6603             if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
6604             return true;
6605         }
6606         // TODO: alternatively we can do panning as touch does
6607         switchOutDrawHistory();
6608         if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
6609             if (DebugFlags.WEB_VIEW) {
6610                 Log.v(LOGTAG, "onTrackballEvent time="
6611                         + time + " last=" + mTrackballLastTime);
6612             }
6613             mTrackballFirstTime = time;
6614             mTrackballXMove = mTrackballYMove = 0;
6615         }
6616         mTrackballLastTime = time;
6617         if (DebugFlags.WEB_VIEW) {
6618             Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
6619         }
6620         mTrackballRemainsX += ev.getX();
6621         mTrackballRemainsY += ev.getY();
6622         doTrackball(time, ev.getMetaState());
6623         return true;
6624     }
6625 
scaleTrackballX(float xRate, int width)6626     private int scaleTrackballX(float xRate, int width) {
6627         int xMove = (int) (xRate / TRACKBALL_SCALE * width);
6628         int nextXMove = xMove;
6629         if (xMove > 0) {
6630             if (xMove > mTrackballXMove) {
6631                 xMove -= mTrackballXMove;
6632             }
6633         } else if (xMove < mTrackballXMove) {
6634             xMove -= mTrackballXMove;
6635         }
6636         mTrackballXMove = nextXMove;
6637         return xMove;
6638     }
6639 
scaleTrackballY(float yRate, int height)6640     private int scaleTrackballY(float yRate, int height) {
6641         int yMove = (int) (yRate / TRACKBALL_SCALE * height);
6642         int nextYMove = yMove;
6643         if (yMove > 0) {
6644             if (yMove > mTrackballYMove) {
6645                 yMove -= mTrackballYMove;
6646             }
6647         } else if (yMove < mTrackballYMove) {
6648             yMove -= mTrackballYMove;
6649         }
6650         mTrackballYMove = nextYMove;
6651         return yMove;
6652     }
6653 
keyCodeToSoundsEffect(int keyCode)6654     private int keyCodeToSoundsEffect(int keyCode) {
6655         switch(keyCode) {
6656             case KeyEvent.KEYCODE_DPAD_UP:
6657                 return SoundEffectConstants.NAVIGATION_UP;
6658             case KeyEvent.KEYCODE_DPAD_RIGHT:
6659                 return SoundEffectConstants.NAVIGATION_RIGHT;
6660             case KeyEvent.KEYCODE_DPAD_DOWN:
6661                 return SoundEffectConstants.NAVIGATION_DOWN;
6662             case KeyEvent.KEYCODE_DPAD_LEFT:
6663                 return SoundEffectConstants.NAVIGATION_LEFT;
6664         }
6665         return 0;
6666     }
6667 
doTrackball(long time, int metaState)6668     private void doTrackball(long time, int metaState) {
6669         int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
6670         if (elapsed == 0) {
6671             elapsed = TRACKBALL_TIMEOUT;
6672         }
6673         float xRate = mTrackballRemainsX * 1000 / elapsed;
6674         float yRate = mTrackballRemainsY * 1000 / elapsed;
6675         int viewWidth = getViewWidth();
6676         int viewHeight = getViewHeight();
6677         float ax = Math.abs(xRate);
6678         float ay = Math.abs(yRate);
6679         float maxA = Math.max(ax, ay);
6680         if (DebugFlags.WEB_VIEW) {
6681             Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
6682                     + " xRate=" + xRate
6683                     + " yRate=" + yRate
6684                     + " mTrackballRemainsX=" + mTrackballRemainsX
6685                     + " mTrackballRemainsY=" + mTrackballRemainsY);
6686         }
6687         int width = mContentWidth - viewWidth;
6688         int height = mContentHeight - viewHeight;
6689         if (width < 0) width = 0;
6690         if (height < 0) height = 0;
6691         ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
6692         ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
6693         maxA = Math.max(ax, ay);
6694         int count = Math.max(0, (int) maxA);
6695         int oldScrollX = getScrollX();
6696         int oldScrollY = getScrollY();
6697         if (count > 0) {
6698             int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
6699                     KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
6700                     mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
6701                     KeyEvent.KEYCODE_DPAD_RIGHT;
6702             count = Math.min(count, TRACKBALL_MOVE_COUNT);
6703             if (DebugFlags.WEB_VIEW) {
6704                 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
6705                         + " count=" + count
6706                         + " mTrackballRemainsX=" + mTrackballRemainsX
6707                         + " mTrackballRemainsY=" + mTrackballRemainsY);
6708             }
6709             if (mNativeClass != 0) {
6710                 for (int i = 0; i < count; i++) {
6711                     letPageHandleNavKey(selectKeyCode, time, true, metaState);
6712                 }
6713                 letPageHandleNavKey(selectKeyCode, time, false, metaState);
6714             }
6715             mTrackballRemainsX = mTrackballRemainsY = 0;
6716         }
6717         if (count >= TRACKBALL_SCROLL_COUNT) {
6718             int xMove = scaleTrackballX(xRate, width);
6719             int yMove = scaleTrackballY(yRate, height);
6720             if (DebugFlags.WEB_VIEW) {
6721                 Log.v(LOGTAG, "doTrackball pinScrollBy"
6722                         + " count=" + count
6723                         + " xMove=" + xMove + " yMove=" + yMove
6724                         + " mScrollX-oldScrollX=" + (getScrollX()-oldScrollX)
6725                         + " mScrollY-oldScrollY=" + (getScrollY()-oldScrollY)
6726                         );
6727             }
6728             if (Math.abs(getScrollX() - oldScrollX) > Math.abs(xMove)) {
6729                 xMove = 0;
6730             }
6731             if (Math.abs(getScrollY() - oldScrollY) > Math.abs(yMove)) {
6732                 yMove = 0;
6733             }
6734             if (xMove != 0 || yMove != 0) {
6735                 pinScrollBy(xMove, yMove, true, 0);
6736             }
6737         }
6738     }
6739 
6740     /**
6741      * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}.
6742      * @return Maximum horizontal scroll position within real content
6743      */
6744     int computeMaxScrollX() {
6745         return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
6746     }
6747 
6748     /**
6749      * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}.
6750      * @return Maximum vertical scroll position within real content
6751      */
6752     int computeMaxScrollY() {
6753         return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
6754                 - getViewHeightWithTitle(), 0);
6755     }
6756 
6757     boolean updateScrollCoordinates(int x, int y) {
6758         int oldX = getScrollX();
6759         int oldY = getScrollY();
6760         setScrollXRaw(x);
6761         setScrollYRaw(y);
6762         if (oldX != getScrollX() || oldY != getScrollY()) {
6763             mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);
6764             return true;
6765         } else {
6766             return false;
6767         }
6768     }
6769 
6770     @Override
6771     public void flingScroll(int vx, int vy) {
6772         mScroller.fling(getScrollX(), getScrollY(), vx, vy, 0, computeMaxScrollX(), 0,
6773                 computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
6774         invalidate();
6775     }
6776 
6777     private void doFling() {
6778         if (mVelocityTracker == null) {
6779             return;
6780         }
6781         int maxX = computeMaxScrollX();
6782         int maxY = computeMaxScrollY();
6783 
6784         mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
6785         int vx = (int) mVelocityTracker.getXVelocity();
6786         int vy = (int) mVelocityTracker.getYVelocity();
6787 
6788         int scrollX = getScrollX();
6789         int scrollY = getScrollY();
6790         int overscrollDistance = mOverscrollDistance;
6791         int overflingDistance = mOverflingDistance;
6792 
6793         // Use the layer's scroll data if applicable.
6794         if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
6795             scrollX = mScrollingLayerRect.left;
6796             scrollY = mScrollingLayerRect.top;
6797             maxX = mScrollingLayerRect.right;
6798             maxY = mScrollingLayerRect.bottom;
6799             // No overscrolling for layers.
6800             overscrollDistance = overflingDistance = 0;
6801         } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
6802             scrollX = getTextScrollX();
6803             scrollY = getTextScrollY();
6804             maxX = getMaxTextScrollX();
6805             maxY = getMaxTextScrollY();
6806             // No overscrolling for edit text.
6807             overscrollDistance = overflingDistance = 0;
6808         }
6809 
6810         if (mSnapScrollMode != SNAP_NONE) {
6811             if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
6812                 vy = 0;
6813             } else {
6814                 vx = 0;
6815             }
6816         }
6817         if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
6818             WebViewCore.resumePriority();
6819             if (!mSelectingText) {
6820                 WebViewCore.resumeUpdatePicture(mWebViewCore);
6821             }
6822             if (mScroller.springBack(scrollX, scrollY, 0, maxX, 0, maxY)) {
6823                 invalidate();
6824             }
6825             return;
6826         }
6827         float currentVelocity = mScroller.getCurrVelocity();
6828         float velocity = (float) Math.hypot(vx, vy);
6829         if (mLastVelocity > 0 && currentVelocity > 0 && velocity
6830                 > mLastVelocity * MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION) {
6831             float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
6832                     - Math.atan2(vy, vx)));
6833             final float circle = (float) (Math.PI) * 2.0f;
6834             if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
6835                 vx += currentVelocity * mLastVelX / mLastVelocity;
6836                 vy += currentVelocity * mLastVelY / mLastVelocity;
6837                 velocity = (float) Math.hypot(vx, vy);
6838                 if (DebugFlags.WEB_VIEW) {
6839                     Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
6840                 }
6841             } else if (DebugFlags.WEB_VIEW) {
6842                 Log.v(LOGTAG, "doFling missed " + deltaR / circle);
6843             }
6844         } else if (DebugFlags.WEB_VIEW) {
6845             Log.v(LOGTAG, "doFling start last=" + mLastVelocity
6846                     + " current=" + currentVelocity
6847                     + " vx=" + vx + " vy=" + vy
6848                     + " maxX=" + maxX + " maxY=" + maxY
6849                     + " scrollX=" + scrollX + " scrollY=" + scrollY
6850                     + " layer=" + mCurrentScrollingLayerId);
6851         }
6852 
6853         // Allow sloppy flings without overscrolling at the edges.
6854         if ((scrollX == 0 || scrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
6855             vx = 0;
6856         }
6857         if ((scrollY == 0 || scrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
6858             vy = 0;
6859         }
6860 
6861         if (overscrollDistance < overflingDistance) {
6862             if ((vx > 0 && scrollX == -overscrollDistance) ||
6863                     (vx < 0 && scrollX == maxX + overscrollDistance)) {
6864                 vx = 0;
6865             }
6866             if ((vy > 0 && scrollY == -overscrollDistance) ||
6867                     (vy < 0 && scrollY == maxY + overscrollDistance)) {
6868                 vy = 0;
6869             }
6870         }
6871 
6872         mLastVelX = vx;
6873         mLastVelY = vy;
6874         mLastVelocity = velocity;
6875 
6876         // no horizontal overscroll if the content just fits
6877         mScroller.fling(scrollX, scrollY, -vx, -vy, 0, maxX, 0, maxY,
6878                 maxX == 0 ? 0 : overflingDistance, overflingDistance);
6879 
6880         invalidate();
6881     }
6882 
6883     /**
6884      * See {@link WebView#getZoomControls()}
6885      */
6886     @Override
6887     @Deprecated
getZoomControls()6888     public View getZoomControls() {
6889         if (!getSettings().supportZoom()) {
6890             Log.w(LOGTAG, "This WebView doesn't support zoom.");
6891             return null;
6892         }
6893         return mZoomManager.getExternalZoomPicker();
6894     }
6895 
dismissZoomControl()6896     void dismissZoomControl() {
6897         mZoomManager.dismissZoomPicker();
6898     }
6899 
getDefaultZoomScale()6900     float getDefaultZoomScale() {
6901         return mZoomManager.getDefaultScale();
6902     }
6903 
6904     /**
6905      * Return the overview scale of the WebView
6906      * @return The overview scale.
6907      */
getZoomOverviewScale()6908     float getZoomOverviewScale() {
6909         return mZoomManager.getZoomOverviewScale();
6910     }
6911 
6912     /**
6913      * See {@link WebView#canZoomIn()}
6914      */
6915     @Override
canZoomIn()6916     public boolean canZoomIn() {
6917         return mZoomManager.canZoomIn();
6918     }
6919 
6920     /**
6921      * See {@link WebView#canZoomOut()}
6922      */
6923     @Override
canZoomOut()6924     public boolean canZoomOut() {
6925         return mZoomManager.canZoomOut();
6926     }
6927 
6928     /**
6929      * See {@link WebView#zoomIn()}
6930      */
6931     @Override
zoomIn()6932     public boolean zoomIn() {
6933         return mZoomManager.zoomIn();
6934     }
6935 
6936     /**
6937      * See {@link WebView#zoomOut()}
6938      */
6939     @Override
zoomOut()6940     public boolean zoomOut() {
6941         return mZoomManager.zoomOut();
6942     }
6943 
6944     /*
6945      * Return true if the rect (e.g. plugin) is fully visible and maximized
6946      * inside the WebView.
6947      */
isRectFitOnScreen(Rect rect)6948     boolean isRectFitOnScreen(Rect rect) {
6949         final int rectWidth = rect.width();
6950         final int rectHeight = rect.height();
6951         final int viewWidth = getViewWidth();
6952         final int viewHeight = getViewHeightWithTitle();
6953         float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight / rectHeight);
6954         scale = mZoomManager.computeScaleWithLimits(scale);
6955         return !mZoomManager.willScaleTriggerZoom(scale)
6956                 && contentToViewX(rect.left) >= getScrollX()
6957                 && contentToViewX(rect.right) <= getScrollX() + viewWidth
6958                 && contentToViewY(rect.top) >= getScrollY()
6959                 && contentToViewY(rect.bottom) <= getScrollY() + viewHeight;
6960     }
6961 
6962     /*
6963      * Maximize and center the rectangle, specified in the document coordinate
6964      * space, inside the WebView. If the zoom doesn't need to be changed, do an
6965      * animated scroll to center it. If the zoom needs to be changed, find the
6966      * zoom center and do a smooth zoom transition. The rect is in document
6967      * coordinates
6968      */
centerFitRect(Rect rect)6969     void centerFitRect(Rect rect) {
6970         final int rectWidth = rect.width();
6971         final int rectHeight = rect.height();
6972         final int viewWidth = getViewWidth();
6973         final int viewHeight = getViewHeightWithTitle();
6974         float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight
6975                 / rectHeight);
6976         scale = mZoomManager.computeScaleWithLimits(scale);
6977         if (!mZoomManager.willScaleTriggerZoom(scale)) {
6978             pinScrollTo(contentToViewX(rect.left + rectWidth / 2) - viewWidth / 2,
6979                     contentToViewY(rect.top + rectHeight / 2) - viewHeight / 2,
6980                     true, 0);
6981         } else {
6982             float actualScale = mZoomManager.getScale();
6983             float oldScreenX = rect.left * actualScale - getScrollX();
6984             float rectViewX = rect.left * scale;
6985             float rectViewWidth = rectWidth * scale;
6986             float newMaxWidth = mContentWidth * scale;
6987             float newScreenX = (viewWidth - rectViewWidth) / 2;
6988             // pin the newX to the WebView
6989             if (newScreenX > rectViewX) {
6990                 newScreenX = rectViewX;
6991             } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) {
6992                 newScreenX = viewWidth - (newMaxWidth - rectViewX);
6993             }
6994             float zoomCenterX = (oldScreenX * scale - newScreenX * actualScale)
6995                     / (scale - actualScale);
6996             float oldScreenY = rect.top * actualScale + getTitleHeight()
6997                     - getScrollY();
6998             float rectViewY = rect.top * scale + getTitleHeight();
6999             float rectViewHeight = rectHeight * scale;
7000             float newMaxHeight = mContentHeight * scale + getTitleHeight();
7001             float newScreenY = (viewHeight - rectViewHeight) / 2;
7002             // pin the newY to the WebView
7003             if (newScreenY > rectViewY) {
7004                 newScreenY = rectViewY;
7005             } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) {
7006                 newScreenY = viewHeight - (newMaxHeight - rectViewY);
7007             }
7008             float zoomCenterY = (oldScreenY * scale - newScreenY * actualScale)
7009                     / (scale - actualScale);
7010             mZoomManager.setZoomCenter(zoomCenterX, zoomCenterY);
7011             mZoomManager.startZoomAnimation(scale, false);
7012         }
7013     }
7014 
7015     // Called by JNI to handle a touch on a node representing an email address,
7016     // address, or phone number
overrideLoading(String url)7017     private void overrideLoading(String url) {
7018         mCallbackProxy.uiOverrideUrlLoading(url);
7019     }
7020 
7021     @Override
requestFocus(int direction, Rect previouslyFocusedRect)7022     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
7023         // Check if we are destroyed
7024         if (mWebViewCore == null) return false;
7025         // FIXME: If a subwindow is showing find, and the user touches the
7026         // background window, it can steal focus.
7027         if (mFindIsUp) return false;
7028         boolean result = false;
7029         result = mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
7030         if (mWebViewCore.getSettings().getNeedInitialFocus()
7031                 && !mWebView.isInTouchMode()) {
7032             // For cases such as GMail, where we gain focus from a direction,
7033             // we want to move to the first available link.
7034             // FIXME: If there are no visible links, we may not want to
7035             int fakeKeyDirection = 0;
7036             switch(direction) {
7037                 case View.FOCUS_UP:
7038                     fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
7039                     break;
7040                 case View.FOCUS_DOWN:
7041                     fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
7042                     break;
7043                 case View.FOCUS_LEFT:
7044                     fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
7045                     break;
7046                 case View.FOCUS_RIGHT:
7047                     fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
7048                     break;
7049                 default:
7050                     return result;
7051             }
7052             mWebViewCore.sendMessage(EventHub.SET_INITIAL_FOCUS, fakeKeyDirection);
7053         }
7054         return result;
7055     }
7056 
7057     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)7058     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
7059         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
7060         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
7061         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
7062         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
7063 
7064         int measuredHeight = heightSize;
7065         int measuredWidth = widthSize;
7066 
7067         // Grab the content size from WebViewCore.
7068         int contentHeight = contentToViewDimension(mContentHeight);
7069         int contentWidth = contentToViewDimension(mContentWidth);
7070 
7071 //        Log.d(LOGTAG, "------- measure " + heightMode);
7072 
7073         if (heightMode != MeasureSpec.EXACTLY) {
7074             mHeightCanMeasure = true;
7075             measuredHeight = contentHeight;
7076             if (heightMode == MeasureSpec.AT_MOST) {
7077                 // If we are larger than the AT_MOST height, then our height can
7078                 // no longer be measured and we should scroll internally.
7079                 if (measuredHeight > heightSize) {
7080                     measuredHeight = heightSize;
7081                     mHeightCanMeasure = false;
7082                     measuredHeight |= View.MEASURED_STATE_TOO_SMALL;
7083                 }
7084             }
7085         } else {
7086             mHeightCanMeasure = false;
7087         }
7088         if (mNativeClass != 0) {
7089             nativeSetHeightCanMeasure(mHeightCanMeasure);
7090         }
7091         // For the width, always use the given size unless unspecified.
7092         if (widthMode == MeasureSpec.UNSPECIFIED) {
7093             mWidthCanMeasure = true;
7094             measuredWidth = contentWidth;
7095         } else {
7096             if (measuredWidth < contentWidth) {
7097                 measuredWidth |= View.MEASURED_STATE_TOO_SMALL;
7098             }
7099             mWidthCanMeasure = false;
7100         }
7101 
7102         synchronized (this) {
7103             mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
7104         }
7105     }
7106 
7107     @Override
requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)7108     public boolean requestChildRectangleOnScreen(View child,
7109                                                  Rect rect,
7110                                                  boolean immediate) {
7111         if (mNativeClass == 0) {
7112             return false;
7113         }
7114         // don't scroll while in zoom animation. When it is done, we will adjust
7115         // the necessary components
7116         if (mZoomManager.isFixedLengthAnimationInProgress()) {
7117             return false;
7118         }
7119 
7120         rect.offset(child.getLeft() - child.getScrollX(),
7121                 child.getTop() - child.getScrollY());
7122 
7123         Rect content = new Rect(viewToContentX(getScrollX()),
7124                 viewToContentY(getScrollY()),
7125                 viewToContentX(getScrollX() + getWidth()
7126                 - mWebView.getVerticalScrollbarWidth()),
7127                 viewToContentY(getScrollY() + getViewHeightWithTitle()));
7128         int screenTop = contentToViewY(content.top);
7129         int screenBottom = contentToViewY(content.bottom);
7130         int height = screenBottom - screenTop;
7131         int scrollYDelta = 0;
7132 
7133         if (rect.bottom > screenBottom) {
7134             int oneThirdOfScreenHeight = height / 3;
7135             if (rect.height() > 2 * oneThirdOfScreenHeight) {
7136                 // If the rectangle is too tall to fit in the bottom two thirds
7137                 // of the screen, place it at the top.
7138                 scrollYDelta = rect.top - screenTop;
7139             } else {
7140                 // If the rectangle will still fit on screen, we want its
7141                 // top to be in the top third of the screen.
7142                 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
7143             }
7144         } else if (rect.top < screenTop) {
7145             scrollYDelta = rect.top - screenTop;
7146         }
7147 
7148         int screenLeft = contentToViewX(content.left);
7149         int screenRight = contentToViewX(content.right);
7150         int width = screenRight - screenLeft;
7151         int scrollXDelta = 0;
7152 
7153         if (rect.right > screenRight && rect.left > screenLeft) {
7154             if (rect.width() > width) {
7155                 scrollXDelta += (rect.left - screenLeft);
7156             } else {
7157                 scrollXDelta += (rect.right - screenRight);
7158             }
7159         } else if (rect.left < screenLeft) {
7160             scrollXDelta -= (screenLeft - rect.left);
7161         }
7162 
7163         if ((scrollYDelta | scrollXDelta) != 0) {
7164             return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
7165         }
7166 
7167         return false;
7168     }
7169 
replaceTextfieldText(int oldStart, int oldEnd, String replace, int newStart, int newEnd)7170     /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
7171             String replace, int newStart, int newEnd) {
7172         WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
7173         arg.mReplace = replace;
7174         arg.mNewStart = newStart;
7175         arg.mNewEnd = newEnd;
7176         mTextGeneration++;
7177         arg.mTextGeneration = mTextGeneration;
7178         sendBatchableInputMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
7179     }
7180 
passToJavaScript(String currentText, KeyEvent event)7181     /* package */ void passToJavaScript(String currentText, KeyEvent event) {
7182         // check if mWebViewCore has been destroyed
7183         if (mWebViewCore == null) {
7184             return;
7185         }
7186         WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
7187         arg.mEvent = event;
7188         arg.mCurrentText = currentText;
7189         // Increase our text generation number, and pass it to webcore thread
7190         mTextGeneration++;
7191         mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
7192         // WebKit's document state is not saved until about to leave the page.
7193         // To make sure the host application, like Browser, has the up to date
7194         // document state when it goes to background, we force to save the
7195         // document state.
7196         mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
7197         mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE, null, 1000);
7198     }
7199 
getWebViewCore()7200     public synchronized WebViewCore getWebViewCore() {
7201         return mWebViewCore;
7202     }
7203 
canTextScroll(int directionX, int directionY)7204     private boolean canTextScroll(int directionX, int directionY) {
7205         int scrollX = getTextScrollX();
7206         int scrollY = getTextScrollY();
7207         int maxScrollX = getMaxTextScrollX();
7208         int maxScrollY = getMaxTextScrollY();
7209         boolean canScrollX = (directionX > 0)
7210                 ? (scrollX < maxScrollX)
7211                 : (scrollX > 0);
7212         boolean canScrollY = (directionY > 0)
7213                 ? (scrollY < maxScrollY)
7214                 : (scrollY > 0);
7215         return canScrollX || canScrollY;
7216     }
7217 
getTextScrollX()7218     private int getTextScrollX() {
7219         return -mEditTextContent.left;
7220     }
7221 
getTextScrollY()7222     private int getTextScrollY() {
7223         return -mEditTextContent.top;
7224     }
7225 
getMaxTextScrollX()7226     private int getMaxTextScrollX() {
7227         return Math.max(0, mEditTextContent.width() - mEditTextContentBounds.width());
7228     }
7229 
getMaxTextScrollY()7230     private int getMaxTextScrollY() {
7231         return Math.max(0, mEditTextContent.height() - mEditTextContentBounds.height());
7232     }
7233 
7234     //-------------------------------------------------------------------------
7235     // Methods can be called from a separate thread, like WebViewCore
7236     // If it needs to call the View system, it has to send message.
7237     //-------------------------------------------------------------------------
7238 
7239     /**
7240      * General handler to receive message coming from webkit thread
7241      */
7242     class PrivateHandler extends Handler implements WebViewInputDispatcher.UiCallbacks {
7243         @Override
handleMessage(Message msg)7244         public void handleMessage(Message msg) {
7245             // exclude INVAL_RECT_MSG_ID since it is frequently output
7246             if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
7247                 if (msg.what >= FIRST_PRIVATE_MSG_ID
7248                         && msg.what <= LAST_PRIVATE_MSG_ID) {
7249                     Log.v(LOGTAG, HandlerPrivateDebugString[msg.what
7250                             - FIRST_PRIVATE_MSG_ID]);
7251                 } else if (msg.what >= FIRST_PACKAGE_MSG_ID
7252                         && msg.what <= LAST_PACKAGE_MSG_ID) {
7253                     Log.v(LOGTAG, HandlerPackageDebugString[msg.what
7254                             - FIRST_PACKAGE_MSG_ID]);
7255                 } else {
7256                     Log.v(LOGTAG, Integer.toString(msg.what));
7257                 }
7258             }
7259             if (mWebViewCore == null) {
7260                 // after WebView's destroy() is called, skip handling messages.
7261                 return;
7262             }
7263             if (mBlockWebkitViewMessages
7264                     && msg.what != WEBCORE_INITIALIZED_MSG_ID) {
7265                 // Blocking messages from webkit
7266                 return;
7267             }
7268             switch (msg.what) {
7269                 case REMEMBER_PASSWORD: {
7270                     mDatabase.setUsernamePassword(
7271                             msg.getData().getString("host"),
7272                             msg.getData().getString("username"),
7273                             msg.getData().getString("password"));
7274                     ((Message) msg.obj).sendToTarget();
7275                     break;
7276                 }
7277                 case NEVER_REMEMBER_PASSWORD: {
7278                     mDatabase.setUsernamePassword(msg.getData().getString("host"), null, null);
7279                     ((Message) msg.obj).sendToTarget();
7280                     break;
7281                 }
7282                 case SCROLL_SELECT_TEXT: {
7283                     if (mAutoScrollX == 0 && mAutoScrollY == 0) {
7284                         mSentAutoScrollMessage = false;
7285                         break;
7286                     }
7287                     if (mCurrentScrollingLayerId == 0) {
7288                         pinScrollBy(mAutoScrollX, mAutoScrollY, true, 0);
7289                     } else {
7290                         scrollLayerTo(mScrollingLayerRect.left + mAutoScrollX,
7291                                 mScrollingLayerRect.top + mAutoScrollY);
7292                     }
7293                     sendEmptyMessageDelayed(
7294                             SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
7295                     break;
7296                 }
7297                 case SCROLL_TO_MSG_ID: {
7298                     // arg1 = animate, arg2 = onlyIfImeIsShowing
7299                     // obj = Point(x, y)
7300                     if (msg.arg2 == 1) {
7301                         // This scroll is intended to bring the textfield into
7302                         // view, but is only necessary if the IME is showing
7303                         InputMethodManager imm = InputMethodManager.peekInstance();
7304                         if (imm == null || !imm.isAcceptingText()
7305                                 || !imm.isActive(mWebView)) {
7306                             break;
7307                         }
7308                     }
7309                     final Point p = (Point) msg.obj;
7310                     contentScrollTo(p.x, p.y, msg.arg1 == 1);
7311                     break;
7312                 }
7313                 case UPDATE_ZOOM_RANGE: {
7314                     WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj;
7315                     // mScrollX contains the new minPrefWidth
7316                     mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
7317                     break;
7318                 }
7319                 case UPDATE_ZOOM_DENSITY: {
7320                     final float density = (Float) msg.obj;
7321                     mZoomManager.updateDefaultZoomDensity(density);
7322                     break;
7323                 }
7324                 case NEW_PICTURE_MSG_ID: {
7325                     // called for new content
7326                     final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
7327                     setNewPicture(draw, true);
7328                     break;
7329                 }
7330                 case WEBCORE_INITIALIZED_MSG_ID:
7331                     // nativeCreate sets mNativeClass to a non-zero value
7332                     String drawableDir = BrowserFrame.getRawResFilename(
7333                             BrowserFrame.DRAWABLEDIR, mContext);
7334                     nativeCreate(msg.arg1, drawableDir, ActivityManager.isHighEndGfx());
7335                     if (mDelaySetPicture != null) {
7336                         setNewPicture(mDelaySetPicture, true);
7337                         mDelaySetPicture = null;
7338                     }
7339                     if (mIsPaused) {
7340                         nativeSetPauseDrawing(mNativeClass, true);
7341                     }
7342                     mInputDispatcher = new WebViewInputDispatcher(this,
7343                             mWebViewCore.getInputDispatcherCallbacks());
7344                     break;
7345                 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
7346                     // Make sure that the textfield is currently focused
7347                     // and representing the same node as the pointer.
7348                     if (msg.arg2 == mTextGeneration) {
7349                         String text = (String) msg.obj;
7350                         if (null == text) {
7351                             text = "";
7352                         }
7353                         if (mInputConnection != null &&
7354                                 mFieldPointer == msg.arg1) {
7355                             mInputConnection.setTextAndKeepSelection(text);
7356                         }
7357                     }
7358                     break;
7359                 case UPDATE_TEXT_SELECTION_MSG_ID:
7360                     updateTextSelectionFromMessage(msg.arg1, msg.arg2,
7361                             (WebViewCore.TextSelectionData) msg.obj);
7362                     break;
7363                 case TAKE_FOCUS:
7364                     int direction = msg.arg1;
7365                     View focusSearch = mWebView.focusSearch(direction);
7366                     if (focusSearch != null && focusSearch != mWebView) {
7367                         focusSearch.requestFocus();
7368                     }
7369                     break;
7370                 case CLEAR_TEXT_ENTRY:
7371                     hideSoftKeyboard();
7372                     break;
7373                 case INVAL_RECT_MSG_ID: {
7374                     Rect r = (Rect)msg.obj;
7375                     if (r == null) {
7376                         invalidate();
7377                     } else {
7378                         // we need to scale r from content into view coords,
7379                         // which viewInvalidate() does for us
7380                         viewInvalidate(r.left, r.top, r.right, r.bottom);
7381                     }
7382                     break;
7383                 }
7384                 case REQUEST_FORM_DATA:
7385                     if (mFieldPointer == msg.arg1) {
7386                         ArrayAdapter<String> adapter = (ArrayAdapter<String>)msg.obj;
7387                         mAutoCompletePopup.setAdapter(adapter);
7388                     }
7389                     break;
7390 
7391                 case LONG_PRESS_CENTER:
7392                     // as this is shared by keydown and trackballdown, reset all
7393                     // the states
7394                     mGotCenterDown = false;
7395                     mTrackballDown = false;
7396                     mWebView.performLongClick();
7397                     break;
7398 
7399                 case WEBCORE_NEED_TOUCH_EVENTS:
7400                     mInputDispatcher.setWebKitWantsTouchEvents(msg.arg1 != 0);
7401                     break;
7402 
7403                 case REQUEST_KEYBOARD:
7404                     if (msg.arg1 == 0) {
7405                         hideSoftKeyboard();
7406                     } else {
7407                         displaySoftKeyboard(false);
7408                     }
7409                     break;
7410 
7411                 case DRAG_HELD_MOTIONLESS:
7412                     mHeldMotionless = MOTIONLESS_TRUE;
7413                     invalidate();
7414                     break;
7415 
7416                 case SCREEN_ON:
7417                     mWebView.setKeepScreenOn(msg.arg1 == 1);
7418                     break;
7419 
7420                 case EXIT_FULLSCREEN_VIDEO:
7421                     if (mHTML5VideoViewProxy != null) {
7422                         mHTML5VideoViewProxy.exitFullScreenVideo();
7423                     }
7424                     break;
7425 
7426                 case SHOW_FULLSCREEN: {
7427                     View view = (View) msg.obj;
7428                     int orientation = msg.arg1;
7429                     int npp = msg.arg2;
7430 
7431                     if (inFullScreenMode()) {
7432                         Log.w(LOGTAG, "Should not have another full screen.");
7433                         dismissFullScreenMode();
7434                     }
7435                     mFullScreenHolder = new PluginFullScreenHolder(WebViewClassic.this, orientation, npp);
7436                     mFullScreenHolder.setContentView(view);
7437                     mFullScreenHolder.show();
7438                     invalidate();
7439 
7440                     break;
7441                 }
7442                 case HIDE_FULLSCREEN:
7443                     dismissFullScreenMode();
7444                     break;
7445 
7446                 case SHOW_RECT_MSG_ID: {
7447                     WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
7448                     int left = contentToViewX(data.mLeft);
7449                     int width = contentToViewDimension(data.mWidth);
7450                     int maxWidth = contentToViewDimension(data.mContentWidth);
7451                     int viewWidth = getViewWidth();
7452                     int x = (int) (left + data.mXPercentInDoc * width -
7453                                    data.mXPercentInView * viewWidth);
7454                     if (DebugFlags.WEB_VIEW) {
7455                         Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" +
7456                               width + ",maxWidth=" + maxWidth +
7457                               ",viewWidth=" + viewWidth + ",x="
7458                               + x + ",xPercentInDoc=" + data.mXPercentInDoc +
7459                               ",xPercentInView=" + data.mXPercentInView+ ")");
7460                     }
7461                     // use the passing content width to cap x as the current
7462                     // mContentWidth may not be updated yet
7463                     x = Math.max(0,
7464                             (Math.min(maxWidth, x + viewWidth)) - viewWidth);
7465                     int top = contentToViewY(data.mTop);
7466                     int height = contentToViewDimension(data.mHeight);
7467                     int maxHeight = contentToViewDimension(data.mContentHeight);
7468                     int viewHeight = getViewHeight();
7469                     int y = (int) (top + data.mYPercentInDoc * height -
7470                                    data.mYPercentInView * viewHeight);
7471                     if (DebugFlags.WEB_VIEW) {
7472                         Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" +
7473                               height + ",maxHeight=" + maxHeight +
7474                               ",viewHeight=" + viewHeight + ",y="
7475                               + y + ",yPercentInDoc=" + data.mYPercentInDoc +
7476                               ",yPercentInView=" + data.mYPercentInView+ ")");
7477                     }
7478                     // use the passing content height to cap y as the current
7479                     // mContentHeight may not be updated yet
7480                     y = Math.max(0,
7481                             (Math.min(maxHeight, y + viewHeight) - viewHeight));
7482                     // We need to take into account the visible title height
7483                     // when scrolling since y is an absolute view position.
7484                     y = Math.max(0, y - getVisibleTitleHeightImpl());
7485                     mWebView.scrollTo(x, y);
7486                     }
7487                     break;
7488 
7489                 case CENTER_FIT_RECT:
7490                     centerFitRect((Rect)msg.obj);
7491                     break;
7492 
7493                 case SET_SCROLLBAR_MODES:
7494                     mHorizontalScrollBarMode = msg.arg1;
7495                     mVerticalScrollBarMode = msg.arg2;
7496                     break;
7497 
7498                 case SELECTION_STRING_CHANGED:
7499                     if (isAccessibilityInjectionEnabled()) {
7500                         getAccessibilityInjector()
7501                                 .handleSelectionChangedIfNecessary((String) msg.obj);
7502                     }
7503                     break;
7504 
7505                 case FOCUS_NODE_CHANGED:
7506                     mIsEditingText = (msg.arg1 == mFieldPointer);
7507                     if (mAutoCompletePopup != null && !mIsEditingText) {
7508                         mAutoCompletePopup.clearAdapter();
7509                     }
7510                     // fall through to HIT_TEST_RESULT
7511                 case HIT_TEST_RESULT:
7512                     WebKitHitTest hit = (WebKitHitTest) msg.obj;
7513                     mFocusedNode = hit;
7514                     setTouchHighlightRects(hit);
7515                     setHitTestResult(hit);
7516                     break;
7517 
7518                 case SAVE_WEBARCHIVE_FINISHED:
7519                     SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj;
7520                     if (saveMessage.mCallback != null) {
7521                         saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile);
7522                     }
7523                     break;
7524 
7525                 case SET_AUTOFILLABLE:
7526                     mAutoFillData = (WebViewCore.AutoFillData) msg.obj;
7527                     if (mInputConnection != null) {
7528                         mInputConnection.setAutoFillable(mAutoFillData.getQueryId());
7529                         mAutoCompletePopup.setAutoFillQueryId(mAutoFillData.getQueryId());
7530                     }
7531                     break;
7532 
7533                 case AUTOFILL_COMPLETE:
7534                     if (mAutoCompletePopup != null) {
7535                         ArrayList<String> pastEntries = new ArrayList<String>();
7536                         mAutoCompletePopup.setAdapter(new ArrayAdapter<String>(
7537                                 mContext,
7538                                 com.android.internal.R.layout.web_text_view_dropdown,
7539                                 pastEntries));
7540                     }
7541                     break;
7542 
7543                 case COPY_TO_CLIPBOARD:
7544                     copyToClipboard((String) msg.obj);
7545                     break;
7546 
7547                 case INIT_EDIT_FIELD:
7548                     if (mInputConnection != null) {
7549                         TextFieldInitData initData = (TextFieldInitData) msg.obj;
7550                         mTextGeneration = 0;
7551                         mFieldPointer = initData.mFieldPointer;
7552                         mInputConnection.initEditorInfo(initData);
7553                         mInputConnection.setTextAndKeepSelection(initData.mText);
7554                         mEditTextContentBounds.set(initData.mContentBounds);
7555                         mEditTextLayerId = initData.mNodeLayerId;
7556                         nativeMapLayerRect(mNativeClass, mEditTextLayerId,
7557                                 mEditTextContentBounds);
7558                         mEditTextContent.set(initData.mClientRect);
7559                         relocateAutoCompletePopup();
7560                     }
7561                     break;
7562 
7563                 case REPLACE_TEXT:{
7564                     String text = (String)msg.obj;
7565                     int start = msg.arg1;
7566                     int end = msg.arg2;
7567                     int cursorPosition = start + text.length();
7568                     replaceTextfieldText(start, end, text,
7569                             cursorPosition, cursorPosition);
7570                     selectionDone();
7571                     break;
7572                 }
7573 
7574                 case UPDATE_MATCH_COUNT: {
7575                     WebViewCore.FindAllRequest request = (WebViewCore.FindAllRequest)msg.obj;
7576                     if (request == null) {
7577                         if (mFindCallback != null) {
7578                             mFindCallback.updateMatchCount(0, 0, true);
7579                         }
7580                     } else if (request == mFindRequest) {
7581                         int matchCount, matchIndex;
7582                         synchronized (mFindRequest) {
7583                             matchCount = request.mMatchCount;
7584                             matchIndex = request.mMatchIndex;
7585                         }
7586                         if (mFindCallback != null) {
7587                             mFindCallback.updateMatchCount(matchIndex, matchCount, false);
7588                         }
7589                         if (mFindListener != null) {
7590                             mFindListener.onFindResultReceived(matchIndex, matchCount, true);
7591                         }
7592                     }
7593                     break;
7594                 }
7595 
7596                 case CLEAR_CARET_HANDLE:
7597                     if (mIsCaretSelection) {
7598                         selectionDone();
7599                     }
7600                     break;
7601 
7602                 case KEY_PRESS:
7603                     sendBatchableInputMessage(EventHub.KEY_PRESS, msg.arg1, 0, null);
7604                     break;
7605 
7606                 case RELOCATE_AUTO_COMPLETE_POPUP:
7607                     relocateAutoCompletePopup();
7608                     break;
7609 
7610                 case AUTOFILL_FORM:
7611                     mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM,
7612                             msg.arg1, /* unused */0);
7613                     break;
7614 
7615                 case EDIT_TEXT_SIZE_CHANGED:
7616                     if (msg.arg1 == mFieldPointer) {
7617                         mEditTextContent.set((Rect)msg.obj);
7618                     }
7619                     break;
7620 
7621                 case SHOW_CARET_HANDLE:
7622                     if (!mSelectingText && mIsEditingText && mIsCaretSelection) {
7623                         setupWebkitSelect();
7624                         resetCaretTimer();
7625                         showPasteWindow();
7626                     }
7627                     break;
7628 
7629                 case UPDATE_CONTENT_BOUNDS:
7630                     mEditTextContentBounds.set((Rect) msg.obj);
7631                     nativeMapLayerRect(mNativeClass, mEditTextLayerId,
7632                             mEditTextContentBounds);
7633                     break;
7634 
7635                 case SCROLL_EDIT_TEXT:
7636                     scrollEditWithCursor();
7637                     break;
7638 
7639                 case SCROLL_HANDLE_INTO_VIEW:
7640                     scrollDraggedSelectionHandleIntoView();
7641                     break;
7642 
7643                 default:
7644                     super.handleMessage(msg);
7645                     break;
7646             }
7647         }
7648 
7649         @Override
getUiLooper()7650         public Looper getUiLooper() {
7651             return getLooper();
7652         }
7653 
7654         @Override
dispatchUiEvent(MotionEvent event, int eventType, int flags)7655         public void dispatchUiEvent(MotionEvent event, int eventType, int flags) {
7656             onHandleUiEvent(event, eventType, flags);
7657         }
7658 
7659         @Override
getContext()7660         public Context getContext() {
7661             return WebViewClassic.this.getContext();
7662         }
7663 
7664         @Override
shouldInterceptTouchEvent(MotionEvent event)7665         public boolean shouldInterceptTouchEvent(MotionEvent event) {
7666             if (!mSelectingText) {
7667                 return false;
7668             }
7669             ensureSelectionHandles();
7670             int y = Math.round(event.getY() - getTitleHeight() + getScrollY());
7671             int x = Math.round(event.getX() + getScrollX());
7672             boolean isPressingHandle;
7673             if (mIsCaretSelection) {
7674                 isPressingHandle = mSelectHandleCenter.getBounds()
7675                         .contains(x, y);
7676             } else {
7677                 isPressingHandle =
7678                         mSelectHandleBaseBounds.contains(x, y)
7679                         || mSelectHandleExtentBounds.contains(x, y);
7680             }
7681             return isPressingHandle;
7682         }
7683 
7684         @Override
showTapHighlight(boolean show)7685         public void showTapHighlight(boolean show) {
7686             if (mShowTapHighlight != show) {
7687                 mShowTapHighlight = show;
7688                 invalidate();
7689             }
7690         }
7691 
7692         @Override
clearPreviousHitTest()7693         public void clearPreviousHitTest() {
7694             setHitTestResult(null);
7695         }
7696     }
7697 
setHitTestTypeFromUrl(String url)7698     private void setHitTestTypeFromUrl(String url) {
7699         String substr = null;
7700         if (url.startsWith(SCHEME_GEO)) {
7701             mInitialHitTestResult.setType(HitTestResult.GEO_TYPE);
7702             substr = url.substring(SCHEME_GEO.length());
7703         } else if (url.startsWith(SCHEME_TEL)) {
7704             mInitialHitTestResult.setType(HitTestResult.PHONE_TYPE);
7705             substr = url.substring(SCHEME_TEL.length());
7706         } else if (url.startsWith(SCHEME_MAILTO)) {
7707             mInitialHitTestResult.setType(HitTestResult.EMAIL_TYPE);
7708             substr = url.substring(SCHEME_MAILTO.length());
7709         } else {
7710             mInitialHitTestResult.setType(HitTestResult.SRC_ANCHOR_TYPE);
7711             mInitialHitTestResult.setExtra(url);
7712             return;
7713         }
7714         try {
7715             mInitialHitTestResult.setExtra(URLDecoder.decode(substr, "UTF-8"));
7716         } catch (Throwable e) {
7717             Log.w(LOGTAG, "Failed to decode URL! " + substr, e);
7718             mInitialHitTestResult.setType(HitTestResult.UNKNOWN_TYPE);
7719         }
7720     }
7721 
setHitTestResult(WebKitHitTest hit)7722     private void setHitTestResult(WebKitHitTest hit) {
7723         if (hit == null) {
7724             mInitialHitTestResult = null;
7725             return;
7726         }
7727         mInitialHitTestResult = new HitTestResult();
7728         if (hit.mLinkUrl != null) {
7729             setHitTestTypeFromUrl(hit.mLinkUrl);
7730             if (hit.mImageUrl != null
7731                     && mInitialHitTestResult.getType() == HitTestResult.SRC_ANCHOR_TYPE) {
7732                 mInitialHitTestResult.setType(HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
7733                 mInitialHitTestResult.setExtra(hit.mImageUrl);
7734             }
7735         } else if (hit.mImageUrl != null) {
7736             mInitialHitTestResult.setType(HitTestResult.IMAGE_TYPE);
7737             mInitialHitTestResult.setExtra(hit.mImageUrl);
7738         } else if (hit.mEditable) {
7739             mInitialHitTestResult.setType(HitTestResult.EDIT_TEXT_TYPE);
7740         } else if (hit.mIntentUrl != null) {
7741             setHitTestTypeFromUrl(hit.mIntentUrl);
7742         }
7743     }
7744 
shouldDrawHighlightRect()7745     private boolean shouldDrawHighlightRect() {
7746         if (mFocusedNode == null || mInitialHitTestResult == null) {
7747             return false;
7748         }
7749         if (mTouchHighlightRegion.isEmpty()) {
7750             return false;
7751         }
7752         if (mFocusedNode.mHasFocus && !mWebView.isInTouchMode()) {
7753             return mDrawCursorRing && !mFocusedNode.mEditable;
7754         }
7755         if (mFocusedNode.mHasFocus && mFocusedNode.mEditable) {
7756             return false;
7757         }
7758         return mShowTapHighlight;
7759     }
7760 
7761 
7762     private FocusTransitionDrawable mFocusTransition = null;
7763     static class FocusTransitionDrawable extends Drawable {
7764         Region mPreviousRegion;
7765         Region mNewRegion;
7766         float mProgress = 0;
7767         WebViewClassic mWebView;
7768         Paint mPaint;
7769         int mMaxAlpha;
7770         Point mTranslate;
7771 
FocusTransitionDrawable(WebViewClassic view)7772         public FocusTransitionDrawable(WebViewClassic view) {
7773             mWebView = view;
7774             mPaint = new Paint(mWebView.mTouchHightlightPaint);
7775             mMaxAlpha = mPaint.getAlpha();
7776         }
7777 
7778         @Override
setColorFilter(ColorFilter cf)7779         public void setColorFilter(ColorFilter cf) {
7780         }
7781 
7782         @Override
setAlpha(int alpha)7783         public void setAlpha(int alpha) {
7784         }
7785 
7786         @Override
getOpacity()7787         public int getOpacity() {
7788             return 0;
7789         }
7790 
setProgress(float p)7791         public void setProgress(float p) {
7792             mProgress = p;
7793             if (mWebView.mFocusTransition == this) {
7794                 if (mProgress == 1f)
7795                     mWebView.mFocusTransition = null;
7796                 mWebView.invalidate();
7797             }
7798         }
7799 
getProgress()7800         public float getProgress() {
7801             return mProgress;
7802         }
7803 
7804         @Override
draw(Canvas canvas)7805         public void draw(Canvas canvas) {
7806             if (mTranslate == null) {
7807                 Rect bounds = mPreviousRegion.getBounds();
7808                 Point from = new Point(bounds.centerX(), bounds.centerY());
7809                 mNewRegion.getBounds(bounds);
7810                 Point to = new Point(bounds.centerX(), bounds.centerY());
7811                 mTranslate = new Point(from.x - to.x, from.y - to.y);
7812             }
7813             int alpha = (int) (mProgress * mMaxAlpha);
7814             RegionIterator iter = new RegionIterator(mPreviousRegion);
7815             Rect r = new Rect();
7816             mPaint.setAlpha(mMaxAlpha - alpha);
7817             float tx = mTranslate.x * mProgress;
7818             float ty = mTranslate.y * mProgress;
7819             int save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
7820             canvas.translate(-tx, -ty);
7821             while (iter.next(r)) {
7822                 canvas.drawRect(r, mPaint);
7823             }
7824             canvas.restoreToCount(save);
7825             iter = new RegionIterator(mNewRegion);
7826             r = new Rect();
7827             mPaint.setAlpha(alpha);
7828             save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
7829             tx = mTranslate.x - tx;
7830             ty = mTranslate.y - ty;
7831             canvas.translate(tx, ty);
7832             while (iter.next(r)) {
7833                 canvas.drawRect(r, mPaint);
7834             }
7835             canvas.restoreToCount(save);
7836         }
7837     };
7838 
shouldAnimateTo(WebKitHitTest hit)7839     private boolean shouldAnimateTo(WebKitHitTest hit) {
7840         // TODO: Don't be annoying or throw out the animation entirely
7841         return false;
7842     }
7843 
setTouchHighlightRects(WebKitHitTest hit)7844     private void setTouchHighlightRects(WebKitHitTest hit) {
7845         FocusTransitionDrawable transition = null;
7846         if (shouldAnimateTo(hit)) {
7847             transition = new FocusTransitionDrawable(this);
7848         }
7849         Rect[] rects = hit != null ? hit.mTouchRects : null;
7850         if (!mTouchHighlightRegion.isEmpty()) {
7851             mWebView.invalidate(mTouchHighlightRegion.getBounds());
7852             if (transition != null) {
7853                 transition.mPreviousRegion = new Region(mTouchHighlightRegion);
7854             }
7855             mTouchHighlightRegion.setEmpty();
7856         }
7857         if (rects != null) {
7858             mTouchHightlightPaint.setColor(hit.mTapHighlightColor);
7859             for (Rect rect : rects) {
7860                 Rect viewRect = contentToViewRect(rect);
7861                 // some sites, like stories in nytimes.com, set
7862                 // mouse event handler in the top div. It is not
7863                 // user friendly to highlight the div if it covers
7864                 // more than half of the screen.
7865                 if (viewRect.width() < getWidth() >> 1
7866                         || viewRect.height() < getHeight() >> 1) {
7867                     mTouchHighlightRegion.union(viewRect);
7868                 } else if (DebugFlags.WEB_VIEW) {
7869                     Log.d(LOGTAG, "Skip the huge selection rect:"
7870                             + viewRect);
7871                 }
7872             }
7873             mWebView.invalidate(mTouchHighlightRegion.getBounds());
7874             if (transition != null && transition.mPreviousRegion != null) {
7875                 transition.mNewRegion = new Region(mTouchHighlightRegion);
7876                 mFocusTransition = transition;
7877                 ObjectAnimator animator = ObjectAnimator.ofFloat(
7878                         mFocusTransition, "progress", 1f);
7879                 animator.start();
7880             }
7881         }
7882     }
7883 
7884     // Interface to allow the profiled WebView to hook the page swap notifications.
7885     public interface PageSwapDelegate {
7886         void onPageSwapOccurred(boolean notifyAnimationStarted);
7887     }
7888 
7889     long mLastSwapTime;
7890     double mAverageSwapFps;
7891 
7892     /** Called by JNI when pages are swapped (only occurs with hardware
7893      * acceleration) */
pageSwapCallback(boolean notifyAnimationStarted)7894     protected void pageSwapCallback(boolean notifyAnimationStarted) {
7895         if (DebugFlags.MEASURE_PAGE_SWAP_FPS) {
7896             long now = System.currentTimeMillis();
7897             long diff = now - mLastSwapTime;
7898             mAverageSwapFps = ((1000.0 / diff) + mAverageSwapFps) / 2;
7899             Log.d(LOGTAG, "page swap fps: " + mAverageSwapFps);
7900             mLastSwapTime = now;
7901         }
7902         mWebViewCore.resumeWebKitDraw();
7903         if (notifyAnimationStarted) {
7904             mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
7905         }
7906         if (mWebView instanceof PageSwapDelegate) {
7907             // This provides a hook for ProfiledWebView to observe the tile page swaps.
7908             ((PageSwapDelegate) mWebView).onPageSwapOccurred(notifyAnimationStarted);
7909         }
7910 
7911         if (mPictureListener != null) {
7912             // trigger picture listener for hardware layers. Software layers are
7913             // triggered in setNewPicture
7914             mPictureListener.onNewPicture(getWebView(), capturePicture());
7915         }
7916     }
7917 
setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer)7918     void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) {
7919         if (mNativeClass == 0) {
7920             if (mDelaySetPicture != null) {
7921                 throw new IllegalStateException("Tried to setNewPicture with"
7922                         + " a delay picture already set! (memory leak)");
7923             }
7924             // Not initialized yet, delay set
7925             mDelaySetPicture = draw;
7926             return;
7927         }
7928         WebViewCore.ViewState viewState = draw.mViewState;
7929         boolean isPictureAfterFirstLayout = viewState != null;
7930 
7931         if (updateBaseLayer) {
7932             setBaseLayer(draw.mBaseLayer,
7933                     getSettings().getShowVisualIndicator(),
7934                     isPictureAfterFirstLayout);
7935         }
7936         final Point viewSize = draw.mViewSize;
7937         // We update the layout (i.e. request a layout from the
7938         // view system) if the last view size that we sent to
7939         // WebCore matches the view size of the picture we just
7940         // received in the fixed dimension.
7941         final boolean updateLayout = viewSize.x == mLastWidthSent
7942                 && viewSize.y == mLastHeightSent;
7943         // Don't send scroll event for picture coming from webkit,
7944         // since the new picture may cause a scroll event to override
7945         // the saved history scroll position.
7946         mSendScrollEvent = false;
7947         recordNewContentSize(draw.mContentSize.x,
7948                 draw.mContentSize.y, updateLayout);
7949         if (isPictureAfterFirstLayout) {
7950             // Reset the last sent data here since dealing with new page.
7951             mLastWidthSent = 0;
7952             mZoomManager.onFirstLayout(draw);
7953             int scrollX = viewState.mShouldStartScrolledRight
7954                     ? getContentWidth() : viewState.mScrollX;
7955             int scrollY = viewState.mScrollY;
7956             contentScrollTo(scrollX, scrollY, false);
7957             if (!mDrawHistory) {
7958                 // As we are on a new page, hide the keyboard
7959                 hideSoftKeyboard();
7960             }
7961         }
7962         mSendScrollEvent = true;
7963 
7964         int functor = 0;
7965         boolean forceInval = isPictureAfterFirstLayout;
7966         ViewRootImpl viewRoot = mWebView.getViewRootImpl();
7967         if (mWebView.isHardwareAccelerated()
7968                 && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE
7969                 && viewRoot != null) {
7970             functor = nativeGetDrawGLFunction(mNativeClass);
7971             if (functor != 0) {
7972                 // force an invalidate if functor attach not successful
7973                 forceInval |= !viewRoot.attachFunctor(functor);
7974             }
7975         }
7976 
7977         if (functor == 0
7978                 || forceInval
7979                 || mWebView.getLayerType() != View.LAYER_TYPE_NONE) {
7980             // invalidate the screen so that the next repaint will show new content
7981             // TODO: partial invalidate
7982             mWebView.invalidate();
7983         }
7984 
7985         // update the zoom information based on the new picture
7986         if (mZoomManager.onNewPicture(draw))
7987             invalidate();
7988 
7989         if (isPictureAfterFirstLayout) {
7990             mViewManager.postReadyToDrawAll();
7991         }
7992         scrollEditWithCursor();
7993 
7994         if (mPictureListener != null) {
7995             if (!mWebView.isHardwareAccelerated()
7996                     || mWebView.getLayerType() == View.LAYER_TYPE_SOFTWARE) {
7997                 // trigger picture listener for software layers. Hardware layers are
7998                 // triggered in pageSwapCallback
7999                 mPictureListener.onNewPicture(getWebView(), capturePicture());
8000             }
8001         }
8002     }
8003 
8004     /**
8005      * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
8006      * and UPDATE_TEXT_SELECTION_MSG_ID.
8007      */
updateTextSelectionFromMessage(int nodePointer, int textGeneration, WebViewCore.TextSelectionData data)8008     private void updateTextSelectionFromMessage(int nodePointer,
8009             int textGeneration, WebViewCore.TextSelectionData data) {
8010         if (textGeneration == mTextGeneration) {
8011             if (mInputConnection != null && mFieldPointer == nodePointer) {
8012                 mInputConnection.setSelection(data.mStart, data.mEnd);
8013             }
8014         }
8015         nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
8016 
8017         if ((data.mSelectionReason == TextSelectionData.REASON_ACCESSIBILITY_INJECTOR)
8018                 || (!mSelectingText && data.mStart != data.mEnd
8019                         && data.mSelectionReason != TextSelectionData.REASON_SELECT_WORD)) {
8020             selectionDone();
8021             mShowTextSelectionExtra = true;
8022             invalidate();
8023             return;
8024         }
8025 
8026         if (data.mSelectTextPtr != 0 &&
8027                 (data.mStart != data.mEnd ||
8028                 (mFieldPointer == nodePointer && mFieldPointer != 0) ||
8029                 (nodePointer == 0 && data.mStart == 0 && data.mEnd == 0))) {
8030             mIsEditingText = (mFieldPointer == nodePointer) && nodePointer != 0;
8031             mIsCaretSelection = (data.mStart == data.mEnd && nodePointer != 0);
8032             if (mIsCaretSelection &&
8033                     (mInputConnection == null ||
8034                     mInputConnection.getEditable().length() == 0)) {
8035                 // There's no text, don't show caret handle.
8036                 selectionDone();
8037             } else {
8038                 if (!mSelectingText) {
8039                     setupWebkitSelect();
8040                 } else {
8041                     syncSelectionCursors();
8042                 }
8043                 animateHandles();
8044                 if (mIsCaretSelection) {
8045                     resetCaretTimer();
8046                 }
8047             }
8048         } else {
8049             selectionDone();
8050         }
8051         invalidate();
8052     }
8053 
scrollEditText(int scrollX, int scrollY)8054     private void scrollEditText(int scrollX, int scrollY) {
8055         // Scrollable edit text. Scroll it.
8056         float maxScrollX = getMaxTextScrollX();
8057         float scrollPercentX = ((float)scrollX)/maxScrollX;
8058         mEditTextContent.offsetTo(-scrollX, -scrollY);
8059         mWebViewCore.removeMessages(EventHub.SCROLL_TEXT_INPUT);
8060         mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0,
8061                 scrollY, (Float)scrollPercentX);
8062         animateHandles();
8063     }
8064 
beginTextBatch()8065     private void beginTextBatch() {
8066         mIsBatchingTextChanges = true;
8067     }
8068 
commitTextBatch()8069     private void commitTextBatch() {
8070         if (mWebViewCore != null) {
8071             mWebViewCore.sendMessages(mBatchedTextChanges);
8072         }
8073         mBatchedTextChanges.clear();
8074         mIsBatchingTextChanges = false;
8075     }
8076 
sendBatchableInputMessage(int what, int arg1, int arg2, Object obj)8077     void sendBatchableInputMessage(int what, int arg1, int arg2,
8078             Object obj) {
8079         if (mWebViewCore == null) {
8080             return;
8081         }
8082         Message message = Message.obtain(null, what, arg1, arg2, obj);
8083         if (mIsBatchingTextChanges) {
8084             mBatchedTextChanges.add(message);
8085         } else {
8086             mWebViewCore.sendMessage(message);
8087         }
8088     }
8089 
8090     // Class used to use a dropdown for a <select> element
8091     private class InvokeListBox implements Runnable {
8092         // Whether the listbox allows multiple selection.
8093         private boolean     mMultiple;
8094         // Passed in to a list with multiple selection to tell
8095         // which items are selected.
8096         private int[]       mSelectedArray;
8097         // Passed in to a list with single selection to tell
8098         // where the initial selection is.
8099         private int         mSelection;
8100 
8101         private Container[] mContainers;
8102 
8103         // Need these to provide stable ids to my ArrayAdapter,
8104         // which normally does not have stable ids. (Bug 1250098)
8105         private class Container extends Object {
8106             /**
8107              * Possible values for mEnabled.  Keep in sync with OptionStatus in
8108              * WebViewCore.cpp
8109              */
8110             final static int OPTGROUP = -1;
8111             final static int OPTION_DISABLED = 0;
8112             final static int OPTION_ENABLED = 1;
8113 
8114             String  mString;
8115             int     mEnabled;
8116             int     mId;
8117 
8118             @Override
toString()8119             public String toString() {
8120                 return mString;
8121             }
8122         }
8123 
8124         /**
8125          *  Subclass ArrayAdapter so we can disable OptionGroupLabels,
8126          *  and allow filtering.
8127          */
8128         private class MyArrayListAdapter extends ArrayAdapter<Container> {
MyArrayListAdapter()8129             public MyArrayListAdapter() {
8130                 super(WebViewClassic.this.mContext,
8131                         mMultiple ? com.android.internal.R.layout.select_dialog_multichoice :
8132                         com.android.internal.R.layout.webview_select_singlechoice,
8133                         mContainers);
8134             }
8135 
8136             @Override
getView(int position, View convertView, ViewGroup parent)8137             public View getView(int position, View convertView,
8138                     ViewGroup parent) {
8139                 // Always pass in null so that we will get a new CheckedTextView
8140                 // Otherwise, an item which was previously used as an <optgroup>
8141                 // element (i.e. has no check), could get used as an <option>
8142                 // element, which needs a checkbox/radio, but it would not have
8143                 // one.
8144                 convertView = super.getView(position, null, parent);
8145                 Container c = item(position);
8146                 if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
8147                     // ListView does not draw dividers between disabled and
8148                     // enabled elements.  Use a LinearLayout to provide dividers
8149                     LinearLayout layout = new LinearLayout(mContext);
8150                     layout.setOrientation(LinearLayout.VERTICAL);
8151                     if (position > 0) {
8152                         View dividerTop = new View(mContext);
8153                         dividerTop.setBackgroundResource(
8154                                 android.R.drawable.divider_horizontal_bright);
8155                         layout.addView(dividerTop);
8156                     }
8157 
8158                     if (Container.OPTGROUP == c.mEnabled) {
8159                         // Currently select_dialog_multichoice uses CheckedTextViews.
8160                         // If that changes, the class cast will no longer be valid.
8161                         if (mMultiple) {
8162                             Assert.assertTrue(convertView instanceof CheckedTextView);
8163                             ((CheckedTextView) convertView).setCheckMarkDrawable(null);
8164                         }
8165                     } else {
8166                         // c.mEnabled == Container.OPTION_DISABLED
8167                         // Draw the disabled element in a disabled state.
8168                         convertView.setEnabled(false);
8169                     }
8170 
8171                     layout.addView(convertView);
8172                     if (position < getCount() - 1) {
8173                         View dividerBottom = new View(mContext);
8174                         dividerBottom.setBackgroundResource(
8175                                 android.R.drawable.divider_horizontal_bright);
8176                         layout.addView(dividerBottom);
8177                     }
8178                     return layout;
8179                 }
8180                 return convertView;
8181             }
8182 
8183             @Override
hasStableIds()8184             public boolean hasStableIds() {
8185                 // AdapterView's onChanged method uses this to determine whether
8186                 // to restore the old state.  Return false so that the old (out
8187                 // of date) state does not replace the new, valid state.
8188                 return false;
8189             }
8190 
item(int position)8191             private Container item(int position) {
8192                 if (position < 0 || position >= getCount()) {
8193                     return null;
8194                 }
8195                 return getItem(position);
8196             }
8197 
8198             @Override
getItemId(int position)8199             public long getItemId(int position) {
8200                 Container item = item(position);
8201                 if (item == null) {
8202                     return -1;
8203                 }
8204                 return item.mId;
8205             }
8206 
8207             @Override
areAllItemsEnabled()8208             public boolean areAllItemsEnabled() {
8209                 return false;
8210             }
8211 
8212             @Override
isEnabled(int position)8213             public boolean isEnabled(int position) {
8214                 Container item = item(position);
8215                 if (item == null) {
8216                     return false;
8217                 }
8218                 return Container.OPTION_ENABLED == item.mEnabled;
8219             }
8220         }
8221 
InvokeListBox(String[] array, int[] enabled, int[] selected)8222         private InvokeListBox(String[] array, int[] enabled, int[] selected) {
8223             mMultiple = true;
8224             mSelectedArray = selected;
8225 
8226             int length = array.length;
8227             mContainers = new Container[length];
8228             for (int i = 0; i < length; i++) {
8229                 mContainers[i] = new Container();
8230                 mContainers[i].mString = array[i];
8231                 mContainers[i].mEnabled = enabled[i];
8232                 mContainers[i].mId = i;
8233             }
8234         }
8235 
InvokeListBox(String[] array, int[] enabled, int selection)8236         private InvokeListBox(String[] array, int[] enabled, int selection) {
8237             mSelection = selection;
8238             mMultiple = false;
8239 
8240             int length = array.length;
8241             mContainers = new Container[length];
8242             for (int i = 0; i < length; i++) {
8243                 mContainers[i] = new Container();
8244                 mContainers[i].mString = array[i];
8245                 mContainers[i].mEnabled = enabled[i];
8246                 mContainers[i].mId = i;
8247             }
8248         }
8249 
8250         /*
8251          * Whenever the data set changes due to filtering, this class ensures
8252          * that the checked item remains checked.
8253          */
8254         private class SingleDataSetObserver extends DataSetObserver {
8255             private long        mCheckedId;
8256             private ListView    mListView;
8257             private Adapter     mAdapter;
8258 
8259             /*
8260              * Create a new observer.
8261              * @param id The ID of the item to keep checked.
8262              * @param l ListView for getting and clearing the checked states
8263              * @param a Adapter for getting the IDs
8264              */
SingleDataSetObserver(long id, ListView l, Adapter a)8265             public SingleDataSetObserver(long id, ListView l, Adapter a) {
8266                 mCheckedId = id;
8267                 mListView = l;
8268                 mAdapter = a;
8269             }
8270 
8271             @Override
onChanged()8272             public void onChanged() {
8273                 // The filter may have changed which item is checked.  Find the
8274                 // item that the ListView thinks is checked.
8275                 int position = mListView.getCheckedItemPosition();
8276                 long id = mAdapter.getItemId(position);
8277                 if (mCheckedId != id) {
8278                     // Clear the ListView's idea of the checked item, since
8279                     // it is incorrect
8280                     mListView.clearChoices();
8281                     // Search for mCheckedId.  If it is in the filtered list,
8282                     // mark it as checked
8283                     int count = mAdapter.getCount();
8284                     for (int i = 0; i < count; i++) {
8285                         if (mAdapter.getItemId(i) == mCheckedId) {
8286                             mListView.setItemChecked(i, true);
8287                             break;
8288                         }
8289                     }
8290                 }
8291             }
8292         }
8293 
8294         @Override
run()8295         public void run() {
8296             if (mWebViewCore == null
8297                     || getWebView().getWindowToken() == null
8298                     || getWebView().getViewRootImpl() == null) {
8299                 // We've been detached and/or destroyed since this was posted
8300                 return;
8301             }
8302             final ListView listView = (ListView) LayoutInflater.from(mContext)
8303                     .inflate(com.android.internal.R.layout.select_dialog, null);
8304             final MyArrayListAdapter adapter = new MyArrayListAdapter();
8305             AlertDialog.Builder b = new AlertDialog.Builder(mContext)
8306                     .setView(listView).setCancelable(true)
8307                     .setInverseBackgroundForced(true);
8308 
8309             if (mMultiple) {
8310                 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
8311                     @Override
8312                     public void onClick(DialogInterface dialog, int which) {
8313                         mWebViewCore.sendMessage(
8314                                 EventHub.LISTBOX_CHOICES,
8315                                 adapter.getCount(), 0,
8316                                 listView.getCheckedItemPositions());
8317                     }});
8318                 b.setNegativeButton(android.R.string.cancel,
8319                         new DialogInterface.OnClickListener() {
8320                     @Override
8321                     public void onClick(DialogInterface dialog, int which) {
8322                         mWebViewCore.sendMessage(
8323                                 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
8324                 }});
8325             }
8326             mListBoxDialog = b.create();
8327             listView.setAdapter(adapter);
8328             listView.setFocusableInTouchMode(true);
8329             // There is a bug (1250103) where the checks in a ListView with
8330             // multiple items selected are associated with the positions, not
8331             // the ids, so the items do not properly retain their checks when
8332             // filtered.  Do not allow filtering on multiple lists until
8333             // that bug is fixed.
8334 
8335             listView.setTextFilterEnabled(!mMultiple);
8336             if (mMultiple) {
8337                 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
8338                 int length = mSelectedArray.length;
8339                 for (int i = 0; i < length; i++) {
8340                     listView.setItemChecked(mSelectedArray[i], true);
8341                 }
8342             } else {
8343                 listView.setOnItemClickListener(new OnItemClickListener() {
8344                     @Override
8345                     public void onItemClick(AdapterView<?> parent, View v,
8346                             int position, long id) {
8347                         // Rather than sending the message right away, send it
8348                         // after the page regains focus.
8349                         mListBoxMessage = Message.obtain(null,
8350                                 EventHub.SINGLE_LISTBOX_CHOICE, (int) id, 0);
8351                         if (mListBoxDialog != null) {
8352                             mListBoxDialog.dismiss();
8353                             mListBoxDialog = null;
8354                         }
8355                     }
8356                 });
8357                 if (mSelection != -1) {
8358                     listView.setSelection(mSelection);
8359                     listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
8360                     listView.setItemChecked(mSelection, true);
8361                     DataSetObserver observer = new SingleDataSetObserver(
8362                             adapter.getItemId(mSelection), listView, adapter);
8363                     adapter.registerDataSetObserver(observer);
8364                 }
8365             }
8366             mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
8367                 @Override
8368                 public void onCancel(DialogInterface dialog) {
8369                     mWebViewCore.sendMessage(
8370                                 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
8371                     mListBoxDialog = null;
8372                 }
8373             });
8374             mListBoxDialog.show();
8375         }
8376     }
8377 
8378     private Message mListBoxMessage;
8379 
8380     /*
8381      * Request a dropdown menu for a listbox with multiple selection.
8382      *
8383      * @param array Labels for the listbox.
8384      * @param enabledArray  State for each element in the list.  See static
8385      *      integers in Container class.
8386      * @param selectedArray Which positions are initally selected.
8387      */
requestListBox(String[] array, int[] enabledArray, int[] selectedArray)8388     void requestListBox(String[] array, int[] enabledArray, int[]
8389             selectedArray) {
8390         mPrivateHandler.post(
8391                 new InvokeListBox(array, enabledArray, selectedArray));
8392     }
8393 
8394     /*
8395      * Request a dropdown menu for a listbox with single selection or a single
8396      * <select> element.
8397      *
8398      * @param array Labels for the listbox.
8399      * @param enabledArray  State for each element in the list.  See static
8400      *      integers in Container class.
8401      * @param selection Which position is initally selected.
8402      */
requestListBox(String[] array, int[] enabledArray, int selection)8403     void requestListBox(String[] array, int[] enabledArray, int selection) {
8404         mPrivateHandler.post(
8405                 new InvokeListBox(array, enabledArray, selection));
8406     }
8407 
getScaledMaxXScroll()8408     private int getScaledMaxXScroll() {
8409         int width;
8410         if (mHeightCanMeasure == false) {
8411             width = getViewWidth() / 4;
8412         } else {
8413             Rect visRect = new Rect();
8414             calcOurVisibleRect(visRect);
8415             width = visRect.width() / 2;
8416         }
8417         // FIXME the divisor should be retrieved from somewhere
8418         return viewToContentX(width);
8419     }
8420 
getScaledMaxYScroll()8421     private int getScaledMaxYScroll() {
8422         int height;
8423         if (mHeightCanMeasure == false) {
8424             height = getViewHeight() / 4;
8425         } else {
8426             Rect visRect = new Rect();
8427             calcOurVisibleRect(visRect);
8428             height = visRect.height() / 2;
8429         }
8430         // FIXME the divisor should be retrieved from somewhere
8431         // the closest thing today is hard-coded into ScrollView.java
8432         // (from ScrollView.java, line 363)   int maxJump = height/2;
8433         return Math.round(height * mZoomManager.getInvScale());
8434     }
8435 
8436     /**
8437      * Called by JNI to invalidate view
8438      */
viewInvalidate()8439     private void viewInvalidate() {
8440         invalidate();
8441     }
8442 
8443     /**
8444      * Pass the key directly to the page.  This assumes that
8445      * nativePageShouldHandleShiftAndArrows() returned true.
8446      */
letPageHandleNavKey(int keyCode, long time, boolean down, int metaState)8447     private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
8448         int keyEventAction;
8449         if (down) {
8450             keyEventAction = KeyEvent.ACTION_DOWN;
8451         } else {
8452             keyEventAction = KeyEvent.ACTION_UP;
8453         }
8454 
8455         KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
8456                 1, (metaState & KeyEvent.META_SHIFT_ON)
8457                 | (metaState & KeyEvent.META_ALT_ON)
8458                 | (metaState & KeyEvent.META_SYM_ON)
8459                 , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
8460         sendKeyEvent(event);
8461     }
8462 
sendKeyEvent(KeyEvent event)8463     private void sendKeyEvent(KeyEvent event) {
8464         int direction = 0;
8465         switch (event.getKeyCode()) {
8466         case KeyEvent.KEYCODE_DPAD_DOWN:
8467             direction = View.FOCUS_DOWN;
8468             break;
8469         case KeyEvent.KEYCODE_DPAD_UP:
8470             direction = View.FOCUS_UP;
8471             break;
8472         case KeyEvent.KEYCODE_DPAD_LEFT:
8473             direction = View.FOCUS_LEFT;
8474             break;
8475         case KeyEvent.KEYCODE_DPAD_RIGHT:
8476             direction = View.FOCUS_RIGHT;
8477             break;
8478         case KeyEvent.KEYCODE_TAB:
8479             direction = event.isShiftPressed() ? View.FOCUS_BACKWARD : View.FOCUS_FORWARD;
8480             break;
8481         }
8482         if (direction != 0 && mWebView.focusSearch(direction) == null) {
8483             // Can't take focus in that direction
8484             direction = 0;
8485         }
8486         int eventHubAction = EventHub.KEY_UP;
8487         if (event.getAction() == KeyEvent.ACTION_DOWN) {
8488             eventHubAction = EventHub.KEY_DOWN;
8489             int sound = keyCodeToSoundsEffect(event.getKeyCode());
8490             if (sound != 0) {
8491                 mWebView.playSoundEffect(sound);
8492             }
8493         }
8494         sendBatchableInputMessage(eventHubAction, direction, 0, event);
8495     }
8496 
8497     /**
8498      * See {@link WebView#setBackgroundColor(int)}
8499      */
8500     @Override
setBackgroundColor(int color)8501     public void setBackgroundColor(int color) {
8502         mBackgroundColor = color;
8503         mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
8504     }
8505 
8506     /**
8507      * Enable the communication b/t the webView and VideoViewProxy
8508      *
8509      * only used by the Browser
8510      */
setHTML5VideoViewProxy(HTML5VideoViewProxy proxy)8511     public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
8512         mHTML5VideoViewProxy = proxy;
8513     }
8514 
8515     /**
8516      * Set the time to wait between passing touches to WebCore. See also the
8517      * TOUCH_SENT_INTERVAL member for further discussion.
8518      *
8519      * This is only used by the DRT test application.
8520      */
setTouchInterval(int interval)8521     public void setTouchInterval(int interval) {
8522         mCurrentTouchInterval = interval;
8523     }
8524 
8525     /**
8526      * Copy text into the clipboard. This is called indirectly from
8527      * WebViewCore.
8528      * @param text The text to put into the clipboard.
8529      */
copyToClipboard(String text)8530     private void copyToClipboard(String text) {
8531         ClipboardManager cm = (ClipboardManager)mContext
8532                 .getSystemService(Context.CLIPBOARD_SERVICE);
8533         ClipData clip = ClipData.newPlainText(getTitle(), text);
8534         cm.setPrimaryClip(clip);
8535     }
8536 
autoFillForm(int autoFillQueryId)8537     /*package*/ void autoFillForm(int autoFillQueryId) {
8538         mPrivateHandler.obtainMessage(AUTOFILL_FORM, autoFillQueryId, 0)
8539             .sendToTarget();
8540     }
8541 
getViewManager()8542     /* package */ ViewManager getViewManager() {
8543         return mViewManager;
8544     }
8545 
8546     /** send content invalidate */
contentInvalidateAll()8547     protected void contentInvalidateAll() {
8548         if (mWebViewCore != null && !mBlockWebkitViewMessages) {
8549             mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
8550         }
8551     }
8552 
8553     /** discard all textures from tiles. Used in Profiled WebView */
discardAllTextures()8554     public void discardAllTextures() {
8555         nativeDiscardAllTextures();
8556     }
8557 
8558     @Override
setLayerType(int layerType, Paint paint)8559     public void setLayerType(int layerType, Paint paint) {
8560         updateHwAccelerated();
8561     }
8562 
updateHwAccelerated()8563     private void updateHwAccelerated() {
8564         if (mNativeClass == 0) {
8565             return;
8566         }
8567         boolean hwAccelerated = false;
8568         if (mWebView.isHardwareAccelerated()
8569                 && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
8570             hwAccelerated = true;
8571         }
8572 
8573         // result is of type LayerAndroid::InvalidateFlags, non zero means invalidate/redraw
8574         int result = nativeSetHwAccelerated(mNativeClass, hwAccelerated);
8575         if (mWebViewCore != null && !mBlockWebkitViewMessages && result != 0) {
8576             mWebViewCore.contentDraw();
8577         }
8578     }
8579 
8580     /**
8581      * Begin collecting per-tile profiling data
8582      *
8583      * only used by profiling tests
8584      */
tileProfilingStart()8585     public void tileProfilingStart() {
8586         nativeTileProfilingStart();
8587     }
8588     /**
8589      * Return per-tile profiling data
8590      *
8591      * only used by profiling tests
8592      */
tileProfilingStop()8593     public float tileProfilingStop() {
8594         return nativeTileProfilingStop();
8595     }
8596 
8597     /** only used by profiling tests */
tileProfilingClear()8598     public void tileProfilingClear() {
8599         nativeTileProfilingClear();
8600     }
8601     /** only used by profiling tests */
tileProfilingNumFrames()8602     public int tileProfilingNumFrames() {
8603         return nativeTileProfilingNumFrames();
8604     }
8605     /** only used by profiling tests */
tileProfilingNumTilesInFrame(int frame)8606     public int tileProfilingNumTilesInFrame(int frame) {
8607         return nativeTileProfilingNumTilesInFrame(frame);
8608     }
8609     /** only used by profiling tests */
tileProfilingGetInt(int frame, int tile, String key)8610     public int tileProfilingGetInt(int frame, int tile, String key) {
8611         return nativeTileProfilingGetInt(frame, tile, key);
8612     }
8613     /** only used by profiling tests */
tileProfilingGetFloat(int frame, int tile, String key)8614     public float tileProfilingGetFloat(int frame, int tile, String key) {
8615         return nativeTileProfilingGetFloat(frame, tile, key);
8616     }
8617 
8618     /**
8619      * Checks the focused content for an editable text field. This can be
8620      * text input or ContentEditable.
8621      * @return true if the focused item is an editable text field.
8622      */
focusCandidateIsEditableText()8623     boolean focusCandidateIsEditableText() {
8624         if (mFocusedNode != null) {
8625             return mFocusedNode.mEditable;
8626         }
8627         return false;
8628     }
8629 
8630     // Called via JNI
postInvalidate()8631     private void postInvalidate() {
8632         mWebView.postInvalidate();
8633     }
8634 
8635     // Note: must be called before first WebViewClassic is created.
setShouldMonitorWebCoreThread()8636     public static void setShouldMonitorWebCoreThread() {
8637         WebViewCore.setShouldMonitorWebCoreThread();
8638     }
8639 
8640     @Override
dumpViewHierarchyWithProperties(BufferedWriter out, int level)8641     public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
8642         int layer = getBaseLayer();
8643         if (layer != 0) {
8644             try {
8645                 ByteArrayOutputStream stream = new ByteArrayOutputStream();
8646                 ViewStateSerializer.dumpLayerHierarchy(layer, stream, level);
8647                 stream.close();
8648                 byte[] buf = stream.toByteArray();
8649                 out.write(new String(buf, "ascii"));
8650             } catch (IOException e) {}
8651         }
8652     }
8653 
8654     @Override
findHierarchyView(String className, int hashCode)8655     public View findHierarchyView(String className, int hashCode) {
8656         if (mNativeClass == 0) return null;
8657         Picture pic = new Picture();
8658         if (!nativeDumpLayerContentToPicture(mNativeClass, className, hashCode, pic)) {
8659             return null;
8660         }
8661         return new PictureWrapperView(getContext(), pic, mWebView);
8662     }
8663 
8664     private static class PictureWrapperView extends View {
8665         Picture mPicture;
8666         WebView mWebView;
8667 
PictureWrapperView(Context context, Picture picture, WebView parent)8668         public PictureWrapperView(Context context, Picture picture, WebView parent) {
8669             super(context);
8670             mPicture = picture;
8671             mWebView = parent;
8672             setWillNotDraw(false);
8673             setRight(mPicture.getWidth());
8674             setBottom(mPicture.getHeight());
8675         }
8676 
8677         @Override
onDraw(Canvas canvas)8678         protected void onDraw(Canvas canvas) {
8679             canvas.drawPicture(mPicture);
8680         }
8681 
8682         @Override
post(Runnable action)8683         public boolean post(Runnable action) {
8684             return mWebView.post(action);
8685         }
8686     }
8687 
8688     private native void     nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx);
8689     private native void     nativeDebugDump();
8690     private static native void nativeDestroy(int ptr);
8691 
8692     private native void nativeDraw(Canvas canvas, RectF visibleRect,
8693             int color, int extra);
8694     private native void     nativeDumpDisplayTree(String urlOrNull);
8695     private native boolean  nativeEvaluateLayersAnimations(int nativeInstance);
8696     private native int      nativeCreateDrawGLFunction(int nativeInstance, Rect invScreenRect,
8697             Rect screenRect, RectF visibleContentRect, float scale, int extras);
8698     private native int      nativeGetDrawGLFunction(int nativeInstance);
8699     private native void     nativeUpdateDrawGLFunction(int nativeInstance, Rect invScreenRect,
8700             Rect screenRect, RectF visibleContentRect, float scale);
8701     private native String   nativeGetSelection();
8702     private native void     nativeSetHeightCanMeasure(boolean measure);
8703     private native boolean  nativeSetBaseLayer(int nativeInstance,
8704             int layer, boolean showVisualIndicator, boolean isPictureAfterFirstLayout,
8705             int scrollingLayer);
8706     private native int      nativeGetBaseLayer(int nativeInstance);
8707     private native void     nativeCopyBaseContentToPicture(Picture pict);
8708     private native boolean     nativeDumpLayerContentToPicture(int nativeInstance,
8709             String className, int layerId, Picture pict);
8710     private native boolean  nativeHasContent();
8711     private native void     nativeStopGL(int ptr);
8712     private native void     nativeDiscardAllTextures();
8713     private native void     nativeTileProfilingStart();
8714     private native float    nativeTileProfilingStop();
8715     private native void     nativeTileProfilingClear();
8716     private native int      nativeTileProfilingNumFrames();
8717     private native int      nativeTileProfilingNumTilesInFrame(int frame);
8718     private native int      nativeTileProfilingGetInt(int frame, int tile, String key);
8719     private native float    nativeTileProfilingGetFloat(int frame, int tile, String key);
8720 
8721     private native void     nativeUseHardwareAccelSkia(boolean enabled);
8722 
8723     // Returns a pointer to the scrollable LayerAndroid at the given point.
8724     private native int      nativeScrollableLayer(int nativeInstance, int x, int y, Rect scrollRect,
8725             Rect scrollBounds);
8726     /**
8727      * Scroll the specified layer.
8728      * @param nativeInstance Native WebView instance
8729      * @param layer Id of the layer to scroll, as determined by nativeScrollableLayer.
8730      * @param newX Destination x position to which to scroll.
8731      * @param newY Destination y position to which to scroll.
8732      * @return True if the layer is successfully scrolled.
8733      */
8734     private native boolean  nativeScrollLayer(int nativeInstance, int layer, int newX, int newY);
8735     private native void     nativeSetIsScrolling(boolean isScrolling);
8736     private native int      nativeGetBackgroundColor(int nativeInstance);
8737     native boolean  nativeSetProperty(String key, String value);
8738     native String   nativeGetProperty(String key);
8739     /**
8740      * See {@link ComponentCallbacks2} for the trim levels and descriptions
8741      */
8742     private static native void     nativeOnTrimMemory(int level);
8743     private static native void nativeSetPauseDrawing(int instance, boolean pause);
8744     private static native void nativeSetTextSelection(int instance, int selection);
8745     private static native int nativeGetHandleLayerId(int instance, int handle,
8746             Point cursorLocation, QuadF textQuad);
8747     private static native void nativeMapLayerRect(int instance, int layerId,
8748             Rect rect);
8749     // Returns 1 if a layer sync is needed, else 0
8750     private static native int nativeSetHwAccelerated(int instance, boolean hwAccelerated);
8751     private static native void nativeFindMaxVisibleRect(int instance, int layerId,
8752             Rect visibleContentRect);
8753     private static native boolean nativeIsHandleLeft(int instance, int handleId);
8754     private static native boolean nativeIsPointVisible(int instance,
8755             int layerId, int contentX, int contentY);
8756 }
8757