blob: b930276b70c627fce3cbd394ce12ccd3c98aa350 [file] [log] [blame]
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001/*
Jonathan Dixon3c909522012-02-28 18:45:06 +00002 * Copyright (C) 2012 The Android Open Source Project
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
alanvfdfd0d82012-08-09 18:05:17 -070019import android.accessibilityservice.AccessibilityServiceInfo;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080020import android.animation.ObjectAnimator;
21import android.annotation.Widget;
22import android.app.ActivityManager;
23import android.app.AlertDialog;
24import android.content.BroadcastReceiver;
25import android.content.ClipData;
26import android.content.ClipboardManager;
27import android.content.ComponentCallbacks2;
28import android.content.Context;
29import android.content.DialogInterface;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080030import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.pm.PackageManager;
33import android.content.res.Configuration;
34import android.database.DataSetObserver;
35import android.graphics.Bitmap;
36import android.graphics.BitmapFactory;
37import android.graphics.BitmapShader;
38import android.graphics.Canvas;
39import android.graphics.Color;
40import android.graphics.ColorFilter;
41import android.graphics.DrawFilter;
42import android.graphics.Paint;
43import android.graphics.PaintFlagsDrawFilter;
44import android.graphics.Picture;
45import android.graphics.Point;
George Mountf9c1f992012-03-21 16:06:10 -070046import android.graphics.PointF;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080047import android.graphics.Rect;
48import android.graphics.RectF;
49import android.graphics.Region;
50import android.graphics.RegionIterator;
51import android.graphics.Shader;
52import android.graphics.drawable.Drawable;
53import android.net.Proxy;
54import android.net.ProxyProperties;
55import android.net.Uri;
56import android.net.http.SslCertificate;
57import android.os.AsyncTask;
Selim Gurune91d5be2012-09-11 16:11:22 -070058import android.os.Build;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080059import android.os.Bundle;
60import android.os.Handler;
Jeff Brown9d3bdbd2012-03-21 11:50:06 -070061import android.os.Looper;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080062import android.os.Message;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080063import android.os.SystemClock;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080064import android.security.KeyChain;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080065import android.text.Editable;
66import android.text.InputType;
67import android.text.Selection;
68import android.text.TextUtils;
Torne (Richard Coles)03ce9b32013-06-12 16:02:03 +010069import android.util.AndroidRuntimeException;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080070import android.util.DisplayMetrics;
71import android.util.EventLog;
72import android.util.Log;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080073import android.view.Gravity;
74import android.view.HapticFeedbackConstants;
75import android.view.HardwareCanvas;
76import android.view.InputDevice;
77import android.view.KeyCharacterMap;
78import android.view.KeyEvent;
79import android.view.LayoutInflater;
80import android.view.MotionEvent;
81import android.view.ScaleGestureDetector;
82import android.view.SoundEffectConstants;
83import android.view.VelocityTracker;
84import android.view.View;
Jonathan Dixon3c909522012-02-28 18:45:06 +000085import android.view.View.MeasureSpec;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080086import android.view.ViewConfiguration;
87import android.view.ViewGroup;
88import android.view.ViewParent;
Chris Craika59558f2012-04-30 10:15:33 -070089import android.view.ViewRootImpl;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080090import android.view.accessibility.AccessibilityEvent;
91import android.view.accessibility.AccessibilityManager;
92import android.view.accessibility.AccessibilityNodeInfo;
Ben Murdoche3f90712013-06-05 14:19:48 +010093import android.view.accessibility.AccessibilityNodeProvider;
Jonathan Dixonded37ed92012-02-13 17:26:46 -080094import android.view.inputmethod.BaseInputConnection;
95import android.view.inputmethod.EditorInfo;
96import android.view.inputmethod.InputConnection;
97import android.view.inputmethod.InputMethodManager;
Jonathan Dixon3c909522012-02-28 18:45:06 +000098import android.webkit.WebView.HitTestResult;
99import android.webkit.WebView.PictureListener;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800100import android.webkit.WebViewCore.DrawData;
101import android.webkit.WebViewCore.EventHub;
102import android.webkit.WebViewCore.TextFieldInitData;
John Reckd4796462012-05-02 18:23:13 -0700103import android.webkit.WebViewCore.TextSelectionData;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800104import android.webkit.WebViewCore.WebKitHitTest;
105import android.widget.AbsoluteLayout;
106import android.widget.Adapter;
107import android.widget.AdapterView;
108import android.widget.AdapterView.OnItemClickListener;
109import android.widget.ArrayAdapter;
110import android.widget.CheckedTextView;
111import android.widget.LinearLayout;
112import android.widget.ListView;
113import android.widget.OverScroller;
114import android.widget.PopupWindow;
George Mountf70276a2012-03-12 14:22:10 -0700115import android.widget.Scroller;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800116import android.widget.TextView;
117import android.widget.Toast;
118
119import junit.framework.Assert;
120
John Reck926cf562012-06-14 10:00:31 -0700121import java.io.BufferedWriter;
122import java.io.ByteArrayOutputStream;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800123import java.io.File;
124import java.io.FileInputStream;
125import java.io.FileNotFoundException;
126import java.io.FileOutputStream;
127import java.io.IOException;
128import java.io.InputStream;
129import java.io.OutputStream;
130import java.net.URLDecoder;
131import java.util.ArrayList;
132import java.util.HashMap;
133import java.util.HashSet;
134import java.util.List;
George Mount9f410c52012-08-10 15:29:30 -0700135import java.util.Locale;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800136import java.util.Map;
137import java.util.Set;
138import java.util.Vector;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800139
140/**
Steve Block406aeb22012-04-23 18:17:19 +0100141 * Implements a backend provider for the {@link WebView} public API.
Jonathan Dixon3c909522012-02-28 18:45:06 +0000142 * @hide
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800143 */
Jonathan Dixon3c909522012-02-28 18:45:06 +0000144// TODO: Check if any WebView published API methods are called from within here, and if so
145// we should bounce the call out via the proxy to enable any sub-class to override it.
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800146@Widget
John Reckb2676f72012-03-02 14:13:06 -0800147@SuppressWarnings("deprecation")
Jonathan Dixon3c909522012-02-28 18:45:06 +0000148public final class WebViewClassic implements WebViewProvider, WebViewProvider.ScrollDelegate,
149 WebViewProvider.ViewDelegate {
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800150 /**
151 * InputConnection used for ContentEditable. This captures changes
152 * to the text and sends them either as key strokes or text changes.
153 */
George Mountbcd5dd72012-03-01 08:39:03 -0800154 class WebViewInputConnection extends BaseInputConnection {
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800155 // Used for mapping characters to keys typed.
156 private KeyCharacterMap mKeyCharacterMap;
157 private boolean mIsKeySentByMe;
158 private int mInputType;
159 private int mImeOptions;
160 private String mHint;
161 private int mMaxLength;
George Mountbcd5dd72012-03-01 08:39:03 -0800162 private boolean mIsAutoFillable;
163 private boolean mIsAutoCompleteEnabled;
164 private String mName;
George Mountdbef1c52012-03-28 14:17:13 -0700165 private int mBatchLevel;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800166
167 public WebViewInputConnection() {
Jonathan Dixon3c909522012-02-28 18:45:06 +0000168 super(mWebView, true);
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800169 }
170
George Mountbcd5dd72012-03-01 08:39:03 -0800171 public void setAutoFillable(int queryId) {
172 mIsAutoFillable = getSettings().getAutoFillEnabled()
173 && (queryId != WebTextView.FORM_NOT_AUTOFILLABLE);
174 int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION;
175 if (variation != EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD
176 && (mIsAutoFillable || mIsAutoCompleteEnabled)) {
177 if (mName != null && mName.length() > 0) {
178 requestFormData(mName, mFieldPointer, mIsAutoFillable,
179 mIsAutoCompleteEnabled);
180 }
181 }
182 }
183
George Mountdbef1c52012-03-28 14:17:13 -0700184 @Override
185 public boolean beginBatchEdit() {
186 if (mBatchLevel == 0) {
187 beginTextBatch();
188 }
189 mBatchLevel++;
190 return false;
191 }
192
193 @Override
194 public boolean endBatchEdit() {
195 mBatchLevel--;
196 if (mBatchLevel == 0) {
197 commitTextBatch();
198 }
199 return false;
200 }
201
George Mountbcd5dd72012-03-01 08:39:03 -0800202 public boolean getIsAutoFillable() {
203 return mIsAutoFillable;
204 }
205
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800206 @Override
207 public boolean sendKeyEvent(KeyEvent event) {
208 // Some IMEs send key events directly using sendKeyEvents.
209 // WebViewInputConnection should treat these as text changes.
210 if (!mIsKeySentByMe) {
211 if (event.getAction() == KeyEvent.ACTION_UP) {
212 if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
213 return deleteSurroundingText(1, 0);
214 } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
215 return deleteSurroundingText(0, 1);
216 } else if (event.getUnicodeChar() != 0){
217 String newComposingText =
218 Character.toString((char)event.getUnicodeChar());
219 return commitText(newComposingText, 1);
220 }
221 } else if (event.getAction() == KeyEvent.ACTION_DOWN &&
222 (event.getKeyCode() == KeyEvent.KEYCODE_DEL
223 || event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL
224 || event.getUnicodeChar() != 0)) {
225 return true; // only act on action_down
226 }
227 }
228 return super.sendKeyEvent(event);
229 }
230
231 public void setTextAndKeepSelection(CharSequence text) {
232 Editable editable = getEditable();
233 int selectionStart = Selection.getSelectionStart(editable);
234 int selectionEnd = Selection.getSelectionEnd(editable);
235 text = limitReplaceTextByMaxLength(text, editable.length());
236 editable.replace(0, editable.length(), text);
237 restartInput();
238 // Keep the previous selection.
239 selectionStart = Math.min(selectionStart, editable.length());
240 selectionEnd = Math.min(selectionEnd, editable.length());
241 setSelection(selectionStart, selectionEnd);
George Mountd4080482012-04-03 11:19:08 -0700242 finishComposingText();
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800243 }
244
245 public void replaceSelection(CharSequence text) {
246 Editable editable = getEditable();
247 int selectionStart = Selection.getSelectionStart(editable);
248 int selectionEnd = Selection.getSelectionEnd(editable);
249 text = limitReplaceTextByMaxLength(text, selectionEnd - selectionStart);
250 setNewText(selectionStart, selectionEnd, text);
251 editable.replace(selectionStart, selectionEnd, text);
252 restartInput();
253 // Move caret to the end of the new text
254 int newCaret = selectionStart + text.length();
255 setSelection(newCaret, newCaret);
256 }
257
258 @Override
259 public boolean setComposingText(CharSequence text, int newCursorPosition) {
260 Editable editable = getEditable();
261 int start = getComposingSpanStart(editable);
262 int end = getComposingSpanEnd(editable);
263 if (start < 0 || end < 0) {
264 start = Selection.getSelectionStart(editable);
265 end = Selection.getSelectionEnd(editable);
266 }
267 if (end < start) {
268 int temp = end;
269 end = start;
270 start = temp;
271 }
272 CharSequence limitedText = limitReplaceTextByMaxLength(text, end - start);
273 setNewText(start, end, limitedText);
274 if (limitedText != text) {
275 newCursorPosition -= text.length() - limitedText.length();
276 }
277 super.setComposingText(limitedText, newCursorPosition);
George Mountcdd48a72012-05-17 10:48:51 -0700278 updateSelection();
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800279 if (limitedText != text) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800280 int lastCaret = start + limitedText.length();
281 finishComposingText();
282 setSelection(lastCaret, lastCaret);
283 }
284 return true;
285 }
286
287 @Override
288 public boolean commitText(CharSequence text, int newCursorPosition) {
289 setComposingText(text, newCursorPosition);
George Mountcdd48a72012-05-17 10:48:51 -0700290 finishComposingText();
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800291 return true;
292 }
293
294 @Override
295 public boolean deleteSurroundingText(int leftLength, int rightLength) {
George Mountcdd48a72012-05-17 10:48:51 -0700296 // This code is from BaseInputConnection#deleteSurroundText.
297 // We have to delete the same text in webkit.
298 Editable content = getEditable();
299 int a = Selection.getSelectionStart(content);
300 int b = Selection.getSelectionEnd(content);
301
302 if (a > b) {
303 int tmp = a;
304 a = b;
305 b = tmp;
306 }
307
308 int ca = getComposingSpanStart(content);
309 int cb = getComposingSpanEnd(content);
310 if (cb < ca) {
311 int tmp = ca;
312 ca = cb;
313 cb = tmp;
314 }
315 if (ca != -1 && cb != -1) {
316 if (ca < a) a = ca;
317 if (cb > b) b = cb;
318 }
319
320 int endDelete = Math.min(content.length(), b + rightLength);
321 if (endDelete > b) {
322 setNewText(b, endDelete, "");
323 }
324 int startDelete = Math.max(0, a - leftLength);
325 if (startDelete < a) {
326 setNewText(startDelete, a, "");
327 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800328 return super.deleteSurroundingText(leftLength, rightLength);
329 }
330
331 @Override
332 public boolean performEditorAction(int editorAction) {
333
334 boolean handled = true;
335 switch (editorAction) {
336 case EditorInfo.IME_ACTION_NEXT:
Jonathan Dixon3c909522012-02-28 18:45:06 +0000337 mWebView.requestFocus(View.FOCUS_FORWARD);
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800338 break;
339 case EditorInfo.IME_ACTION_PREVIOUS:
Jonathan Dixon3c909522012-02-28 18:45:06 +0000340 mWebView.requestFocus(View.FOCUS_BACKWARD);
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800341 break;
342 case EditorInfo.IME_ACTION_DONE:
Jonathan Dixon3c909522012-02-28 18:45:06 +0000343 WebViewClassic.this.hideSoftKeyboard();
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800344 break;
345 case EditorInfo.IME_ACTION_GO:
346 case EditorInfo.IME_ACTION_SEARCH:
Jonathan Dixon3c909522012-02-28 18:45:06 +0000347 WebViewClassic.this.hideSoftKeyboard();
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800348 String text = getEditable().toString();
349 passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_DOWN,
350 KeyEvent.KEYCODE_ENTER));
351 passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_UP,
352 KeyEvent.KEYCODE_ENTER));
353 break;
354
355 default:
356 handled = super.performEditorAction(editorAction);
357 break;
358 }
359
360 return handled;
361 }
362
363 public void initEditorInfo(WebViewCore.TextFieldInitData initData) {
364 int type = initData.mType;
365 int inputType = InputType.TYPE_CLASS_TEXT
366 | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
367 int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
368 | EditorInfo.IME_FLAG_NO_FULLSCREEN;
369 if (!initData.mIsSpellCheckEnabled) {
370 inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
371 }
George Mounta34a5ed2012-03-09 16:58:40 -0800372 if (WebTextView.TEXT_AREA != type) {
373 if (initData.mIsTextFieldNext) {
374 imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
375 }
376 if (initData.mIsTextFieldPrev) {
377 imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
378 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800379 }
George Mount68c0c122012-08-15 10:15:01 -0700380 int action = EditorInfo.IME_ACTION_GO;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800381 switch (type) {
382 case WebTextView.NORMAL_TEXT_FIELD:
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800383 break;
384 case WebTextView.TEXT_AREA:
385 inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE
386 | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
387 | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
George Mount68c0c122012-08-15 10:15:01 -0700388 action = EditorInfo.IME_ACTION_NONE;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800389 break;
390 case WebTextView.PASSWORD:
391 inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800392 break;
393 case WebTextView.SEARCH:
George Mount68c0c122012-08-15 10:15:01 -0700394 action = EditorInfo.IME_ACTION_SEARCH;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800395 break;
396 case WebTextView.EMAIL:
397 // inputType needs to be overwritten because of the different text variation.
398 inputType = InputType.TYPE_CLASS_TEXT
399 | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800400 break;
401 case WebTextView.NUMBER:
402 // inputType needs to be overwritten because of the different class.
403 inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL
404 | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL;
405 // Number and telephone do not have both a Tab key and an
406 // action, so set the action to NEXT
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800407 break;
408 case WebTextView.TELEPHONE:
409 // inputType needs to be overwritten because of the different class.
410 inputType = InputType.TYPE_CLASS_PHONE;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800411 break;
412 case WebTextView.URL:
413 // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
414 // exclude it for now.
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800415 inputType |= InputType.TYPE_TEXT_VARIATION_URI;
416 break;
417 default:
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800418 break;
419 }
George Mount68c0c122012-08-15 10:15:01 -0700420 imeOptions |= action;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800421 mHint = initData.mLabel;
422 mInputType = inputType;
423 mImeOptions = imeOptions;
424 mMaxLength = initData.mMaxLength;
George Mountbcd5dd72012-03-01 08:39:03 -0800425 mIsAutoCompleteEnabled = initData.mIsAutoCompleteEnabled;
426 mName = initData.mName;
427 mAutoCompletePopup.clearAdapter();
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800428 }
429
430 public void setupEditorInfo(EditorInfo outAttrs) {
431 outAttrs.inputType = mInputType;
432 outAttrs.imeOptions = mImeOptions;
433 outAttrs.hintText = mHint;
434 outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
George Mountcdd48a72012-05-17 10:48:51 -0700435
436 Editable editable = getEditable();
437 int selectionStart = Selection.getSelectionStart(editable);
438 int selectionEnd = Selection.getSelectionEnd(editable);
439 if (selectionStart < 0 || selectionEnd < 0) {
440 selectionStart = editable.length();
441 selectionEnd = selectionStart;
442 }
443 outAttrs.initialSelStart = selectionStart;
444 outAttrs.initialSelEnd = selectionEnd;
445 }
446
447 @Override
448 public boolean setSelection(int start, int end) {
449 boolean result = super.setSelection(start, end);
450 updateSelection();
451 return result;
452 }
453
454 @Override
455 public boolean setComposingRegion(int start, int end) {
456 boolean result = super.setComposingRegion(start, end);
457 updateSelection();
458 return result;
459 }
460
461 /**
462 * Send the selection and composing spans to the IME.
463 */
464 private void updateSelection() {
465 Editable editable = getEditable();
466 int selectionStart = Selection.getSelectionStart(editable);
467 int selectionEnd = Selection.getSelectionEnd(editable);
468 int composingStart = getComposingSpanStart(editable);
469 int composingEnd = getComposingSpanEnd(editable);
470 InputMethodManager imm = InputMethodManager.peekInstance();
471 if (imm != null) {
472 imm.updateSelection(mWebView, selectionStart, selectionEnd,
473 composingStart, composingEnd);
474 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800475 }
476
477 /**
478 * Sends a text change to webkit indirectly. If it is a single-
479 * character add or delete, it sends it as a key stroke. If it cannot
480 * be represented as a key stroke, it sends it as a field change.
481 * @param start The start offset (inclusive) of the text being changed.
482 * @param end The end offset (exclusive) of the text being changed.
483 * @param text The new text to replace the changed text.
484 */
485 private void setNewText(int start, int end, CharSequence text) {
486 mIsKeySentByMe = true;
487 Editable editable = getEditable();
488 CharSequence original = editable.subSequence(start, end);
489 boolean isCharacterAdd = false;
490 boolean isCharacterDelete = false;
491 int textLength = text.length();
492 int originalLength = original.length();
George Mount5d09bf62012-05-16 14:56:09 -0700493 int selectionStart = Selection.getSelectionStart(editable);
494 int selectionEnd = Selection.getSelectionEnd(editable);
495 if (selectionStart == selectionEnd) {
496 if (textLength > originalLength) {
497 isCharacterAdd = (textLength == originalLength + 1)
498 && TextUtils.regionMatches(text, 0, original, 0,
499 originalLength);
500 } else if (originalLength > textLength) {
501 isCharacterDelete = (textLength == originalLength - 1)
502 && TextUtils.regionMatches(text, 0, original, 0,
503 textLength);
504 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800505 }
506 if (isCharacterAdd) {
507 sendCharacter(text.charAt(textLength - 1));
508 } else if (isCharacterDelete) {
509 sendKey(KeyEvent.KEYCODE_DEL);
510 } else if ((textLength != originalLength) ||
511 !TextUtils.regionMatches(text, 0, original, 0,
512 textLength)) {
513 // Send a message so that key strokes and text replacement
514 // do not come out of order.
515 Message replaceMessage = mPrivateHandler.obtainMessage(
516 REPLACE_TEXT, start, end, text.toString());
517 mPrivateHandler.sendMessage(replaceMessage);
518 }
George Mountbcd5dd72012-03-01 08:39:03 -0800519 if (mAutoCompletePopup != null) {
520 StringBuilder newText = new StringBuilder();
521 newText.append(editable.subSequence(0, start));
522 newText.append(text);
523 newText.append(editable.subSequence(end, editable.length()));
524 mAutoCompletePopup.setText(newText.toString());
525 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800526 mIsKeySentByMe = false;
527 }
528
529 /**
530 * Send a single character to the WebView as a key down and up event.
531 * @param c The character to be sent.
532 */
533 private void sendCharacter(char c) {
534 if (mKeyCharacterMap == null) {
535 mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
536 }
537 char[] chars = new char[1];
538 chars[0] = c;
539 KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
540 if (events != null) {
541 for (KeyEvent event : events) {
542 sendKeyEvent(event);
543 }
544 } else {
545 Message msg = mPrivateHandler.obtainMessage(KEY_PRESS, (int) c, 0);
546 mPrivateHandler.sendMessage(msg);
547 }
548 }
549
550 /**
551 * Send a key event for a specific key code, not a standard
552 * unicode character.
553 * @param keyCode The key code to send.
554 */
555 private void sendKey(int keyCode) {
556 long eventTime = SystemClock.uptimeMillis();
557 sendKeyEvent(new KeyEvent(eventTime, eventTime,
558 KeyEvent.ACTION_DOWN, keyCode, 0, 0,
559 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
560 KeyEvent.FLAG_SOFT_KEYBOARD));
561 sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
562 KeyEvent.ACTION_UP, keyCode, 0, 0,
563 KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
564 KeyEvent.FLAG_SOFT_KEYBOARD));
565 }
566
567 private CharSequence limitReplaceTextByMaxLength(CharSequence text,
568 int numReplaced) {
569 if (mMaxLength > 0) {
570 Editable editable = getEditable();
571 int maxReplace = mMaxLength - editable.length() + numReplaced;
572 if (maxReplace < text.length()) {
573 maxReplace = Math.max(maxReplace, 0);
574 // New length is greater than the maximum. trim it down.
575 text = text.subSequence(0, maxReplace);
576 }
577 }
578 return text;
579 }
580
581 private void restartInput() {
582 InputMethodManager imm = InputMethodManager.peekInstance();
583 if (imm != null) {
584 // Since the text has changed, do not allow the IME to replace the
585 // existing text as though it were a completion.
Jonathan Dixon3c909522012-02-28 18:45:06 +0000586 imm.restartInput(mWebView);
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800587 }
588 }
589 }
590
Jonathan Dixon3c909522012-02-28 18:45:06 +0000591 private class PastePopupWindow extends PopupWindow implements View.OnClickListener {
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800592 private ViewGroup mContentView;
593 private TextView mPasteTextView;
594
595 public PastePopupWindow() {
Jonathan Dixon3c909522012-02-28 18:45:06 +0000596 super(mContext, null,
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800597 com.android.internal.R.attr.textSelectHandleWindowStyle);
598 setClippingEnabled(true);
Jonathan Dixon3c909522012-02-28 18:45:06 +0000599 LinearLayout linearLayout = new LinearLayout(mContext);
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800600 linearLayout.setOrientation(LinearLayout.HORIZONTAL);
601 mContentView = linearLayout;
602 mContentView.setBackgroundResource(
603 com.android.internal.R.drawable.text_edit_paste_window);
604
Jonathan Dixon3c909522012-02-28 18:45:06 +0000605 LayoutInflater inflater = (LayoutInflater)mContext.
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800606 getSystemService(Context.LAYOUT_INFLATER_SERVICE);
607
608 ViewGroup.LayoutParams wrapContent = new ViewGroup.LayoutParams(
609 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
610
611 mPasteTextView = (TextView) inflater.inflate(
612 com.android.internal.R.layout.text_edit_action_popup_text, null);
613 mPasteTextView.setLayoutParams(wrapContent);
614 mContentView.addView(mPasteTextView);
615 mPasteTextView.setText(com.android.internal.R.string.paste);
616 mPasteTextView.setOnClickListener(this);
617 this.setContentView(mContentView);
618 }
619
George Mountf9c1f992012-03-21 16:06:10 -0700620 public void show(Point cursorBottom, Point cursorTop,
621 int windowLeft, int windowTop) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800622 measureContent();
623
624 int width = mContentView.getMeasuredWidth();
625 int height = mContentView.getMeasuredHeight();
George Mountf9c1f992012-03-21 16:06:10 -0700626 int y = cursorTop.y - height;
627 int x = cursorTop.x - (width / 2);
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800628 if (y < windowTop) {
629 // There's not enough room vertically, move it below the
630 // handle.
George Mount9a676bf2012-03-01 16:28:12 -0800631 ensureSelectionHandles();
George Mountf9c1f992012-03-21 16:06:10 -0700632 y = cursorBottom.y + mSelectHandleCenter.getIntrinsicHeight();
633 x = cursorBottom.x - (width / 2);
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800634 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800635 if (x < windowLeft) {
636 x = windowLeft;
637 }
638 if (!isShowing()) {
Jonathan Dixon3c909522012-02-28 18:45:06 +0000639 showAtLocation(mWebView, Gravity.NO_GRAVITY, x, y);
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800640 }
641 update(x, y, width, height);
642 }
643
644 public void hide() {
645 dismiss();
646 }
647
648 @Override
649 public void onClick(View view) {
650 pasteFromClipboard();
651 selectionDone();
652 }
653
654 protected void measureContent() {
655 final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
656 mContentView.measure(
657 View.MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels,
658 View.MeasureSpec.AT_MOST),
659 View.MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels,
660 View.MeasureSpec.AT_MOST));
661 }
662 }
663
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800664 // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
665 // the screen all-the-time. Good for profiling our drawing code
666 static private final boolean AUTO_REDRAW_HACK = false;
George Mount557748d2012-04-04 13:56:49 -0700667
668 // The rate at which edit text is scrolled in content pixels per millisecond
669 static private final float TEXT_SCROLL_RATE = 0.01f;
670
671 // The presumed scroll rate for the first scroll of edit text
672 static private final long TEXT_SCROLL_FIRST_SCROLL_MS = 16;
673
George Mount312cad62012-05-08 10:14:05 -0700674 // Buffer pixels of the caret rectangle when moving edit text into view
675 // after resize.
676 static private final int EDIT_RECT_BUFFER = 10;
677
George Mount9b6eb692012-06-05 09:19:51 -0700678 static private final long SELECTION_HANDLE_ANIMATION_MS = 150;
679
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800680 // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
681 private boolean mAutoRedraw;
682
683 // Reference to the AlertDialog displayed by InvokeListBox.
684 // It's used to dismiss the dialog in destroy if not done before.
685 private AlertDialog mListBoxDialog = null;
686
Johan Redestig8655e902012-08-30 10:13:41 +0200687 // Reference to the save password dialog so it can be dimissed in
688 // destroy if not done before.
689 private AlertDialog mSavePasswordDialog = null;
690
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800691 static final String LOGTAG = "webview";
692
693 private ZoomManager mZoomManager;
694
Teng-Hui Zhu508d7052012-05-03 14:31:55 -0700695 private final Rect mInvScreenRect = new Rect();
696 private final Rect mScreenRect = new Rect();
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800697 private final RectF mVisibleContentRect = new RectF();
Teng-Hui Zhu658e9992012-05-07 16:39:13 -0700698 private boolean mIsWebViewVisible = true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800699 WebViewInputConnection mInputConnection = null;
700 private int mFieldPointer;
701 private PastePopupWindow mPasteWindow;
George Mountf70276a2012-03-12 14:22:10 -0700702 private AutoCompletePopup mAutoCompletePopup;
George Mount7102eb22012-04-10 13:41:51 -0700703 Rect mEditTextContentBounds = new Rect();
George Mountf70276a2012-03-12 14:22:10 -0700704 Rect mEditTextContent = new Rect();
705 int mEditTextLayerId;
706 boolean mIsEditingText = false;
George Mountdbef1c52012-03-28 14:17:13 -0700707 ArrayList<Message> mBatchedTextChanges = new ArrayList<Message>();
708 boolean mIsBatchingTextChanges = false;
George Mount557748d2012-04-04 13:56:49 -0700709 private long mLastEditScroll = 0;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800710
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800711 private static class OnTrimMemoryListener implements ComponentCallbacks2 {
712 private static OnTrimMemoryListener sInstance = null;
713
714 static void init(Context c) {
715 if (sInstance == null) {
716 sInstance = new OnTrimMemoryListener(c.getApplicationContext());
717 }
718 }
719
720 private OnTrimMemoryListener(Context c) {
721 c.registerComponentCallbacks(this);
722 }
723
724 @Override
725 public void onConfigurationChanged(Configuration newConfig) {
726 // Ignore
727 }
728
729 @Override
730 public void onLowMemory() {
731 // Ignore
732 }
733
734 @Override
735 public void onTrimMemory(int level) {
736 if (DebugFlags.WEB_VIEW) {
737 Log.d("WebView", "onTrimMemory: " + level);
738 }
739 // When framework reset EGL context during high memory pressure, all
740 // the existing GL resources for the html5 video will be destroyed
741 // at native side.
742 // Here we just need to clean up the Surface Texture which is static.
Teng-Hui Zhuc3a28582012-05-31 17:36:17 -0700743 if (level > TRIM_MEMORY_UI_HIDDEN) {
Dianne Hackborn162bc0e2012-04-09 14:06:16 -0700744 HTML5VideoInline.cleanupSurfaceTexture();
Teng-Hui Zhu156f97b2012-07-09 15:54:32 -0700745 HTML5VideoView.release();
Dianne Hackborn162bc0e2012-04-09 14:06:16 -0700746 }
Jonathan Dixon3c909522012-02-28 18:45:06 +0000747 WebViewClassic.nativeOnTrimMemory(level);
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800748 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800749 }
750
751 // A final CallbackProxy shared by WebViewCore and BrowserFrame.
Jonathan Dixon3c909522012-02-28 18:45:06 +0000752 private CallbackProxy mCallbackProxy;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800753
Ben Murdoch99c12e82012-04-25 15:00:17 +0100754 private WebViewDatabaseClassic mDatabase;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800755
756 // SSL certificate for the main top-level page (if secure)
757 private SslCertificate mCertificate;
758
759 // Native WebView pointer that is 0 until the native object has been
760 // created.
761 private int mNativeClass;
762 // This would be final but it needs to be set to null when the WebView is
763 // destroyed.
764 private WebViewCore mWebViewCore;
765 // Handler for dispatching UI messages.
766 /* package */ final Handler mPrivateHandler = new PrivateHandler();
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800767 // Used to ignore changes to webkit text that arrives to the UI side after
768 // more key events.
769 private int mTextGeneration;
770
771 /* package */ void incrementTextGeneration() { mTextGeneration++; }
772
773 // Used by WebViewCore to create child views.
Jonathan Dixon3c909522012-02-28 18:45:06 +0000774 /* package */ ViewManager mViewManager;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800775
776 // Used to display in full screen mode
777 PluginFullScreenHolder mFullScreenHolder;
778
779 /**
780 * Position of the last touch event in pixels.
781 * Use integer to prevent loss of dragging delta calculation accuracy;
782 * which was done in float and converted to integer, and resulted in gradual
783 * and compounding touch position and view dragging mismatch.
784 */
785 private int mLastTouchX;
786 private int mLastTouchY;
787 private int mStartTouchX;
788 private int mStartTouchY;
789 private float mAverageAngle;
790
791 /**
792 * Time of the last touch event.
793 */
794 private long mLastTouchTime;
795
796 /**
797 * Time of the last time sending touch event to WebViewCore
798 */
799 private long mLastSentTouchTime;
800
801 /**
802 * The minimum elapsed time before sending another ACTION_MOVE event to
803 * WebViewCore. This really should be tuned for each type of the devices.
804 * For example in Google Map api test case, it takes Dream device at least
805 * 150ms to do a full cycle in the WebViewCore by processing a touch event,
806 * triggering the layout and drawing the picture. While the same process
807 * takes 60+ms on the current high speed device. If we make
808 * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
809 * to WebViewCore queue and the real layout and draw events will be pushed
810 * to further, which slows down the refresh rate. Choose 50 to favor the
811 * current high speed devices. For Dream like devices, 100 is a better
812 * choice. Maybe make this in the buildspec later.
813 * (Update 12/14/2010: changed to 0 since current device should be able to
814 * handle the raw events and Map team voted to have the raw events too.
815 */
816 private static final int TOUCH_SENT_INTERVAL = 0;
817 private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
818
819 /**
820 * Helper class to get velocity for fling
821 */
822 VelocityTracker mVelocityTracker;
823 private int mMaximumFling;
824 private float mLastVelocity;
825 private float mLastVelX;
826 private float mLastVelY;
827
828 // The id of the native layer being scrolled.
829 private int mCurrentScrollingLayerId;
830 private Rect mScrollingLayerRect = new Rect();
831
832 // only trigger accelerated fling if the new velocity is at least
833 // MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity
834 private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f;
835
836 /**
837 * Touch mode
Jeff Brown9d3bdbd2012-03-21 11:50:06 -0700838 * TODO: Some of this is now unnecessary as it is handled by
839 * WebInputTouchDispatcher (such as click, long press, and double tap).
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800840 */
841 private int mTouchMode = TOUCH_DONE_MODE;
842 private static final int TOUCH_INIT_MODE = 1;
843 private static final int TOUCH_DRAG_START_MODE = 2;
844 private static final int TOUCH_DRAG_MODE = 3;
845 private static final int TOUCH_SHORTPRESS_START_MODE = 4;
846 private static final int TOUCH_SHORTPRESS_MODE = 5;
847 private static final int TOUCH_DOUBLE_TAP_MODE = 6;
848 private static final int TOUCH_DONE_MODE = 7;
849 private static final int TOUCH_PINCH_DRAG = 8;
850 private static final int TOUCH_DRAG_LAYER_MODE = 9;
George Mountfcff68f2012-03-20 10:21:57 -0700851 private static final int TOUCH_DRAG_TEXT_MODE = 10;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800852
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800853 // true when the touch movement exceeds the slop
854 private boolean mConfirmMove;
George Mountf70276a2012-03-12 14:22:10 -0700855 private boolean mTouchInEditText;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800856
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800857 // Whether or not to draw the cursor ring.
858 private boolean mDrawCursorRing = true;
859
860 // true if onPause has been called (and not onResume)
861 private boolean mIsPaused;
862
863 private HitTestResult mInitialHitTestResult;
864 private WebKitHitTest mFocusedNode;
865
866 /**
867 * Customizable constant
868 */
869 // pre-computed square of ViewConfiguration.getScaledTouchSlop()
870 private int mTouchSlopSquare;
871 // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
872 private int mDoubleTapSlopSquare;
873 // pre-computed density adjusted navigation slop
874 private int mNavSlop;
875 // This should be ViewConfiguration.getTapTimeout()
876 // But system time out is 100ms, which is too short for the browser.
877 // In the browser, if it switches out of tap too soon, jump tap won't work.
878 // In addition, a double tap on a trackpad will always have a duration of
879 // 300ms, so this value must be at least that (otherwise we will timeout the
880 // first tap and convert it to a long press).
881 private static final int TAP_TIMEOUT = 300;
882 // This should be ViewConfiguration.getLongPressTimeout()
883 // But system time out is 500ms, which is too short for the browser.
884 // With a short timeout, it's difficult to treat trigger a short press.
885 private static final int LONG_PRESS_TIMEOUT = 1000;
886 // needed to avoid flinging after a pause of no movement
887 private static final int MIN_FLING_TIME = 250;
888 // draw unfiltered after drag is held without movement
889 private static final int MOTIONLESS_TIME = 100;
890 // The amount of content to overlap between two screens when going through
891 // pages with the space bar, in pixels.
892 private static final int PAGE_SCROLL_OVERLAP = 24;
893
894 /**
895 * These prevent calling requestLayout if either dimension is fixed. This
896 * depends on the layout parameters and the measure specs.
897 */
898 boolean mWidthCanMeasure;
899 boolean mHeightCanMeasure;
900
901 // Remember the last dimensions we sent to the native side so we can avoid
902 // sending the same dimensions more than once.
903 int mLastWidthSent;
904 int mLastHeightSent;
905 // Since view height sent to webkit could be fixed to avoid relayout, this
906 // value records the last sent actual view height.
907 int mLastActualHeightSent;
908
909 private int mContentWidth; // cache of value from WebViewCore
910 private int mContentHeight; // cache of value from WebViewCore
911
912 // Need to have the separate control for horizontal and vertical scrollbar
913 // style than the View's single scrollbar style
914 private boolean mOverlayHorizontalScrollbar = true;
915 private boolean mOverlayVerticalScrollbar = false;
916
917 // our standard speed. this way small distances will be traversed in less
918 // time than large distances, but we cap the duration, so that very large
919 // distances won't take too long to get there.
920 private static final int STD_SPEED = 480; // pixels per second
921 // time for the longest scroll animation
922 private static final int MAX_DURATION = 750; // milliseconds
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800923
924 // Used by OverScrollGlow
925 OverScroller mScroller;
George Mountf70276a2012-03-12 14:22:10 -0700926 Scroller mEditTextScroller;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800927
928 private boolean mInOverScrollMode = false;
929 private static Paint mOverScrollBackground;
930 private static Paint mOverScrollBorder;
931
932 private boolean mWrapContent;
933 private static final int MOTIONLESS_FALSE = 0;
934 private static final int MOTIONLESS_PENDING = 1;
935 private static final int MOTIONLESS_TRUE = 2;
936 private static final int MOTIONLESS_IGNORE = 3;
937 private int mHeldMotionless;
938
alanv525823a2012-05-16 20:01:51 -0700939 // Lazily-instantiated instance for injecting accessibility.
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800940 private AccessibilityInjector mAccessibilityInjector;
941
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800942 /**
943 * How long the caret handle will last without being touched.
944 */
945 private static final long CARET_HANDLE_STAMINA_MS = 3000;
946
947 private Drawable mSelectHandleLeft;
948 private Drawable mSelectHandleRight;
949 private Drawable mSelectHandleCenter;
George Mountb49f2bb2012-06-01 14:29:37 -0700950 private Point mSelectOffset;
951 private Point mSelectCursorBase = new Point();
952 private Rect mSelectHandleBaseBounds = new Rect();
953 private int mSelectCursorBaseLayerId;
954 private QuadF mSelectCursorBaseTextQuad = new QuadF();
955 private Point mSelectCursorExtent = new Point();
956 private Rect mSelectHandleExtentBounds = new Rect();
957 private int mSelectCursorExtentLayerId;
958 private QuadF mSelectCursorExtentTextQuad = new QuadF();
George Mountf9c1f992012-03-21 16:06:10 -0700959 private Point mSelectDraggingCursor;
George Mountf9c1f992012-03-21 16:06:10 -0700960 private QuadF mSelectDraggingTextQuad;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800961 private boolean mIsCaretSelection;
George Mountb49f2bb2012-06-01 14:29:37 -0700962 static final int HANDLE_ID_BASE = 0;
963 static final int HANDLE_ID_EXTENT = 1;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800964
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800965 // the color used to highlight the touch rectangles
966 static final int HIGHLIGHT_COLOR = 0x6633b5e5;
967 // the region indicating where the user touched on the screen
968 private Region mTouchHighlightRegion = new Region();
969 // the paint for the touch highlight
970 private Paint mTouchHightlightPaint = new Paint();
971 // debug only
972 private static final boolean DEBUG_TOUCH_HIGHLIGHT = true;
973 private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000;
974 private Paint mTouchCrossHairColor;
975 private int mTouchHighlightX;
976 private int mTouchHighlightY;
John Reck84fa2412012-05-03 15:56:58 -0700977 private boolean mShowTapHighlight;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800978
979 // Basically this proxy is used to tell the Video to update layer tree at
980 // SetBaseLayer time and to pause when WebView paused.
981 private HTML5VideoViewProxy mHTML5VideoViewProxy;
982
983 // If we are using a set picture, don't send view updates to webkit
984 private boolean mBlockWebkitViewMessages = false;
985
986 // cached value used to determine if we need to switch drawing models
987 private boolean mHardwareAccelSkia = false;
988
989 /*
990 * Private message ids
991 */
992 private static final int REMEMBER_PASSWORD = 1;
993 private static final int NEVER_REMEMBER_PASSWORD = 2;
994 private static final int SWITCH_TO_SHORTPRESS = 3;
995 private static final int SWITCH_TO_LONGPRESS = 4;
996 private static final int RELEASE_SINGLE_TAP = 5;
George Mountbcd5dd72012-03-01 08:39:03 -0800997 private static final int REQUEST_FORM_DATA = 6;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800998 private static final int DRAG_HELD_MOTIONLESS = 8;
Jonathan Dixonded37ed92012-02-13 17:26:46 -0800999 private static final int PREVENT_DEFAULT_TIMEOUT = 10;
1000 private static final int SCROLL_SELECT_TEXT = 11;
1001
1002
1003 private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD;
1004 private static final int LAST_PRIVATE_MSG_ID = SCROLL_SELECT_TEXT;
1005
1006 /*
1007 * Package message ids
1008 */
1009 static final int SCROLL_TO_MSG_ID = 101;
1010 static final int NEW_PICTURE_MSG_ID = 105;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001011 static final int WEBCORE_INITIALIZED_MSG_ID = 107;
1012 static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 108;
1013 static final int UPDATE_ZOOM_RANGE = 109;
John Reck4fa40372012-03-06 14:31:45 -08001014 static final int TAKE_FOCUS = 110;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001015 static final int CLEAR_TEXT_ENTRY = 111;
1016 static final int UPDATE_TEXT_SELECTION_MSG_ID = 112;
1017 static final int SHOW_RECT_MSG_ID = 113;
1018 static final int LONG_PRESS_CENTER = 114;
1019 static final int PREVENT_TOUCH_ID = 115;
1020 static final int WEBCORE_NEED_TOUCH_EVENTS = 116;
1021 // obj=Rect in doc coordinates
1022 static final int INVAL_RECT_MSG_ID = 117;
1023 static final int REQUEST_KEYBOARD = 118;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001024 static final int SHOW_FULLSCREEN = 120;
1025 static final int HIDE_FULLSCREEN = 121;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001026 static final int UPDATE_MATCH_COUNT = 126;
1027 static final int CENTER_FIT_RECT = 127;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001028 static final int SET_SCROLLBAR_MODES = 129;
Alan Viverettef4d45952013-02-01 16:14:33 -08001029 static final int HIT_TEST_RESULT = 130;
1030 static final int SAVE_WEBARCHIVE_FINISHED = 131;
1031 static final int SET_AUTOFILLABLE = 132;
1032 static final int AUTOFILL_COMPLETE = 133;
1033 static final int SCREEN_ON = 134;
1034 static final int UPDATE_ZOOM_DENSITY = 135;
1035 static final int EXIT_FULLSCREEN_VIDEO = 136;
1036 static final int COPY_TO_CLIPBOARD = 137;
1037 static final int INIT_EDIT_FIELD = 138;
1038 static final int REPLACE_TEXT = 139;
1039 static final int CLEAR_CARET_HANDLE = 140;
1040 static final int KEY_PRESS = 141;
1041 static final int RELOCATE_AUTO_COMPLETE_POPUP = 142;
1042 static final int FOCUS_NODE_CHANGED = 143;
1043 static final int AUTOFILL_FORM = 144;
1044 static final int SCROLL_EDIT_TEXT = 145;
1045 static final int EDIT_TEXT_SIZE_CHANGED = 146;
1046 static final int SHOW_CARET_HANDLE = 147;
1047 static final int UPDATE_CONTENT_BOUNDS = 148;
1048 static final int SCROLL_HANDLE_INTO_VIEW = 149;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001049
1050 private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
1051 private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;
1052
1053 static final String[] HandlerPrivateDebugString = {
1054 "REMEMBER_PASSWORD", // = 1;
1055 "NEVER_REMEMBER_PASSWORD", // = 2;
1056 "SWITCH_TO_SHORTPRESS", // = 3;
1057 "SWITCH_TO_LONGPRESS", // = 4;
1058 "RELEASE_SINGLE_TAP", // = 5;
1059 "REQUEST_FORM_DATA", // = 6;
1060 "RESUME_WEBCORE_PRIORITY", // = 7;
1061 "DRAG_HELD_MOTIONLESS", // = 8;
John Reck7c2e3292012-05-16 15:16:48 -07001062 "", // = 9;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001063 "PREVENT_DEFAULT_TIMEOUT", // = 10;
1064 "SCROLL_SELECT_TEXT" // = 11;
1065 };
1066
1067 static final String[] HandlerPackageDebugString = {
1068 "SCROLL_TO_MSG_ID", // = 101;
1069 "102", // = 102;
1070 "103", // = 103;
1071 "104", // = 104;
1072 "NEW_PICTURE_MSG_ID", // = 105;
1073 "UPDATE_TEXT_ENTRY_MSG_ID", // = 106;
1074 "WEBCORE_INITIALIZED_MSG_ID", // = 107;
1075 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 108;
1076 "UPDATE_ZOOM_RANGE", // = 109;
1077 "UNHANDLED_NAV_KEY", // = 110;
1078 "CLEAR_TEXT_ENTRY", // = 111;
1079 "UPDATE_TEXT_SELECTION_MSG_ID", // = 112;
1080 "SHOW_RECT_MSG_ID", // = 113;
1081 "LONG_PRESS_CENTER", // = 114;
1082 "PREVENT_TOUCH_ID", // = 115;
1083 "WEBCORE_NEED_TOUCH_EVENTS", // = 116;
1084 "INVAL_RECT_MSG_ID", // = 117;
1085 "REQUEST_KEYBOARD", // = 118;
1086 "DO_MOTION_UP", // = 119;
1087 "SHOW_FULLSCREEN", // = 120;
1088 "HIDE_FULLSCREEN", // = 121;
1089 "DOM_FOCUS_CHANGED", // = 122;
1090 "REPLACE_BASE_CONTENT", // = 123;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001091 "RETURN_LABEL", // = 125;
1092 "UPDATE_MATCH_COUNT", // = 126;
1093 "CENTER_FIT_RECT", // = 127;
1094 "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
1095 "SET_SCROLLBAR_MODES", // = 129;
1096 "SELECTION_STRING_CHANGED", // = 130;
1097 "SET_TOUCH_HIGHLIGHT_RECTS", // = 131;
1098 "SAVE_WEBARCHIVE_FINISHED", // = 132;
1099 "SET_AUTOFILLABLE", // = 133;
1100 "AUTOFILL_COMPLETE", // = 134;
1101 "SELECT_AT", // = 135;
1102 "SCREEN_ON", // = 136;
1103 "ENTER_FULLSCREEN_VIDEO", // = 137;
1104 "UPDATE_SELECTION", // = 138;
1105 "UPDATE_ZOOM_DENSITY" // = 139;
1106 };
1107
1108 // If the site doesn't use the viewport meta tag to specify the viewport,
1109 // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
1110 static final int DEFAULT_VIEWPORT_WIDTH = 980;
1111
1112 // normally we try to fit the content to the minimum preferred width
1113 // calculated by the Webkit. To avoid the bad behavior when some site's
1114 // minimum preferred width keeps growing when changing the viewport width or
1115 // the minimum preferred width is huge, an upper limit is needed.
1116 static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
1117
1118 // initial scale in percent. 0 means using default.
1119 private int mInitialScaleInPercent = 0;
1120
1121 // Whether or not a scroll event should be sent to webkit. This is only set
1122 // to false when restoring the scroll position.
1123 private boolean mSendScrollEvent = true;
1124
1125 private int mSnapScrollMode = SNAP_NONE;
1126 private static final int SNAP_NONE = 0;
1127 private static final int SNAP_LOCK = 1; // not a separate state
1128 private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
1129 private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
1130 private boolean mSnapPositive;
1131
1132 // keep these in sync with their counterparts in WebView.cpp
1133 private static final int DRAW_EXTRAS_NONE = 0;
1134 private static final int DRAW_EXTRAS_SELECTION = 1;
1135 private static final int DRAW_EXTRAS_CURSOR_RING = 2;
1136
1137 // keep this in sync with WebCore:ScrollbarMode in WebKit
1138 private static final int SCROLLBAR_AUTO = 0;
1139 private static final int SCROLLBAR_ALWAYSOFF = 1;
1140 // as we auto fade scrollbar, this is ignored.
1141 private static final int SCROLLBAR_ALWAYSON = 2;
1142 private int mHorizontalScrollBarMode = SCROLLBAR_AUTO;
1143 private int mVerticalScrollBarMode = SCROLLBAR_AUTO;
1144
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001145 /**
1146 * Max distance to overscroll by in pixels.
1147 * This how far content can be pulled beyond its normal bounds by the user.
1148 */
1149 private int mOverscrollDistance;
1150
1151 /**
1152 * Max distance to overfling by in pixels.
1153 * This is how far flinged content can move beyond the end of its normal bounds.
1154 */
1155 private int mOverflingDistance;
1156
1157 private OverScrollGlow mOverScrollGlow;
1158
1159 // Used to match key downs and key ups
1160 private Vector<Integer> mKeysPressed;
1161
1162 /* package */ static boolean mLogEvent = true;
1163
1164 // for event log
1165 private long mLastTouchUpTime = 0;
1166
1167 private WebViewCore.AutoFillData mAutoFillData;
1168
1169 private static boolean sNotificationsEnabled = true;
1170
1171 /**
1172 * URI scheme for telephone number
1173 */
1174 public static final String SCHEME_TEL = "tel:";
1175 /**
1176 * URI scheme for email address
1177 */
1178 public static final String SCHEME_MAILTO = "mailto:";
1179 /**
1180 * URI scheme for map address
1181 */
1182 public static final String SCHEME_GEO = "geo:0,0?q=";
1183
1184 private int mBackgroundColor = Color.WHITE;
1185
1186 private static final long SELECT_SCROLL_INTERVAL = 1000 / 60; // 60 / second
1187 private int mAutoScrollX = 0;
1188 private int mAutoScrollY = 0;
1189 private int mMinAutoScrollX = 0;
1190 private int mMaxAutoScrollX = 0;
1191 private int mMinAutoScrollY = 0;
1192 private int mMaxAutoScrollY = 0;
1193 private Rect mScrollingLayerBounds = new Rect();
1194 private boolean mSentAutoScrollMessage = false;
1195
1196 // used for serializing asynchronously handled touch events.
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001197 private WebViewInputDispatcher mInputDispatcher;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001198
1199 // Used to track whether picture updating was paused due to a window focus change.
1200 private boolean mPictureUpdatePausedForFocusChange = false;
1201
1202 // Used to notify listeners of a new picture.
1203 private PictureListener mPictureListener;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001204
Victoria Leaseabeb6a72012-03-05 16:29:12 -08001205 // Used to notify listeners about find-on-page results.
Victoria Leased405a432012-03-22 15:53:48 -07001206 private WebView.FindListener mFindListener;
Victoria Leaseabeb6a72012-03-05 16:29:12 -08001207
Michael Kolbd2bfdfd2012-03-30 13:06:00 -07001208 // Used to prevent resending save password message
1209 private Message mResumeMsg;
1210
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001211 /**
1212 * Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
1213 */
1214 static class FocusNodeHref {
1215 static final String TITLE = "title";
1216 static final String URL = "url";
1217 static final String SRC = "src";
1218 }
1219
Jonathan Dixon3c909522012-02-28 18:45:06 +00001220 public WebViewClassic(WebView webView, WebView.PrivateAccess privateAccess) {
1221 mWebView = webView;
1222 mWebViewPrivate = privateAccess;
1223 mContext = webView.getContext();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001224 }
1225
1226 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00001227 * See {@link WebViewProvider#init(Map, boolean)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001228 */
Jonathan Dixon3c909522012-02-28 18:45:06 +00001229 @Override
1230 public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00001231 Context context = mContext;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001232
1233 // Used by the chrome stack to find application paths
1234 JniUtil.setContext(context);
1235
1236 mCallbackProxy = new CallbackProxy(context, this);
1237 mViewManager = new ViewManager(this);
1238 L10nUtils.setApplicationContext(context.getApplicationContext());
1239 mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces);
Ben Murdoch99c12e82012-04-25 15:00:17 +01001240 mDatabase = WebViewDatabaseClassic.getInstance(context);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001241 mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
1242 mZoomManager = new ZoomManager(this, mCallbackProxy);
1243
1244 /* The init method must follow the creation of certain member variables,
1245 * such as the mZoomManager.
1246 */
1247 init();
1248 setupPackageListener(context);
1249 setupProxyListener(context);
1250 setupTrustStorageListener(context);
1251 updateMultiTouchSupport(context);
1252
1253 if (privateBrowsing) {
1254 startPrivateBrowsing();
1255 }
1256
1257 mAutoFillData = new WebViewCore.AutoFillData();
George Mountf70276a2012-03-12 14:22:10 -07001258 mEditTextScroller = new Scroller(context);
kiwon72034952013-02-07 20:02:39 +09001259
1260 // Calculate channel distance
1261 calculateChannelDistance(context);
1262 }
1263
1264 /**
1265 * Calculate sChannelDistance based on the screen information.
1266 * @param context A Context object used to access application assets.
1267 */
1268 private void calculateChannelDistance(Context context) {
1269 // The channel distance is adjusted for density and screen size
1270 final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
1271 final double screenSize = Math.hypot((double)(metrics.widthPixels/metrics.densityDpi),
1272 (double)(metrics.heightPixels/metrics.densityDpi));
1273 if (screenSize < 3.0) {
1274 sChannelDistance = 16;
1275 } else if (screenSize < 5.0) {
1276 sChannelDistance = 22;
1277 } else if (screenSize < 7.0) {
1278 sChannelDistance = 28;
1279 } else {
1280 sChannelDistance = 34;
1281 }
1282 sChannelDistance = (int)(sChannelDistance * metrics.density);
1283 if (sChannelDistance < 16) sChannelDistance = 16;
1284
1285 if (DebugFlags.WEB_VIEW) {
1286 Log.v(LOGTAG, "sChannelDistance : " + sChannelDistance
1287 + ", density : " + metrics.density
1288 + ", screenSize : " + screenSize
1289 + ", metrics.heightPixels : " + metrics.heightPixels
1290 + ", metrics.widthPixels : " + metrics.widthPixels
1291 + ", metrics.densityDpi : " + metrics.densityDpi);
1292 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001293 }
1294
Jonathan Dixoncd93e152012-03-02 19:19:44 +00001295 // WebViewProvider bindings
Jonathan Dixon3c909522012-02-28 18:45:06 +00001296
1297 static class Factory implements WebViewFactoryProvider, WebViewFactoryProvider.Statics {
Torne (Richard Coles)03ce9b32013-06-12 16:02:03 +01001298 Factory() {
1299 // Touch JniUtil and WebViewCore in case this is being called from
1300 // WebViewFactory.Preloader, to ensure that the JNI libraries that they use are
1301 // preloaded in the zygote.
1302 try {
1303 Class.forName("android.webkit.JniUtil");
1304 Class.forName("android.webkit.WebViewCore");
1305 } catch (ClassNotFoundException e) {
1306 Log.e(LOGTAG, "failed to load JNI libraries");
1307 throw new AndroidRuntimeException(e);
1308 }
1309 }
1310
Jonathan Dixon3c909522012-02-28 18:45:06 +00001311 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00001312 public String findAddress(String addr) {
1313 return WebViewClassic.findAddress(addr);
1314 }
1315 @Override
1316 public void setPlatformNotificationsEnabled(boolean enable) {
1317 if (enable) {
1318 WebViewClassic.enablePlatformNotifications();
1319 } else {
1320 WebViewClassic.disablePlatformNotifications();
1321 }
1322 }
1323
Jonathan Dixond3101b12012-04-12 20:51:51 +01001324 @Override
1325 public Statics getStatics() { return this; }
1326
1327 @Override
1328 public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
1329 return new WebViewClassic(webView, privateAccess);
1330 }
1331
1332 @Override
1333 public GeolocationPermissions getGeolocationPermissions() {
1334 return GeolocationPermissionsClassic.getInstance();
1335 }
1336
1337 @Override
1338 public CookieManager getCookieManager() {
1339 return CookieManagerClassic.getInstance();
1340 }
1341
1342 @Override
1343 public WebIconDatabase getWebIconDatabase() {
1344 return WebIconDatabaseClassic.getInstance();
1345 }
1346
1347 @Override
1348 public WebStorage getWebStorage() {
1349 return WebStorageClassic.getInstance();
1350 }
Ben Murdoch99c12e82012-04-25 15:00:17 +01001351
1352 @Override
1353 public WebViewDatabase getWebViewDatabase(Context context) {
1354 return WebViewDatabaseClassic.getInstance(context);
1355 }
George Mount9f410c52012-08-10 15:29:30 -07001356
1357 @Override
1358 public String getDefaultUserAgent(Context context) {
1359 return WebSettingsClassic.getDefaultUserAgentForLocale(context,
1360 Locale.getDefault());
1361 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00001362 }
1363
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001364 private void onHandleUiEvent(MotionEvent event, int eventType, int flags) {
1365 switch (eventType) {
1366 case WebViewInputDispatcher.EVENT_TYPE_LONG_PRESS:
1367 HitTestResult hitTest = getHitTestResult();
George Mounte3e26c42012-04-19 09:57:23 -07001368 if (hitTest != null) {
John Recka8ae3e92012-06-13 10:37:40 -07001369 mWebView.performLongClick();
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001370 }
1371 break;
1372 case WebViewInputDispatcher.EVENT_TYPE_DOUBLE_TAP:
1373 mZoomManager.handleDoubleTap(event.getX(), event.getY());
1374 break;
1375 case WebViewInputDispatcher.EVENT_TYPE_TOUCH:
1376 onHandleUiTouchEvent(event);
1377 break;
John Reck9155cba2012-05-03 09:51:32 -07001378 case WebViewInputDispatcher.EVENT_TYPE_CLICK:
1379 if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) {
1380 mWebView.playSoundEffect(SoundEffectConstants.CLICK);
1381 overrideLoading(mFocusedNode.mIntentUrl);
1382 }
1383 break;
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001384 }
1385 }
1386
1387 private void onHandleUiTouchEvent(MotionEvent ev) {
1388 final ScaleGestureDetector detector =
Adam Powell1027ed22012-08-31 17:19:24 -07001389 mZoomManager.getScaleGestureDetector();
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001390
Adam Powell1027ed22012-08-31 17:19:24 -07001391 int action = ev.getActionMasked();
1392 final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
1393 final boolean configChanged =
1394 action == MotionEvent.ACTION_POINTER_UP ||
1395 action == MotionEvent.ACTION_POINTER_DOWN;
1396 final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
1397
1398 // Determine focal point
1399 float sumX = 0, sumY = 0;
1400 final int count = ev.getPointerCount();
1401 for (int i = 0; i < count; i++) {
1402 if (skipIndex == i) continue;
1403 sumX += ev.getX(i);
1404 sumY += ev.getY(i);
1405 }
1406 final int div = pointerUp ? count - 1 : count;
1407 float x = sumX / div;
1408 float y = sumY / div;
1409
1410 if (configChanged) {
1411 mLastTouchX = Math.round(x);
1412 mLastTouchY = Math.round(y);
1413 mLastTouchTime = ev.getEventTime();
1414 mWebView.cancelLongPress();
1415 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
1416 }
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001417
1418 if (detector != null) {
1419 detector.onTouchEvent(ev);
1420 if (detector.isInProgress()) {
1421 mLastTouchTime = ev.getEventTime();
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001422
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001423 if (!mZoomManager.supportsPanDuringZoom()) {
1424 return;
1425 }
1426 mTouchMode = TOUCH_DRAG_MODE;
1427 if (mVelocityTracker == null) {
1428 mVelocityTracker = VelocityTracker.obtain();
1429 }
1430 }
1431 }
1432
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001433 if (action == MotionEvent.ACTION_POINTER_DOWN) {
1434 cancelTouch();
1435 action = MotionEvent.ACTION_DOWN;
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07001436 } else if (action == MotionEvent.ACTION_MOVE) {
1437 // negative x or y indicate it is on the edge, skip it.
1438 if (x < 0 || y < 0) {
1439 return;
1440 }
1441 }
1442
1443 handleTouchEventCommon(ev, action, Math.round(x), Math.round(y));
1444 }
1445
Jonathan Dixon3c909522012-02-28 18:45:06 +00001446 // The webview that is bound to this WebViewClassic instance. Primarily needed for supplying
1447 // as the first param in the WebViewClient and WebChromeClient callbacks.
1448 final private WebView mWebView;
1449 // Callback interface, provides priviledged access into the WebView instance.
1450 final private WebView.PrivateAccess mWebViewPrivate;
1451 // Cached reference to mWebView.getContext(), for convenience.
1452 final private Context mContext;
1453
1454 /**
1455 * @return The webview proxy that this classic webview is bound to.
1456 */
1457 public WebView getWebView() {
1458 return mWebView;
1459 }
1460
1461 @Override
1462 public ViewDelegate getViewDelegate() {
1463 return this;
1464 }
1465
1466 @Override
1467 public ScrollDelegate getScrollDelegate() {
1468 return this;
1469 }
1470
1471 public static WebViewClassic fromWebView(WebView webView) {
1472 return webView == null ? null : (WebViewClassic) webView.getWebViewProvider();
1473 }
1474
1475 // Accessors, purely for convenience (and to reduce code churn during webview proxy migration).
1476 int getScrollX() {
1477 return mWebView.getScrollX();
1478 }
1479
1480 int getScrollY() {
1481 return mWebView.getScrollY();
1482 }
1483
1484 int getWidth() {
1485 return mWebView.getWidth();
1486 }
1487
1488 int getHeight() {
1489 return mWebView.getHeight();
1490 }
1491
1492 Context getContext() {
1493 return mContext;
1494 }
1495
1496 void invalidate() {
1497 mWebView.invalidate();
1498 }
1499
1500 // Setters for the Scroll X & Y, without invoking the onScrollChanged etc code paths.
1501 void setScrollXRaw(int mScrollX) {
1502 mWebViewPrivate.setScrollXRaw(mScrollX);
1503 }
1504
1505 void setScrollYRaw(int mScrollY) {
1506 mWebViewPrivate.setScrollYRaw(mScrollY);
1507 }
1508
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001509 private static class TrustStorageListener extends BroadcastReceiver {
1510 @Override
1511 public void onReceive(Context context, Intent intent) {
1512 if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
1513 handleCertTrustChanged();
1514 }
1515 }
1516 }
1517 private static TrustStorageListener sTrustStorageListener;
1518
1519 /**
1520 * Handles update to the trust storage.
1521 */
1522 private static void handleCertTrustChanged() {
1523 // send a message for indicating trust storage change
1524 WebViewCore.sendStaticMessage(EventHub.TRUST_STORAGE_UPDATED, null);
1525 }
1526
1527 /*
1528 * @param context This method expects this to be a valid context.
1529 */
1530 private static void setupTrustStorageListener(Context context) {
1531 if (sTrustStorageListener != null ) {
1532 return;
1533 }
1534 IntentFilter filter = new IntentFilter();
1535 filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
1536 sTrustStorageListener = new TrustStorageListener();
1537 Intent current =
1538 context.getApplicationContext().registerReceiver(sTrustStorageListener, filter);
1539 if (current != null) {
1540 handleCertTrustChanged();
1541 }
1542 }
1543
1544 private static class ProxyReceiver extends BroadcastReceiver {
1545 @Override
1546 public void onReceive(Context context, Intent intent) {
1547 if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
1548 handleProxyBroadcast(intent);
1549 }
1550 }
1551 }
1552
1553 /*
1554 * Receiver for PROXY_CHANGE_ACTION, will be null when it is not added handling broadcasts.
1555 */
1556 private static ProxyReceiver sProxyReceiver;
1557
1558 /*
1559 * @param context This method expects this to be a valid context
1560 */
1561 private static synchronized void setupProxyListener(Context context) {
1562 if (sProxyReceiver != null || sNotificationsEnabled == false) {
1563 return;
1564 }
1565 IntentFilter filter = new IntentFilter();
1566 filter.addAction(Proxy.PROXY_CHANGE_ACTION);
1567 sProxyReceiver = new ProxyReceiver();
1568 Intent currentProxy = context.getApplicationContext().registerReceiver(
1569 sProxyReceiver, filter);
1570 if (currentProxy != null) {
1571 handleProxyBroadcast(currentProxy);
1572 }
1573 }
1574
1575 /*
1576 * @param context This method expects this to be a valid context
1577 */
1578 private static synchronized void disableProxyListener(Context context) {
1579 if (sProxyReceiver == null)
1580 return;
1581
1582 context.getApplicationContext().unregisterReceiver(sProxyReceiver);
1583 sProxyReceiver = null;
1584 }
1585
1586 private static void handleProxyBroadcast(Intent intent) {
1587 ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
1588 if (proxyProperties == null || proxyProperties.getHost() == null) {
1589 WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, null);
1590 return;
1591 }
1592 WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, proxyProperties);
1593 }
1594
1595 /*
1596 * A variable to track if there is a receiver added for ACTION_PACKAGE_ADDED
1597 * or ACTION_PACKAGE_REMOVED.
1598 */
1599 private static boolean sPackageInstallationReceiverAdded = false;
1600
1601 /*
1602 * A set of Google packages we monitor for the
1603 * navigator.isApplicationInstalled() API. Add additional packages as
1604 * needed.
1605 */
1606 private static Set<String> sGoogleApps;
1607 static {
1608 sGoogleApps = new HashSet<String>();
1609 sGoogleApps.add("com.google.android.youtube");
1610 }
1611
1612 private static class PackageListener extends BroadcastReceiver {
1613 @Override
1614 public void onReceive(Context context, Intent intent) {
1615 final String action = intent.getAction();
1616 final String packageName = intent.getData().getSchemeSpecificPart();
1617 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1618 if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
1619 // if it is replacing, refreshPlugins() when adding
1620 return;
1621 }
1622
1623 if (sGoogleApps.contains(packageName)) {
1624 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
1625 WebViewCore.sendStaticMessage(EventHub.ADD_PACKAGE_NAME, packageName);
1626 } else {
1627 WebViewCore.sendStaticMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
1628 }
1629 }
1630
1631 PluginManager pm = PluginManager.getInstance(context);
1632 if (pm.containsPluginPermissionAndSignatures(packageName)) {
1633 pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action));
1634 }
1635 }
1636 }
1637
1638 private void setupPackageListener(Context context) {
1639
1640 /*
1641 * we must synchronize the instance check and the creation of the
1642 * receiver to ensure that only ONE receiver exists for all WebView
1643 * instances.
1644 */
Jonathan Dixon3c909522012-02-28 18:45:06 +00001645 synchronized (WebViewClassic.class) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001646
1647 // if the receiver already exists then we do not need to register it
1648 // again
1649 if (sPackageInstallationReceiverAdded) {
1650 return;
1651 }
1652
1653 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1654 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1655 filter.addDataScheme("package");
1656 BroadcastReceiver packageListener = new PackageListener();
1657 context.getApplicationContext().registerReceiver(packageListener, filter);
1658 sPackageInstallationReceiverAdded = true;
1659 }
1660
1661 // check if any of the monitored apps are already installed
1662 AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() {
1663
1664 @Override
1665 protected Set<String> doInBackground(Void... unused) {
1666 Set<String> installedPackages = new HashSet<String>();
1667 PackageManager pm = mContext.getPackageManager();
1668 for (String name : sGoogleApps) {
1669 try {
1670 pm.getPackageInfo(name,
1671 PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
1672 installedPackages.add(name);
1673 } catch (PackageManager.NameNotFoundException e) {
1674 // package not found
1675 }
1676 }
1677 return installedPackages;
1678 }
1679
1680 // Executes on the UI thread
1681 @Override
1682 protected void onPostExecute(Set<String> installedPackages) {
1683 if (mWebViewCore != null) {
1684 mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages);
1685 }
1686 }
1687 };
1688 task.execute();
1689 }
1690
1691 void updateMultiTouchSupport(Context context) {
1692 mZoomManager.updateMultiTouchSupport(context);
1693 }
1694
alanvc98b3462012-08-27 15:21:09 -07001695 void updateJavaScriptEnabled(boolean enabled) {
1696 if (isAccessibilityInjectionEnabled()) {
1697 getAccessibilityInjector().updateJavaScriptEnabled(enabled);
1698 }
1699 }
1700
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001701 private void init() {
Jonathan Dixon3c909522012-02-28 18:45:06 +00001702 OnTrimMemoryListener.init(mContext);
Jonathan Dixon3c909522012-02-28 18:45:06 +00001703 mWebView.setWillNotDraw(false);
Jonathan Dixon3c909522012-02-28 18:45:06 +00001704 mWebView.setClickable(true);
1705 mWebView.setLongClickable(true);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001706
Jonathan Dixon3c909522012-02-28 18:45:06 +00001707 final ViewConfiguration configuration = ViewConfiguration.get(mContext);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001708 int slop = configuration.getScaledTouchSlop();
1709 mTouchSlopSquare = slop * slop;
1710 slop = configuration.getScaledDoubleTapSlop();
1711 mDoubleTapSlopSquare = slop * slop;
John Reck3f5a59a2012-09-27 16:31:15 -07001712 final float density = WebViewCore.getFixedDisplayDensity(mContext);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001713 // use one line height, 16 based on our current default font, for how
1714 // far we allow a touch be away from the edge of a link
1715 mNavSlop = (int) (16 * density);
1716 mZoomManager.init(density);
1717 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
1718
1719 // Compute the inverse of the density squared.
1720 DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
1721
1722 mOverscrollDistance = configuration.getScaledOverscrollDistance();
1723 mOverflingDistance = configuration.getScaledOverflingDistance();
1724
Jonathan Dixon3c909522012-02-28 18:45:06 +00001725 setScrollBarStyle(mWebViewPrivate.super_getScrollBarStyle());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001726 // Initially use a size of two, since the user is likely to only hold
1727 // down two keys at a time (shift + another key)
1728 mKeysPressed = new Vector<Integer>(2);
1729 mHTML5VideoViewProxy = null ;
1730 }
1731
1732 @Override
1733 public boolean shouldDelayChildPressedState() {
1734 return true;
1735 }
1736
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001737 @Override
alanv448902d2012-05-16 20:27:47 -07001738 public boolean performAccessibilityAction(int action, Bundle arguments) {
1739 if (!mWebView.isEnabled()) {
1740 // Only default actions are supported while disabled.
1741 return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
1742 }
1743
alanv500b91e2012-05-22 14:22:48 -07001744 if (getAccessibilityInjector().supportsAccessibilityAction(action)) {
1745 return getAccessibilityInjector().performAccessibilityAction(action, arguments);
alanv448902d2012-05-16 20:27:47 -07001746 }
1747
1748 switch (action) {
1749 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
1750 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
1751 final int convertedContentHeight = contentToViewY(getContentHeight());
1752 final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
1753 - mWebView.getPaddingBottom();
1754 final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
1755 final boolean canScrollBackward = (getScrollY() > 0);
1756 final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
1757 if ((action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) && canScrollBackward) {
1758 mWebView.scrollBy(0, adjustedViewHeight);
1759 return true;
1760 }
1761 if ((action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) && canScrollForward) {
1762 mWebView.scrollBy(0, -adjustedViewHeight);
1763 return true;
1764 }
1765 return false;
1766 }
1767 }
1768
1769 return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
1770 }
1771
1772 @Override
Ben Murdoche3f90712013-06-05 14:19:48 +01001773 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
1774 return null;
1775 }
1776
1777 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001778 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
alanv448902d2012-05-16 20:27:47 -07001779 if (!mWebView.isEnabled()) {
1780 // Only default actions are supported while disabled.
1781 return;
1782 }
1783
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001784 info.setScrollable(isScrollableForAccessibility());
alanv448902d2012-05-16 20:27:47 -07001785
1786 final int convertedContentHeight = contentToViewY(getContentHeight());
1787 final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
1788 - mWebView.getPaddingBottom();
1789 final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
1790 final boolean canScrollBackward = (getScrollY() > 0);
1791 final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
1792
1793 if (canScrollForward) {
1794 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
1795 }
1796
1797 if (canScrollForward) {
1798 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
1799 }
1800
alanv500b91e2012-05-22 14:22:48 -07001801 getAccessibilityInjector().onInitializeAccessibilityNodeInfo(info);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001802 }
1803
1804 @Override
1805 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001806 event.setScrollable(isScrollableForAccessibility());
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00001807 event.setScrollX(getScrollX());
1808 event.setScrollY(getScrollY());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001809 final int convertedContentWidth = contentToViewX(getContentWidth());
Jonathan Dixon3c909522012-02-28 18:45:06 +00001810 final int adjustedViewWidth = getWidth() - mWebView.getPaddingLeft()
1811 - mWebView.getPaddingLeft();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001812 event.setMaxScrollX(Math.max(convertedContentWidth - adjustedViewWidth, 0));
1813 final int convertedContentHeight = contentToViewY(getContentHeight());
Jonathan Dixon3c909522012-02-28 18:45:06 +00001814 final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
1815 - mWebView.getPaddingBottom();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001816 event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
1817 }
1818
Alan Viverettef4d45952013-02-01 16:14:33 -08001819 /* package */ void handleSelectionChangedWebCoreThread(String selection, int token) {
1820 if (isAccessibilityInjectionEnabled()) {
1821 getAccessibilityInjector().onSelectionStringChangedWebCoreThread(selection, token);
1822 }
1823 }
1824
alanvfdfd0d82012-08-09 18:05:17 -07001825 private boolean isAccessibilityInjectionEnabled() {
1826 final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
1827 if (!manager.isEnabled()) {
1828 return false;
1829 }
1830
1831 // Accessibility scripts should be injected only when a speaking service
1832 // is enabled. This may need to change later to accommodate Braille.
1833 final List<AccessibilityServiceInfo> services = manager.getEnabledAccessibilityServiceList(
1834 AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1835 if (services.isEmpty()) {
1836 return false;
1837 }
1838
1839 return true;
alanv525823a2012-05-16 20:01:51 -07001840 }
1841
1842 private AccessibilityInjector getAccessibilityInjector() {
1843 if (mAccessibilityInjector == null) {
1844 mAccessibilityInjector = new AccessibilityInjector(this);
1845 }
1846 return mAccessibilityInjector;
1847 }
1848
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001849 private boolean isScrollableForAccessibility() {
Jonathan Dixon3c909522012-02-28 18:45:06 +00001850 return (contentToViewX(getContentWidth()) > getWidth() - mWebView.getPaddingLeft()
1851 - mWebView.getPaddingRight()
1852 || contentToViewY(getContentHeight()) > getHeight() - mWebView.getPaddingTop()
1853 - mWebView.getPaddingBottom());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001854 }
1855
1856 @Override
1857 public void setOverScrollMode(int mode) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00001858 if (mode != View.OVER_SCROLL_NEVER) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001859 if (mOverScrollGlow == null) {
1860 mOverScrollGlow = new OverScrollGlow(this);
1861 }
1862 } else {
1863 mOverScrollGlow = null;
1864 }
1865 }
1866
1867 /* package */ void adjustDefaultZoomDensity(int zoomDensity) {
John Reck3f5a59a2012-09-27 16:31:15 -07001868 final float density = WebViewCore.getFixedDisplayDensity(mContext)
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001869 * 100 / zoomDensity;
1870 updateDefaultZoomDensity(density);
1871 }
1872
1873 /* package */ void updateDefaultZoomDensity(float density) {
1874 mNavSlop = (int) (16 * density);
1875 mZoomManager.updateDefaultZoomDensity(density);
1876 }
1877
John Reck41f73bd2012-05-14 17:59:13 -07001878 /* package */ int getScaledNavSlop() {
1879 return viewToContentDimension(mNavSlop);
1880 }
1881
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001882 /* package */ boolean onSavePassword(String schemePlusHost, String username,
1883 String password, final Message resumeMsg) {
Michael Kolbd2bfdfd2012-03-30 13:06:00 -07001884 boolean rVal = false;
1885 if (resumeMsg == null) {
1886 // null resumeMsg implies saving password silently
1887 mDatabase.setUsernamePassword(schemePlusHost, username, password);
1888 } else {
1889 if (mResumeMsg != null) {
1890 Log.w(LOGTAG, "onSavePassword should not be called while dialog is up");
1891 resumeMsg.sendToTarget();
1892 return true;
1893 }
1894 mResumeMsg = resumeMsg;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001895 final Message remember = mPrivateHandler.obtainMessage(
1896 REMEMBER_PASSWORD);
1897 remember.getData().putString("host", schemePlusHost);
1898 remember.getData().putString("username", username);
1899 remember.getData().putString("password", password);
1900 remember.obj = resumeMsg;
1901
1902 final Message neverRemember = mPrivateHandler.obtainMessage(
1903 NEVER_REMEMBER_PASSWORD);
1904 neverRemember.getData().putString("host", schemePlusHost);
1905 neverRemember.getData().putString("username", username);
1906 neverRemember.getData().putString("password", password);
1907 neverRemember.obj = resumeMsg;
1908
Johan Redestig8655e902012-08-30 10:13:41 +02001909 mSavePasswordDialog = new AlertDialog.Builder(mContext)
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001910 .setTitle(com.android.internal.R.string.save_password_label)
1911 .setMessage(com.android.internal.R.string.save_password_message)
1912 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
1913 new DialogInterface.OnClickListener() {
1914 @Override
1915 public void onClick(DialogInterface dialog, int which) {
Michael Kolbd2bfdfd2012-03-30 13:06:00 -07001916 if (mResumeMsg != null) {
1917 resumeMsg.sendToTarget();
1918 mResumeMsg = null;
1919 }
Johan Redestig8655e902012-08-30 10:13:41 +02001920 mSavePasswordDialog = null;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001921 }
1922 })
1923 .setNeutralButton(com.android.internal.R.string.save_password_remember,
1924 new DialogInterface.OnClickListener() {
1925 @Override
1926 public void onClick(DialogInterface dialog, int which) {
Michael Kolbd2bfdfd2012-03-30 13:06:00 -07001927 if (mResumeMsg != null) {
1928 remember.sendToTarget();
1929 mResumeMsg = null;
1930 }
Johan Redestig8655e902012-08-30 10:13:41 +02001931 mSavePasswordDialog = null;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001932 }
1933 })
1934 .setNegativeButton(com.android.internal.R.string.save_password_never,
1935 new DialogInterface.OnClickListener() {
1936 @Override
1937 public void onClick(DialogInterface dialog, int which) {
Michael Kolbd2bfdfd2012-03-30 13:06:00 -07001938 if (mResumeMsg != null) {
1939 neverRemember.sendToTarget();
1940 mResumeMsg = null;
1941 }
Johan Redestig8655e902012-08-30 10:13:41 +02001942 mSavePasswordDialog = null;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001943 }
1944 })
John Reck095e8aa2012-09-10 16:07:04 -07001945 .setOnDismissListener(new DialogInterface.OnDismissListener() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001946 @Override
John Reck095e8aa2012-09-10 16:07:04 -07001947 public void onDismiss(DialogInterface dialog) {
Michael Kolbd2bfdfd2012-03-30 13:06:00 -07001948 if (mResumeMsg != null) {
1949 resumeMsg.sendToTarget();
1950 mResumeMsg = null;
1951 }
Johan Redestig8655e902012-08-30 10:13:41 +02001952 mSavePasswordDialog = null;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001953 }
1954 }).show();
1955 // Return true so that WebViewCore will pause while the dialog is
1956 // up.
1957 rVal = true;
1958 }
Michael Kolbd2bfdfd2012-03-30 13:06:00 -07001959 return rVal;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001960 }
1961
1962 @Override
1963 public void setScrollBarStyle(int style) {
1964 if (style == View.SCROLLBARS_INSIDE_INSET
1965 || style == View.SCROLLBARS_OUTSIDE_INSET) {
1966 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
1967 } else {
1968 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
1969 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001970 }
1971
1972 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00001973 * See {@link WebView#setHorizontalScrollbarOverlay(boolean)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001974 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00001975 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001976 public void setHorizontalScrollbarOverlay(boolean overlay) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001977 mOverlayHorizontalScrollbar = overlay;
1978 }
1979
1980 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00001981 * See {@link WebView#setVerticalScrollbarOverlay(boolean)
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001982 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00001983 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001984 public void setVerticalScrollbarOverlay(boolean overlay) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001985 mOverlayVerticalScrollbar = overlay;
1986 }
1987
1988 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00001989 * See {@link WebView#overlayHorizontalScrollbar()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001990 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00001991 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001992 public boolean overlayHorizontalScrollbar() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001993 return mOverlayHorizontalScrollbar;
1994 }
1995
1996 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00001997 * See {@link WebView#overlayVerticalScrollbar()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08001998 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00001999 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002000 public boolean overlayVerticalScrollbar() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002001 return mOverlayVerticalScrollbar;
2002 }
2003
2004 /*
2005 * Return the width of the view where the content of WebView should render
2006 * to.
2007 * Note: this can be called from WebCoreThread.
2008 */
2009 /* package */ int getViewWidth() {
Jonathan Dixon3c909522012-02-28 18:45:06 +00002010 if (!mWebView.isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002011 return getWidth();
2012 } else {
Jonathan Dixon3c909522012-02-28 18:45:06 +00002013 return Math.max(0, getWidth() - mWebView.getVerticalScrollbarWidth());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002014 }
2015 }
2016
Jonathan Dixon3c909522012-02-28 18:45:06 +00002017 // Interface to enable the browser to override title bar handling.
2018 public interface TitleBarDelegate {
2019 int getTitleHeight();
2020 public void onSetEmbeddedTitleBar(final View title);
2021 }
2022
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002023 /**
2024 * Returns the height (in pixels) of the embedded title bar (if any). Does not care about
2025 * scrolling
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002026 */
2027 protected int getTitleHeight() {
Jonathan Dixon3c909522012-02-28 18:45:06 +00002028 if (mWebView instanceof TitleBarDelegate) {
2029 return ((TitleBarDelegate) mWebView).getTitleHeight();
2030 }
Michael Kolb8116da52012-04-02 16:16:16 -07002031 return 0;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002032 }
2033
2034 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002035 * See {@link WebView#getVisibleTitleHeight()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002036 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002037 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002038 @Deprecated
2039 public int getVisibleTitleHeight() {
2040 // Actually, this method returns the height of the embedded title bar if one is set via the
2041 // hidden setEmbeddedTitleBar method.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002042 return getVisibleTitleHeightImpl();
2043 }
2044
2045 private int getVisibleTitleHeightImpl() {
2046 // need to restrict mScrollY due to over scroll
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00002047 return Math.max(getTitleHeight() - Math.max(0, getScrollY()),
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002048 getOverlappingActionModeHeight());
2049 }
2050
2051 private int mCachedOverlappingActionModeHeight = -1;
2052
2053 private int getOverlappingActionModeHeight() {
2054 if (mFindCallback == null) {
2055 return 0;
2056 }
2057 if (mCachedOverlappingActionModeHeight < 0) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00002058 mWebView.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002059 mCachedOverlappingActionModeHeight = Math.max(0,
2060 mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top);
2061 }
2062 return mCachedOverlappingActionModeHeight;
2063 }
2064
2065 /*
2066 * Return the height of the view where the content of WebView should render
2067 * to. Note that this excludes mTitleBar, if there is one.
2068 * Note: this can be called from WebCoreThread.
2069 */
2070 /* package */ int getViewHeight() {
2071 return getViewHeightWithTitle() - getVisibleTitleHeightImpl();
2072 }
2073
2074 int getViewHeightWithTitle() {
2075 int height = getHeight();
Jonathan Dixon3c909522012-02-28 18:45:06 +00002076 if (mWebView.isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
2077 height -= mWebViewPrivate.getHorizontalScrollbarHeight();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002078 }
2079 return height;
2080 }
2081
2082 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002083 * See {@link WebView#getCertificate()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002084 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002085 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002086 public SslCertificate getCertificate() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002087 return mCertificate;
2088 }
2089
2090 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002091 * See {@link WebView#setCertificate(SslCertificate)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002092 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002093 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002094 public void setCertificate(SslCertificate certificate) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002095 if (DebugFlags.WEB_VIEW) {
2096 Log.v(LOGTAG, "setCertificate=" + certificate);
2097 }
2098 // here, the certificate can be null (if the site is not secure)
2099 mCertificate = certificate;
2100 }
2101
2102 //-------------------------------------------------------------------------
2103 // Methods called by activity
2104 //-------------------------------------------------------------------------
2105
2106 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002107 * See {@link WebView#savePassword(String, String, String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002108 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002109 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002110 public void savePassword(String host, String username, String password) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002111 mDatabase.setUsernamePassword(host, username, password);
2112 }
2113
2114 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002115 * See {@link WebView#setHttpAuthUsernamePassword(String, String, String, String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002116 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002117 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002118 public void setHttpAuthUsernamePassword(String host, String realm,
2119 String username, String password) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002120 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
2121 }
2122
2123 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002124 * See {@link WebView#getHttpAuthUsernamePassword(String, String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002125 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002126 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002127 public String[] getHttpAuthUsernamePassword(String host, String realm) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002128 return mDatabase.getHttpAuthUsernamePassword(host, realm);
2129 }
2130
2131 /**
2132 * Remove Find or Select ActionModes, if active.
2133 */
2134 private void clearActionModes() {
2135 if (mSelectCallback != null) {
2136 mSelectCallback.finish();
2137 }
2138 if (mFindCallback != null) {
2139 mFindCallback.finish();
2140 }
2141 }
2142
2143 /**
2144 * Called to clear state when moving from one page to another, or changing
2145 * in some other way that makes elements associated with the current page
John Reckb2676f72012-03-02 14:13:06 -08002146 * (such as ActionModes) no longer relevant.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002147 */
2148 private void clearHelpers() {
John Reckb2676f72012-03-02 14:13:06 -08002149 hideSoftKeyboard();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002150 clearActionModes();
2151 dismissFullScreenMode();
John Reck095e8aa2012-09-10 16:07:04 -07002152 cancelDialogs();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002153 }
2154
John Reck095e8aa2012-09-10 16:07:04 -07002155 private void cancelDialogs() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002156 if (mListBoxDialog != null) {
2157 mListBoxDialog.cancel();
2158 mListBoxDialog = null;
2159 }
John Reck095e8aa2012-09-10 16:07:04 -07002160 if (mSavePasswordDialog != null) {
2161 mSavePasswordDialog.dismiss();
2162 mSavePasswordDialog = null;
2163 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002164 }
2165
2166 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002167 * See {@link WebView#destroy()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002168 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002169 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002170 public void destroy() {
John Reckd5e29372012-06-07 11:35:35 -07002171 if (mWebView.getViewRootImpl() != null) {
Kristian Monsen083c4dc2013-04-16 18:14:47 -07002172 Log.e(LOGTAG, Log.getStackTraceString(
2173 new Throwable("Error: WebView.destroy() called while still attached!")));
Chris Craik9e080122012-05-22 16:00:19 -07002174 }
John Reckd5e29372012-06-07 11:35:35 -07002175 ensureFunctorDetached();
2176 destroyJava();
2177 destroyNative();
2178 }
Chris Craik9e080122012-05-22 16:00:19 -07002179
John Reckd5e29372012-06-07 11:35:35 -07002180 private void ensureFunctorDetached() {
Chris Craik9e080122012-05-22 16:00:19 -07002181 if (mWebView.isHardwareAccelerated()) {
Chris Craikaa3b7d82012-06-06 16:45:53 -07002182 int drawGLFunction = nativeGetDrawGLFunction(mNativeClass);
John Reckd5e29372012-06-07 11:35:35 -07002183 ViewRootImpl viewRoot = mWebView.getViewRootImpl();
Chris Craik9e080122012-05-22 16:00:19 -07002184 if (drawGLFunction != 0 && viewRoot != null) {
Chris Craik9e080122012-05-22 16:00:19 -07002185 viewRoot.detachFunctor(drawGLFunction);
2186 }
2187 }
John Reckd5e29372012-06-07 11:35:35 -07002188 }
Chris Craik9e080122012-05-22 16:00:19 -07002189
John Reckd5e29372012-06-07 11:35:35 -07002190 private void destroyJava() {
Michael Kolbd78ad292012-03-09 09:38:30 -08002191 mCallbackProxy.blockMessages();
alanv03e636f2012-10-12 11:42:37 -07002192 if (mAccessibilityInjector != null) {
2193 mAccessibilityInjector.destroy();
2194 mAccessibilityInjector = null;
2195 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002196 if (mWebViewCore != null) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002197 // Tell WebViewCore to destroy itself
2198 synchronized (this) {
2199 WebViewCore webViewCore = mWebViewCore;
2200 mWebViewCore = null; // prevent using partial webViewCore
2201 webViewCore.destroy();
2202 }
2203 // Remove any pending messages that might not be serviced yet.
2204 mPrivateHandler.removeCallbacksAndMessages(null);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002205 }
John Reckd5e29372012-06-07 11:35:35 -07002206 }
2207
2208 private void destroyNative() {
2209 if (mNativeClass == 0) return;
2210 int nptr = mNativeClass;
2211 mNativeClass = 0;
2212 if (Thread.currentThread() == mPrivateHandler.getLooper().getThread()) {
2213 // We are on the main thread and can safely delete
2214 nativeDestroy(nptr);
2215 } else {
2216 mPrivateHandler.post(new DestroyNativeRunnable(nptr));
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002217 }
2218 }
2219
John Reckd5e29372012-06-07 11:35:35 -07002220 private static class DestroyNativeRunnable implements Runnable {
2221
2222 private int mNativePtr;
2223
2224 public DestroyNativeRunnable(int nativePtr) {
2225 mNativePtr = nativePtr;
2226 }
2227
2228 @Override
2229 public void run() {
2230 // nativeDestroy also does a stopGL()
2231 nativeDestroy(mNativePtr);
2232 }
2233
2234 }
2235
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002236 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002237 * See {@link WebView#enablePlatformNotifications()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002238 */
2239 @Deprecated
2240 public static void enablePlatformNotifications() {
Jonathan Dixon3c909522012-02-28 18:45:06 +00002241 synchronized (WebViewClassic.class) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002242 sNotificationsEnabled = true;
2243 Context context = JniUtil.getContext();
2244 if (context != null)
2245 setupProxyListener(context);
2246 }
2247 }
2248
2249 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002250 * See {@link WebView#disablePlatformNotifications()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002251 */
2252 @Deprecated
2253 public static void disablePlatformNotifications() {
Jonathan Dixon3c909522012-02-28 18:45:06 +00002254 synchronized (WebViewClassic.class) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002255 sNotificationsEnabled = false;
2256 Context context = JniUtil.getContext();
2257 if (context != null)
2258 disableProxyListener(context);
2259 }
2260 }
2261
2262 /**
2263 * Sets JavaScript engine flags.
2264 *
2265 * @param flags JS engine flags in a String
2266 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00002267 * This is an implementation detail.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002268 */
2269 public void setJsFlags(String flags) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002270 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
2271 }
2272
2273 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002274 * See {@link WebView#setNetworkAvailable(boolean)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002275 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002276 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002277 public void setNetworkAvailable(boolean networkUp) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002278 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
2279 networkUp ? 1 : 0, 0);
2280 }
2281
2282 /**
2283 * Inform WebView about the current network type.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002284 */
2285 public void setNetworkType(String type, String subtype) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002286 Map<String, String> map = new HashMap<String, String>();
2287 map.put("type", type);
2288 map.put("subtype", subtype);
2289 mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
2290 }
Jonathan Dixon19b80112012-03-02 18:18:00 +00002291
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002292 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002293 * See {@link WebView#saveState(Bundle)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002294 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002295 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002296 public WebBackForwardList saveState(Bundle outState) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002297 if (outState == null) {
2298 return null;
2299 }
2300 // We grab a copy of the back/forward list because a client of WebView
2301 // may have invalidated the history list by calling clearHistory.
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07002302 WebBackForwardListClassic list = copyBackForwardList();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002303 final int currentIndex = list.getCurrentIndex();
2304 final int size = list.getSize();
2305 // We should fail saving the state if the list is empty or the index is
2306 // not in a valid range.
2307 if (currentIndex < 0 || currentIndex >= size || size == 0) {
2308 return null;
2309 }
2310 outState.putInt("index", currentIndex);
2311 // FIXME: This should just be a byte[][] instead of ArrayList but
2312 // Parcel.java does not have the code to handle multi-dimensional
2313 // arrays.
2314 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
2315 for (int i = 0; i < size; i++) {
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07002316 WebHistoryItemClassic item = list.getItemAtIndex(i);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002317 if (null == item) {
2318 // FIXME: this shouldn't happen
2319 // need to determine how item got set to null
2320 Log.w(LOGTAG, "saveState: Unexpected null history item.");
2321 return null;
2322 }
2323 byte[] data = item.getFlattenedData();
2324 if (data == null) {
2325 // It would be very odd to not have any data for a given history
2326 // item. And we will fail to rebuild the history list without
2327 // flattened data.
2328 return null;
2329 }
2330 history.add(data);
2331 }
2332 outState.putSerializable("history", history);
2333 if (mCertificate != null) {
2334 outState.putBundle("certificate",
2335 SslCertificate.saveState(mCertificate));
2336 }
2337 outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
2338 mZoomManager.saveZoomState(outState);
2339 return list;
2340 }
2341
2342 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002343 * See {@link WebView#savePicture(Bundle, File)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002344 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002345 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002346 @Deprecated
2347 public boolean savePicture(Bundle b, final File dest) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002348 if (dest == null || b == null) {
2349 return false;
2350 }
2351 final Picture p = capturePicture();
2352 // Use a temporary file while writing to ensure the destination file
2353 // contains valid data.
2354 final File temp = new File(dest.getPath() + ".writing");
2355 new Thread(new Runnable() {
2356 @Override
2357 public void run() {
2358 FileOutputStream out = null;
2359 try {
2360 out = new FileOutputStream(temp);
2361 p.writeToStream(out);
2362 // Writing the picture succeeded, rename the temporary file
2363 // to the destination.
2364 temp.renameTo(dest);
2365 } catch (Exception e) {
2366 // too late to do anything about it.
2367 } finally {
2368 if (out != null) {
2369 try {
2370 out.close();
2371 } catch (Exception e) {
2372 // Can't do anything about that
2373 }
2374 }
2375 temp.delete();
2376 }
2377 }
2378 }).start();
2379 // now update the bundle
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00002380 b.putInt("scrollX", getScrollX());
2381 b.putInt("scrollY", getScrollY());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002382 mZoomManager.saveZoomState(b);
2383 return true;
2384 }
2385
2386 private void restoreHistoryPictureFields(Picture p, Bundle b) {
2387 int sx = b.getInt("scrollX", 0);
2388 int sy = b.getInt("scrollY", 0);
2389
2390 mDrawHistory = true;
2391 mHistoryPicture = p;
2392
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00002393 setScrollXRaw(sx);
2394 setScrollYRaw(sy);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002395 mZoomManager.restoreZoomState(b);
2396 final float scale = mZoomManager.getScale();
2397 mHistoryWidth = Math.round(p.getWidth() * scale);
2398 mHistoryHeight = Math.round(p.getHeight() * scale);
2399
2400 invalidate();
2401 }
2402
2403 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002404 * See {@link WebView#restorePicture(Bundle, File)};
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002405 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002406 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002407 @Deprecated
2408 public boolean restorePicture(Bundle b, File src) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002409 if (src == null || b == null) {
2410 return false;
2411 }
2412 if (!src.exists()) {
2413 return false;
2414 }
2415 try {
2416 final FileInputStream in = new FileInputStream(src);
2417 final Bundle copy = new Bundle(b);
2418 new Thread(new Runnable() {
2419 @Override
2420 public void run() {
2421 try {
2422 final Picture p = Picture.createFromStream(in);
2423 if (p != null) {
2424 // Post a runnable on the main thread to update the
2425 // history picture fields.
2426 mPrivateHandler.post(new Runnable() {
2427 @Override
2428 public void run() {
2429 restoreHistoryPictureFields(p, copy);
2430 }
2431 });
2432 }
2433 } finally {
2434 try {
2435 in.close();
2436 } catch (Exception e) {
2437 // Nothing we can do now.
2438 }
2439 }
2440 }
2441 }).start();
2442 } catch (FileNotFoundException e){
2443 e.printStackTrace();
2444 }
2445 return true;
2446 }
2447
2448 /**
2449 * Saves the view data to the output stream. The output is highly
2450 * version specific, and may not be able to be loaded by newer versions
2451 * of WebView.
2452 * @param stream The {@link OutputStream} to save to
John Reckee3b5622012-04-19 15:27:58 -07002453 * @param callback The {@link ValueCallback} to call with the result
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002454 */
John Reckee3b5622012-04-19 15:27:58 -07002455 public void saveViewState(OutputStream stream, ValueCallback<Boolean> callback) {
2456 if (mWebViewCore == null) {
2457 callback.onReceiveValue(false);
2458 return;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002459 }
John Reckee3b5622012-04-19 15:27:58 -07002460 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SAVE_VIEW_STATE,
2461 new WebViewCore.SaveViewStateRequest(stream, callback));
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002462 }
2463
2464 /**
2465 * Loads the view data from the input stream. See
Romain Guyba6be8a2012-04-23 18:22:09 -07002466 * {@link #saveViewState(java.io.OutputStream, ValueCallback)} for more information.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002467 * @param stream The {@link InputStream} to load from
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002468 */
John Reckee3b5622012-04-19 15:27:58 -07002469 public void loadViewState(InputStream stream) {
2470 mBlockWebkitViewMessages = true;
2471 new AsyncTask<InputStream, Void, DrawData>() {
2472
2473 @Override
2474 protected DrawData doInBackground(InputStream... params) {
2475 try {
2476 return ViewStateSerializer.deserializeViewState(params[0]);
2477 } catch (IOException e) {
2478 return null;
2479 }
2480 }
2481
2482 @Override
2483 protected void onPostExecute(DrawData draw) {
2484 if (draw == null) {
2485 Log.e(LOGTAG, "Failed to load view state!");
2486 return;
2487 }
2488 int viewWidth = getViewWidth();
2489 int viewHeight = getViewHeightWithTitle() - getTitleHeight();
2490 draw.mViewSize = new Point(viewWidth, viewHeight);
2491 draw.mViewState.mDefaultScale = getDefaultZoomScale();
2492 mLoadedPicture = draw;
2493 setNewPicture(mLoadedPicture, true);
2494 mLoadedPicture.mViewState = null;
2495 }
2496
2497 }.execute(stream);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002498 }
2499
2500 /**
2501 * Clears the view state set with {@link #loadViewState(InputStream)}.
2502 * This WebView will then switch to showing the content from webkit
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002503 */
2504 public void clearViewState() {
2505 mBlockWebkitViewMessages = false;
2506 mLoadedPicture = null;
2507 invalidate();
2508 }
2509
2510 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002511 * See {@link WebView#restoreState(Bundle)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002512 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002513 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002514 public WebBackForwardList restoreState(Bundle inState) {
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07002515 WebBackForwardListClassic returnList = null;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002516 if (inState == null) {
2517 return returnList;
2518 }
2519 if (inState.containsKey("index") && inState.containsKey("history")) {
2520 mCertificate = SslCertificate.restoreState(
2521 inState.getBundle("certificate"));
2522
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07002523 final WebBackForwardListClassic list = mCallbackProxy.getBackForwardList();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002524 final int index = inState.getInt("index");
2525 // We can't use a clone of the list because we need to modify the
2526 // shared copy, so synchronize instead to prevent concurrent
2527 // modifications.
2528 synchronized (list) {
2529 final List<byte[]> history =
2530 (List<byte[]>) inState.getSerializable("history");
2531 final int size = history.size();
2532 // Check the index bounds so we don't crash in native code while
2533 // restoring the history index.
2534 if (index < 0 || index >= size) {
2535 return null;
2536 }
2537 for (int i = 0; i < size; i++) {
2538 byte[] data = history.remove(0);
2539 if (data == null) {
2540 // If we somehow have null data, we cannot reconstruct
2541 // the item and thus our history list cannot be rebuilt.
2542 return null;
2543 }
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07002544 WebHistoryItem item = new WebHistoryItemClassic(data);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002545 list.addHistoryItem(item);
2546 }
2547 // Grab the most recent copy to return to the caller.
2548 returnList = copyBackForwardList();
2549 // Update the copy to have the correct index.
2550 returnList.setCurrentIndex(index);
2551 }
2552 // Restore private browsing setting.
2553 if (inState.getBoolean("privateBrowsingEnabled")) {
2554 getSettings().setPrivateBrowsingEnabled(true);
2555 }
2556 mZoomManager.restoreZoomState(inState);
2557 // Remove all pending messages because we are restoring previous
2558 // state.
2559 mWebViewCore.removeMessages();
alanv992ceef2012-11-01 14:25:01 -07002560 if (isAccessibilityInjectionEnabled()) {
2561 getAccessibilityInjector().addAccessibilityApisIfNecessary();
2562 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002563 // Send a restore state message.
2564 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
2565 }
2566 return returnList;
2567 }
2568
2569 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002570 * See {@link WebView#loadUrl(String, Map)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002571 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002572 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002573 public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002574 loadUrlImpl(url, additionalHttpHeaders);
2575 }
2576
2577 private void loadUrlImpl(String url, Map<String, String> extraHeaders) {
2578 switchOutDrawHistory();
2579 WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
2580 arg.mUrl = url;
2581 arg.mExtraHeaders = extraHeaders;
2582 mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
2583 clearHelpers();
2584 }
2585
2586 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002587 * See {@link WebView#loadUrl(String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002588 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002589 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002590 public void loadUrl(String url) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002591 loadUrlImpl(url);
2592 }
2593
2594 private void loadUrlImpl(String url) {
2595 if (url == null) {
2596 return;
2597 }
2598 loadUrlImpl(url, null);
2599 }
2600
2601 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002602 * See {@link WebView#postUrl(String, byte[])}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002603 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002604 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002605 public void postUrl(String url, byte[] postData) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002606 if (URLUtil.isNetworkUrl(url)) {
2607 switchOutDrawHistory();
2608 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
2609 arg.mUrl = url;
2610 arg.mPostData = postData;
2611 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
2612 clearHelpers();
2613 } else {
2614 loadUrlImpl(url);
2615 }
2616 }
2617
2618 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002619 * See {@link WebView#loadData(String, String, String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002620 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002621 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002622 public void loadData(String data, String mimeType, String encoding) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002623 loadDataImpl(data, mimeType, encoding);
2624 }
2625
2626 private void loadDataImpl(String data, String mimeType, String encoding) {
2627 StringBuilder dataUrl = new StringBuilder("data:");
2628 dataUrl.append(mimeType);
2629 if ("base64".equals(encoding)) {
2630 dataUrl.append(";base64");
2631 }
2632 dataUrl.append(",");
2633 dataUrl.append(data);
2634 loadUrlImpl(dataUrl.toString());
2635 }
2636
2637 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002638 * See {@link WebView#loadDataWithBaseURL(String, String, String, String, String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002639 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002640 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002641 public void loadDataWithBaseURL(String baseUrl, String data,
2642 String mimeType, String encoding, String historyUrl) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002643
2644 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
2645 loadDataImpl(data, mimeType, encoding);
2646 return;
2647 }
2648 switchOutDrawHistory();
2649 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
2650 arg.mBaseUrl = baseUrl;
2651 arg.mData = data;
2652 arg.mMimeType = mimeType;
2653 arg.mEncoding = encoding;
2654 arg.mHistoryUrl = historyUrl;
2655 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
2656 clearHelpers();
2657 }
2658
Ben Murdocha5cdd512013-07-17 16:25:07 +01002659 @Override
2660 public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
2661 // K-only API not implemented in WebViewClassic.
2662 throw new IllegalStateException("This API not supported in Classic WebView.");
2663 }
2664
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002665 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002666 * See {@link WebView#saveWebArchive(String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002667 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002668 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002669 public void saveWebArchive(String filename) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002670 saveWebArchiveImpl(filename, false, null);
2671 }
2672
2673 /* package */ static class SaveWebArchiveMessage {
2674 SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) {
2675 mBasename = basename;
2676 mAutoname = autoname;
2677 mCallback = callback;
2678 }
2679
2680 /* package */ final String mBasename;
2681 /* package */ final boolean mAutoname;
2682 /* package */ final ValueCallback<String> mCallback;
2683 /* package */ String mResultFile;
2684 }
2685
2686 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002687 * See {@link WebView#saveWebArchive(String, boolean, ValueCallback)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002688 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002689 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002690 public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002691 saveWebArchiveImpl(basename, autoname, callback);
2692 }
2693
2694 private void saveWebArchiveImpl(String basename, boolean autoname,
2695 ValueCallback<String> callback) {
2696 mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
2697 new SaveWebArchiveMessage(basename, autoname, callback));
2698 }
2699
2700 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002701 * See {@link WebView#stopLoading()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002702 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002703 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002704 public void stopLoading() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002705 // TODO: should we clear all the messages in the queue before sending
2706 // STOP_LOADING?
2707 switchOutDrawHistory();
2708 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
2709 }
2710
2711 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002712 * See {@link WebView#reload()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002713 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002714 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002715 public void reload() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002716 clearHelpers();
2717 switchOutDrawHistory();
2718 mWebViewCore.sendMessage(EventHub.RELOAD);
2719 }
2720
2721 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002722 * See {@link WebView#canGoBack()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002723 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002724 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002725 public boolean canGoBack() {
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07002726 WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002727 synchronized (l) {
2728 if (l.getClearPending()) {
2729 return false;
2730 } else {
2731 return l.getCurrentIndex() > 0;
2732 }
2733 }
2734 }
2735
2736 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002737 * See {@link WebView#goBack()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002738 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002739 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002740 public void goBack() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002741 goBackOrForwardImpl(-1);
2742 }
2743
2744 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002745 * See {@link WebView#canGoForward()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002746 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002747 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002748 public boolean canGoForward() {
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07002749 WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002750 synchronized (l) {
2751 if (l.getClearPending()) {
2752 return false;
2753 } else {
2754 return l.getCurrentIndex() < l.getSize() - 1;
2755 }
2756 }
2757 }
2758
2759 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002760 * See {@link WebView#goForward()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002761 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002762 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002763 public void goForward() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002764 goBackOrForwardImpl(1);
2765 }
2766
2767 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002768 * See {@link WebView#canGoBackOrForward(int)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002769 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002770 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002771 public boolean canGoBackOrForward(int steps) {
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07002772 WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002773 synchronized (l) {
2774 if (l.getClearPending()) {
2775 return false;
2776 } else {
2777 int newIndex = l.getCurrentIndex() + steps;
2778 return newIndex >= 0 && newIndex < l.getSize();
2779 }
2780 }
2781 }
2782
2783 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002784 * See {@link WebView#goBackOrForward(int)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002785 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002786 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002787 public void goBackOrForward(int steps) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002788 goBackOrForwardImpl(steps);
2789 }
2790
2791 private void goBackOrForwardImpl(int steps) {
2792 goBackOrForward(steps, false);
2793 }
2794
2795 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
2796 if (steps != 0) {
2797 clearHelpers();
2798 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
2799 ignoreSnapshot ? 1 : 0);
2800 }
2801 }
2802
2803 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002804 * See {@link WebView#isPrivateBrowsingEnabled()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002805 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002806 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002807 public boolean isPrivateBrowsingEnabled() {
Michael Kolb13d7c032012-04-26 10:43:08 -07002808 WebSettingsClassic settings = getSettings();
2809 return (settings != null) ? settings.isPrivateBrowsingEnabled() : false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002810 }
2811
2812 private void startPrivateBrowsing() {
2813 getSettings().setPrivateBrowsingEnabled(true);
2814 }
2815
2816 private boolean extendScroll(int y) {
2817 int finalY = mScroller.getFinalY();
2818 int newY = pinLocY(finalY + y);
2819 if (newY == finalY) return false;
2820 mScroller.setFinalY(newY);
2821 mScroller.extendDuration(computeDuration(0, y));
2822 return true;
2823 }
2824
2825 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002826 * See {@link WebView#pageUp(boolean)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002827 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002828 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002829 public boolean pageUp(boolean top) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002830 if (mNativeClass == 0) {
2831 return false;
2832 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002833 if (top) {
2834 // go to the top of the document
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00002835 return pinScrollTo(getScrollX(), 0, true, 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002836 }
2837 // Page up
2838 int h = getHeight();
2839 int y;
2840 if (h > 2 * PAGE_SCROLL_OVERLAP) {
2841 y = -h + PAGE_SCROLL_OVERLAP;
2842 } else {
2843 y = -h / 2;
2844 }
2845 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
2846 : extendScroll(y);
2847 }
2848
2849 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002850 * See {@link WebView#pageDown(boolean)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002851 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002852 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002853 public boolean pageDown(boolean bottom) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002854 if (mNativeClass == 0) {
2855 return false;
2856 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002857 if (bottom) {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00002858 return pinScrollTo(getScrollX(), computeRealVerticalScrollRange(), true, 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002859 }
2860 // Page down.
2861 int h = getHeight();
2862 int y;
2863 if (h > 2 * PAGE_SCROLL_OVERLAP) {
2864 y = h - PAGE_SCROLL_OVERLAP;
2865 } else {
2866 y = h / 2;
2867 }
2868 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
2869 : extendScroll(y);
2870 }
2871
2872 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002873 * See {@link WebView#clearView()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002874 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002875 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002876 public void clearView() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002877 mContentWidth = 0;
2878 mContentHeight = 0;
Chris Craikc2c95432012-04-25 15:13:52 -07002879 setBaseLayer(0, false, false);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002880 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
2881 }
2882
2883 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002884 * See {@link WebView#capturePicture()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002885 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002886 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002887 public Picture capturePicture() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002888 if (mNativeClass == 0) return null;
2889 Picture result = new Picture();
2890 nativeCopyBaseContentToPicture(result);
2891 return result;
2892 }
2893
2894 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002895 * See {@link WebView#getScale()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002896 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002897 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002898 public float getScale() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002899 return mZoomManager.getScale();
2900 }
2901
2902 /**
2903 * Compute the reading level scale of the WebView
2904 * @param scale The current scale.
2905 * @return The reading level scale.
2906 */
2907 /*package*/ float computeReadingLevelScale(float scale) {
2908 return mZoomManager.computeReadingLevelScale(scale);
2909 }
2910
2911 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002912 * See {@link WebView#setInitialScale(int)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002913 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002914 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002915 public void setInitialScale(int scaleInPercent) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002916 mZoomManager.setInitialScaleInPercent(scaleInPercent);
2917 }
2918
2919 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002920 * See {@link WebView#invokeZoomPicker()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002921 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002922 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002923 public void invokeZoomPicker() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002924 if (!getSettings().supportZoom()) {
2925 Log.w(LOGTAG, "This WebView doesn't support zoom.");
2926 return;
2927 }
2928 clearHelpers();
2929 mZoomManager.invokeZoomPicker();
2930 }
2931
2932 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002933 * See {@link WebView#getHitTestResult()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002934 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002935 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002936 public HitTestResult getHitTestResult() {
John Reckb2676f72012-03-02 14:13:06 -08002937 return mInitialHitTestResult;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002938 }
2939
John Reckb2676f72012-03-02 14:13:06 -08002940 // No left edge for double-tap zoom alignment
2941 static final int NO_LEFTEDGE = -1;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002942
2943 int getBlockLeftEdge(int x, int y, float readingScale) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002944 float invReadingScale = 1.0f / readingScale;
2945 int readingWidth = (int) (getViewWidth() * invReadingScale);
2946 int left = NO_LEFTEDGE;
2947 if (mFocusedNode != null) {
2948 final int length = mFocusedNode.mEnclosingParentRects.length;
2949 for (int i = 0; i < length; i++) {
2950 Rect rect = mFocusedNode.mEnclosingParentRects[i];
2951 if (rect.width() < mFocusedNode.mHitTestSlop) {
2952 // ignore bounding boxes that are too small
2953 continue;
Mangesh Ghiware518e83b2012-04-10 17:42:49 -07002954 } else if (rect.width() > readingWidth) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002955 // stop when bounding box doesn't fit the screen width
2956 // at reading scale
2957 break;
2958 }
2959
2960 left = rect.left;
2961 }
2962 }
2963
2964 return left;
2965 }
2966
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002967 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002968 * See {@link WebView#requestFocusNodeHref(Message)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002969 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002970 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002971 public void requestFocusNodeHref(Message hrefMsg) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002972 if (hrefMsg == null) {
2973 return;
2974 }
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00002975 int contentX = viewToContentX(mLastTouchX + getScrollX());
2976 int contentY = viewToContentY(mLastTouchY + getScrollY());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002977 if (mFocusedNode != null && mFocusedNode.mHitTestX == contentX
2978 && mFocusedNode.mHitTestY == contentY) {
2979 hrefMsg.getData().putString(FocusNodeHref.URL, mFocusedNode.mLinkUrl);
2980 hrefMsg.getData().putString(FocusNodeHref.TITLE, mFocusedNode.mAnchorText);
2981 hrefMsg.getData().putString(FocusNodeHref.SRC, mFocusedNode.mImageUrl);
2982 hrefMsg.sendToTarget();
2983 return;
2984 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002985 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
2986 contentX, contentY, hrefMsg);
2987 }
2988
2989 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00002990 * See {@link WebView#requestImageRef(Message)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002991 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00002992 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002993 public void requestImageRef(Message msg) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002994 if (0 == mNativeClass) return; // client isn't initialized
John Reckb2676f72012-03-02 14:13:06 -08002995 String url = mFocusedNode != null ? mFocusedNode.mImageUrl : null;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002996 Bundle data = msg.getData();
John Reckb2676f72012-03-02 14:13:06 -08002997 data.putString("url", url);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08002998 msg.setData(data);
2999 msg.sendToTarget();
3000 }
3001
3002 static int pinLoc(int x, int viewMax, int docMax) {
3003// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
3004 if (docMax < viewMax) { // the doc has room on the sides for "blank"
3005 // pin the short document to the top/left of the screen
3006 x = 0;
3007// Log.d(LOGTAG, "--- center " + x);
3008 } else if (x < 0) {
3009 x = 0;
3010// Log.d(LOGTAG, "--- zero");
3011 } else if (x + viewMax > docMax) {
3012 x = docMax - viewMax;
3013// Log.d(LOGTAG, "--- pin " + x);
3014 }
3015 return x;
3016 }
3017
3018 // Expects x in view coordinates
3019 int pinLocX(int x) {
3020 if (mInOverScrollMode) return x;
3021 return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
3022 }
3023
3024 // Expects y in view coordinates
3025 int pinLocY(int y) {
3026 if (mInOverScrollMode) return y;
3027 return pinLoc(y, getViewHeightWithTitle(),
3028 computeRealVerticalScrollRange() + getTitleHeight());
3029 }
3030
3031 /**
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003032 * Given a distance in view space, convert it to content space. Note: this
3033 * does not reflect translation, just scaling, so this should not be called
3034 * with coordinates, but should be called for dimensions like width or
3035 * height.
3036 */
3037 private int viewToContentDimension(int d) {
3038 return Math.round(d * mZoomManager.getInvScale());
3039 }
3040
3041 /**
3042 * Given an x coordinate in view space, convert it to content space. Also
John Reckb2676f72012-03-02 14:13:06 -08003043 * may be used for absolute heights.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003044 */
3045 /*package*/ int viewToContentX(int x) {
3046 return viewToContentDimension(x);
3047 }
3048
3049 /**
3050 * Given a y coordinate in view space, convert it to content space.
3051 * Takes into account the height of the title bar if there is one
3052 * embedded into the WebView.
3053 */
3054 /*package*/ int viewToContentY(int y) {
3055 return viewToContentDimension(y - getTitleHeight());
3056 }
3057
3058 /**
3059 * Given a x coordinate in view space, convert it to content space.
3060 * Returns the result as a float.
3061 */
3062 private float viewToContentXf(int x) {
3063 return x * mZoomManager.getInvScale();
3064 }
3065
3066 /**
3067 * Given a y coordinate in view space, convert it to content space.
3068 * Takes into account the height of the title bar if there is one
3069 * embedded into the WebView. Returns the result as a float.
3070 */
3071 private float viewToContentYf(int y) {
3072 return (y - getTitleHeight()) * mZoomManager.getInvScale();
3073 }
3074
3075 /**
3076 * Given a distance in content space, convert it to view space. Note: this
3077 * does not reflect translation, just scaling, so this should not be called
3078 * with coordinates, but should be called for dimensions like width or
3079 * height.
3080 */
3081 /*package*/ int contentToViewDimension(int d) {
3082 return Math.round(d * mZoomManager.getScale());
3083 }
3084
3085 /**
3086 * Given an x coordinate in content space, convert it to view
3087 * space.
3088 */
3089 /*package*/ int contentToViewX(int x) {
3090 return contentToViewDimension(x);
3091 }
3092
3093 /**
3094 * Given a y coordinate in content space, convert it to view
3095 * space. Takes into account the height of the title bar.
3096 */
3097 /*package*/ int contentToViewY(int y) {
3098 return contentToViewDimension(y) + getTitleHeight();
3099 }
3100
3101 private Rect contentToViewRect(Rect x) {
3102 return new Rect(contentToViewX(x.left), contentToViewY(x.top),
3103 contentToViewX(x.right), contentToViewY(x.bottom));
3104 }
3105
3106 /* To invalidate a rectangle in content coordinates, we need to transform
3107 the rect into view coordinates, so we can then call invalidate(...).
3108
3109 Normally, we would just call contentToView[XY](...), which eventually
3110 calls Math.round(coordinate * mActualScale). However, for invalidates,
3111 we need to account for the slop that occurs with antialiasing. To
3112 address that, we are a little more liberal in the size of the rect that
3113 we invalidate.
3114
3115 This liberal calculation calls floor() for the top/left, and ceil() for
3116 the bottom/right coordinates. This catches the possible extra pixels of
3117 antialiasing that we might have missed with just round().
3118 */
3119
3120 // Called by JNI to invalidate the View, given rectangle coordinates in
3121 // content space
3122 private void viewInvalidate(int l, int t, int r, int b) {
3123 final float scale = mZoomManager.getScale();
3124 final int dy = getTitleHeight();
Jonathan Dixon3c909522012-02-28 18:45:06 +00003125 mWebView.invalidate((int)Math.floor(l * scale),
3126 (int)Math.floor(t * scale) + dy,
3127 (int)Math.ceil(r * scale),
3128 (int)Math.ceil(b * scale) + dy);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003129 }
3130
3131 // Called by JNI to invalidate the View after a delay, given rectangle
3132 // coordinates in content space
3133 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
3134 final float scale = mZoomManager.getScale();
3135 final int dy = getTitleHeight();
Jonathan Dixon3c909522012-02-28 18:45:06 +00003136 mWebView.postInvalidateDelayed(delay,
3137 (int)Math.floor(l * scale),
3138 (int)Math.floor(t * scale) + dy,
3139 (int)Math.ceil(r * scale),
3140 (int)Math.ceil(b * scale) + dy);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003141 }
3142
3143 private void invalidateContentRect(Rect r) {
3144 viewInvalidate(r.left, r.top, r.right, r.bottom);
3145 }
3146
3147 // stop the scroll animation, and don't let a subsequent fling add
3148 // to the existing velocity
3149 private void abortAnimation() {
3150 mScroller.abortAnimation();
3151 mLastVelocity = 0;
3152 }
3153
3154 /* call from webcoreview.draw(), so we're still executing in the UI thread
3155 */
3156 private void recordNewContentSize(int w, int h, boolean updateLayout) {
3157
3158 // premature data from webkit, ignore
3159 if ((w | h) == 0) {
Chris Craik02c2f452012-05-15 10:34:33 -07003160 invalidate();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003161 return;
3162 }
3163
3164 // don't abort a scroll animation if we didn't change anything
3165 if (mContentWidth != w || mContentHeight != h) {
3166 // record new dimensions
3167 mContentWidth = w;
3168 mContentHeight = h;
3169 // If history Picture is drawn, don't update scroll. They will be
3170 // updated when we get out of that mode.
3171 if (!mDrawHistory) {
3172 // repin our scroll, taking into account the new content size
Chris Craik02c2f452012-05-15 10:34:33 -07003173 updateScrollCoordinates(pinLocX(getScrollX()), pinLocY(getScrollY()));
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003174 if (!mScroller.isFinished()) {
3175 // We are in the middle of a scroll. Repin the final scroll
3176 // position.
3177 mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
3178 mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
3179 }
3180 }
Chris Craik02c2f452012-05-15 10:34:33 -07003181 invalidate();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003182 }
3183 contentSizeChanged(updateLayout);
3184 }
3185
3186 // Used to avoid sending many visible rect messages.
3187 private Rect mLastVisibleRectSent = new Rect();
3188 private Rect mLastGlobalRect = new Rect();
3189 private Rect mVisibleRect = new Rect();
3190 private Rect mGlobalVisibleRect = new Rect();
3191 private Point mScrollOffset = new Point();
3192
3193 Rect sendOurVisibleRect() {
3194 if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent;
3195 calcOurContentVisibleRect(mVisibleRect);
3196 // Rect.equals() checks for null input.
3197 if (!mVisibleRect.equals(mLastVisibleRectSent)) {
3198 if (!mBlockWebkitViewMessages) {
3199 mScrollOffset.set(mVisibleRect.left, mVisibleRect.top);
3200 mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET);
3201 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
John Reckb2676f72012-03-02 14:13:06 -08003202 mSendScrollEvent ? 1 : 0, mScrollOffset);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003203 }
3204 mLastVisibleRectSent.set(mVisibleRect);
3205 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
3206 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00003207 if (mWebView.getGlobalVisibleRect(mGlobalVisibleRect)
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003208 && !mGlobalVisibleRect.equals(mLastGlobalRect)) {
3209 if (DebugFlags.WEB_VIEW) {
3210 Log.v(LOGTAG, "sendOurVisibleRect=(" + mGlobalVisibleRect.left + ","
3211 + mGlobalVisibleRect.top + ",r=" + mGlobalVisibleRect.right + ",b="
3212 + mGlobalVisibleRect.bottom);
3213 }
3214 // TODO: the global offset is only used by windowRect()
3215 // in ChromeClientAndroid ; other clients such as touch
3216 // and mouse events could return view + screen relative points.
3217 if (!mBlockWebkitViewMessages) {
3218 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, mGlobalVisibleRect);
3219 }
3220 mLastGlobalRect.set(mGlobalVisibleRect);
3221 }
3222 return mVisibleRect;
3223 }
3224
3225 private Point mGlobalVisibleOffset = new Point();
3226 // Sets r to be the visible rectangle of our webview in view coordinates
3227 private void calcOurVisibleRect(Rect r) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00003228 mWebView.getGlobalVisibleRect(r, mGlobalVisibleOffset);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003229 r.offset(-mGlobalVisibleOffset.x, -mGlobalVisibleOffset.y);
3230 }
3231
3232 // Sets r to be our visible rectangle in content coordinates
3233 private void calcOurContentVisibleRect(Rect r) {
3234 calcOurVisibleRect(r);
3235 r.left = viewToContentX(r.left);
3236 // viewToContentY will remove the total height of the title bar. Add
3237 // the visible height back in to account for the fact that if the title
3238 // bar is partially visible, the part of the visible rect which is
3239 // displaying our content is displaced by that amount.
3240 r.top = viewToContentY(r.top + getVisibleTitleHeightImpl());
3241 r.right = viewToContentX(r.right);
3242 r.bottom = viewToContentY(r.bottom);
3243 }
3244
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07003245 private final Rect mTempContentVisibleRect = new Rect();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003246 // Sets r to be our visible rectangle in content coordinates. We use this
3247 // method on the native side to compute the position of the fixed layers.
3248 // Uses floating coordinates (necessary to correctly place elements when
3249 // the scale factor is not 1)
3250 private void calcOurContentVisibleRectF(RectF r) {
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07003251 calcOurVisibleRect(mTempContentVisibleRect);
3252 viewToContentVisibleRect(r, mTempContentVisibleRect);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003253 }
3254
3255 static class ViewSizeData {
3256 int mWidth;
3257 int mHeight;
3258 float mHeightWidthRatio;
3259 int mActualViewHeight;
3260 int mTextWrapWidth;
3261 int mAnchorX;
3262 int mAnchorY;
3263 float mScale;
3264 boolean mIgnoreHeight;
3265 }
3266
3267 /**
3268 * Compute unzoomed width and height, and if they differ from the last
3269 * values we sent, send them to webkit (to be used as new viewport)
3270 *
3271 * @param force ensures that the message is sent to webkit even if the width
3272 * or height has not changed since the last message
3273 *
3274 * @return true if new values were sent
3275 */
3276 boolean sendViewSizeZoom(boolean force) {
3277 if (mBlockWebkitViewMessages) return false;
3278 if (mZoomManager.isPreventingWebkitUpdates()) return false;
3279
3280 int viewWidth = getViewWidth();
3281 int newWidth = Math.round(viewWidth * mZoomManager.getInvScale());
3282 // This height could be fixed and be different from actual visible height.
3283 int viewHeight = getViewHeightWithTitle() - getTitleHeight();
3284 int newHeight = Math.round(viewHeight * mZoomManager.getInvScale());
3285 // Make the ratio more accurate than (newHeight / newWidth), since the
3286 // latter both are calculated and rounded.
3287 float heightWidthRatio = (float) viewHeight / viewWidth;
3288 /*
3289 * Because the native side may have already done a layout before the
3290 * View system was able to measure us, we have to send a height of 0 to
3291 * remove excess whitespace when we grow our width. This will trigger a
3292 * layout and a change in content size. This content size change will
3293 * mean that contentSizeChanged will either call this method directly or
3294 * indirectly from onSizeChanged.
3295 */
3296 if (newWidth > mLastWidthSent && mWrapContent) {
3297 newHeight = 0;
3298 heightWidthRatio = 0;
3299 }
3300 // Actual visible content height.
3301 int actualViewHeight = Math.round(getViewHeight() * mZoomManager.getInvScale());
3302 // Avoid sending another message if the dimensions have not changed.
3303 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force ||
3304 actualViewHeight != mLastActualHeightSent) {
3305 ViewSizeData data = new ViewSizeData();
3306 data.mWidth = newWidth;
3307 data.mHeight = newHeight;
3308 data.mHeightWidthRatio = heightWidthRatio;
3309 data.mActualViewHeight = actualViewHeight;
3310 data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale());
3311 data.mScale = mZoomManager.getScale();
3312 data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress()
3313 && !mHeightCanMeasure;
3314 data.mAnchorX = mZoomManager.getDocumentAnchorX();
3315 data.mAnchorY = mZoomManager.getDocumentAnchorY();
3316 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
3317 mLastWidthSent = newWidth;
3318 mLastHeightSent = newHeight;
3319 mLastActualHeightSent = actualViewHeight;
3320 mZoomManager.clearDocumentAnchor();
3321 return true;
3322 }
3323 return false;
3324 }
3325
3326 /**
3327 * Update the double-tap zoom.
3328 */
3329 /* package */ void updateDoubleTapZoom(int doubleTapZoom) {
3330 mZoomManager.updateDoubleTapZoom(doubleTapZoom);
3331 }
3332
3333 private int computeRealHorizontalScrollRange() {
3334 if (mDrawHistory) {
3335 return mHistoryWidth;
3336 } else {
3337 // to avoid rounding error caused unnecessary scrollbar, use floor
3338 return (int) Math.floor(mContentWidth * mZoomManager.getScale());
3339 }
3340 }
3341
3342 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00003343 public int computeHorizontalScrollRange() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003344 int range = computeRealHorizontalScrollRange();
3345
3346 // Adjust reported range if overscrolled to compress the scroll bars
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003347 final int scrollX = getScrollX();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003348 final int overscrollRight = computeMaxScrollX();
3349 if (scrollX < 0) {
3350 range -= scrollX;
3351 } else if (scrollX > overscrollRight) {
3352 range += scrollX - overscrollRight;
3353 }
3354
3355 return range;
3356 }
3357
3358 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00003359 public int computeHorizontalScrollOffset() {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003360 return Math.max(getScrollX(), 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003361 }
3362
3363 private int computeRealVerticalScrollRange() {
3364 if (mDrawHistory) {
3365 return mHistoryHeight;
3366 } else {
3367 // to avoid rounding error caused unnecessary scrollbar, use floor
3368 return (int) Math.floor(mContentHeight * mZoomManager.getScale());
3369 }
3370 }
3371
3372 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00003373 public int computeVerticalScrollRange() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003374 int range = computeRealVerticalScrollRange();
3375
3376 // Adjust reported range if overscrolled to compress the scroll bars
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003377 final int scrollY = getScrollY();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003378 final int overscrollBottom = computeMaxScrollY();
3379 if (scrollY < 0) {
3380 range -= scrollY;
3381 } else if (scrollY > overscrollBottom) {
3382 range += scrollY - overscrollBottom;
3383 }
3384
3385 return range;
3386 }
3387
3388 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00003389 public int computeVerticalScrollOffset() {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003390 return Math.max(getScrollY() - getTitleHeight(), 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003391 }
3392
3393 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00003394 public int computeVerticalScrollExtent() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003395 return getViewHeight();
3396 }
3397
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003398 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00003399 public void onDrawVerticalScrollBar(Canvas canvas,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003400 Drawable scrollBar,
3401 int l, int t, int r, int b) {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003402 if (getScrollY() < 0) {
3403 t -= getScrollY();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003404 }
3405 scrollBar.setBounds(l, t + getVisibleTitleHeightImpl(), r, b);
3406 scrollBar.draw(canvas);
3407 }
3408
3409 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00003410 public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003411 boolean clampedY) {
3412 // Special-case layer scrolling so that we do not trigger normal scroll
3413 // updating.
George Mountfcff68f2012-03-20 10:21:57 -07003414 if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
3415 scrollEditText(scrollX, scrollY);
3416 return;
3417 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003418 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
3419 scrollLayerTo(scrollX, scrollY);
George Mount9b6eb692012-06-05 09:19:51 -07003420 animateHandles();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003421 return;
3422 }
3423 mInOverScrollMode = false;
3424 int maxX = computeMaxScrollX();
3425 int maxY = computeMaxScrollY();
3426 if (maxX == 0) {
3427 // do not over scroll x if the page just fits the screen
3428 scrollX = pinLocX(scrollX);
3429 } else if (scrollX < 0 || scrollX > maxX) {
3430 mInOverScrollMode = true;
3431 }
3432 if (scrollY < 0 || scrollY > maxY) {
3433 mInOverScrollMode = true;
3434 }
3435
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003436 int oldX = getScrollX();
3437 int oldY = getScrollY();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003438
Jonathan Dixon3c909522012-02-28 18:45:06 +00003439 mWebViewPrivate.super_scrollTo(scrollX, scrollY);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003440
George Mount9b6eb692012-06-05 09:19:51 -07003441 animateHandles();
3442
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003443 if (mOverScrollGlow != null) {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003444 mOverScrollGlow.pullGlow(getScrollX(), getScrollY(), oldX, oldY, maxX, maxY);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003445 }
3446 }
3447
3448 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003449 * See {@link WebView#getUrl()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003450 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003451 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003452 public String getUrl() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003453 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
3454 return h != null ? h.getUrl() : null;
3455 }
3456
3457 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003458 * See {@link WebView#getOriginalUrl()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003459 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003460 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003461 public String getOriginalUrl() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003462 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
3463 return h != null ? h.getOriginalUrl() : null;
3464 }
3465
3466 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003467 * See {@link WebView#getTitle()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003468 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003469 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003470 public String getTitle() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003471 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
3472 return h != null ? h.getTitle() : null;
3473 }
3474
3475 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003476 * See {@link WebView#getFavicon()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003477 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003478 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003479 public Bitmap getFavicon() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003480 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
3481 return h != null ? h.getFavicon() : null;
3482 }
3483
3484 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003485 * See {@link WebView#getTouchIconUrl()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003486 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003487 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003488 public String getTouchIconUrl() {
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07003489 WebHistoryItemClassic h = mCallbackProxy.getBackForwardList().getCurrentItem();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003490 return h != null ? h.getTouchIconUrl() : null;
3491 }
3492
3493 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003494 * See {@link WebView#getProgress()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003495 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003496 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003497 public int getProgress() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003498 return mCallbackProxy.getProgress();
3499 }
3500
3501 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003502 * See {@link WebView#getContentHeight()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003503 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003504 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003505 public int getContentHeight() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003506 return mContentHeight;
3507 }
3508
3509 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003510 * See {@link WebView#getContentWidth()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003511 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003512 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003513 public int getContentWidth() {
3514 return mContentWidth;
3515 }
3516
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003517 public int getPageBackgroundColor() {
Chris Craikb5dc2152012-05-08 13:44:33 -07003518 if (mNativeClass == 0) return Color.WHITE;
3519 return nativeGetBackgroundColor(mNativeClass);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003520 }
3521
3522 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003523 * See {@link WebView#pauseTimers()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003524 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003525 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003526 public void pauseTimers() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003527 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
3528 }
3529
3530 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003531 * See {@link WebView#resumeTimers()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003532 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003533 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003534 public void resumeTimers() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003535 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
3536 }
3537
3538 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003539 * See {@link WebView#onPause()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003540 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003541 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003542 public void onPause() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003543 if (!mIsPaused) {
3544 mIsPaused = true;
3545 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
3546 // We want to pause the current playing video when switching out
3547 // from the current WebView/tab.
3548 if (mHTML5VideoViewProxy != null) {
3549 mHTML5VideoViewProxy.pauseAndDispatch();
3550 }
3551 if (mNativeClass != 0) {
3552 nativeSetPauseDrawing(mNativeClass, true);
3553 }
3554
John Reck095e8aa2012-09-10 16:07:04 -07003555 cancelDialogs();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003556 WebCoreThreadWatchdog.pause();
3557 }
3558 }
3559
3560 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00003561 public void onWindowVisibilityChanged(int visibility) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003562 updateDrawingState();
3563 }
3564
3565 void updateDrawingState() {
3566 if (mNativeClass == 0 || mIsPaused) return;
Jonathan Dixon3c909522012-02-28 18:45:06 +00003567 if (mWebView.getWindowVisibility() != View.VISIBLE) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003568 nativeSetPauseDrawing(mNativeClass, true);
Jonathan Dixon3c909522012-02-28 18:45:06 +00003569 } else if (mWebView.getVisibility() != View.VISIBLE) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003570 nativeSetPauseDrawing(mNativeClass, true);
3571 } else {
3572 nativeSetPauseDrawing(mNativeClass, false);
3573 }
3574 }
3575
3576 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003577 * See {@link WebView#onResume()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003578 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003579 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003580 public void onResume() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003581 if (mIsPaused) {
3582 mIsPaused = false;
3583 mWebViewCore.sendMessage(EventHub.ON_RESUME);
3584 if (mNativeClass != 0) {
3585 nativeSetPauseDrawing(mNativeClass, false);
3586 }
3587 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003588 // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need
3589 // to ensure that the Watchdog thread is running for the new WebView, so call
3590 // it outside the if block above.
3591 WebCoreThreadWatchdog.resume();
3592 }
3593
3594 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003595 * See {@link WebView#isPaused()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003596 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003597 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003598 public boolean isPaused() {
3599 return mIsPaused;
3600 }
3601
3602 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003603 * See {@link WebView#freeMemory()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003604 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003605 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003606 public void freeMemory() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003607 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
3608 }
3609
3610 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003611 * See {@link WebView#clearCache(boolean)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003612 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003613 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003614 public void clearCache(boolean includeDiskFiles) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003615 // Note: this really needs to be a static method as it clears cache for all
3616 // WebView. But we need mWebViewCore to send message to WebCore thread, so
3617 // we can't make this static.
3618 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
3619 includeDiskFiles ? 1 : 0, 0);
3620 }
3621
3622 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003623 * See {@link WebView#clearFormData()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003624 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003625 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003626 public void clearFormData() {
George Mountbcd5dd72012-03-01 08:39:03 -08003627 if (mAutoCompletePopup != null) {
3628 mAutoCompletePopup.clearAdapter();
3629 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003630 }
3631
3632 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003633 * See {@link WebView#clearHistory()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003634 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003635 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003636 public void clearHistory() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003637 mCallbackProxy.getBackForwardList().setClearPending();
3638 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
3639 }
3640
3641 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003642 * See {@link WebView#clearSslPreferences()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003643 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003644 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003645 public void clearSslPreferences() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003646 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
3647 }
3648
3649 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003650 * See {@link WebView#copyBackForwardList()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003651 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003652 @Override
Jesse Greenwaldfcc1f752012-05-29 15:39:40 -07003653 public WebBackForwardListClassic copyBackForwardList() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003654 return mCallbackProxy.getBackForwardList().clone();
3655 }
3656
Jonathan Dixon19b80112012-03-02 18:18:00 +00003657 /**
Victoria Leased405a432012-03-22 15:53:48 -07003658 * See {@link WebView#setFindListener(WebView.FindListener)}.
3659 * @hide
Victoria Leaseabeb6a72012-03-05 16:29:12 -08003660 */
Jonathan Dixon19274f52012-07-12 18:03:43 -07003661 @Override
3662 public void setFindListener(WebView.FindListener listener) {
Victoria Leaseabeb6a72012-03-05 16:29:12 -08003663 mFindListener = listener;
3664 }
3665
3666 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003667 * See {@link WebView#findNext(boolean)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003668 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003669 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003670 public void findNext(boolean forward) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003671 if (0 == mNativeClass) return; // client isn't initialized
Victoria Lease0b8413b2012-03-26 13:04:10 -07003672 if (mFindRequest != null) {
3673 mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0, mFindRequest);
3674 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003675 }
3676
Jonathan Dixon19b80112012-03-02 18:18:00 +00003677 /**
3678 * See {@link WebView#findAll(String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003679 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003680 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003681 public int findAll(String find) {
3682 return findAllBody(find, false);
3683 }
3684
Jonathan Dixon19274f52012-07-12 18:03:43 -07003685 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003686 public void findAllAsync(String find) {
3687 findAllBody(find, true);
3688 }
3689
3690 private int findAllBody(String find, boolean isAsync) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003691 if (0 == mNativeClass) return 0; // client isn't initialized
Victoria Lease0b8413b2012-03-26 13:04:10 -07003692 mFindRequest = null;
Victoria Leaseabeb6a72012-03-05 16:29:12 -08003693 if (find == null) return 0;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003694 mWebViewCore.removeMessages(EventHub.FIND_ALL);
Victoria Lease0b8413b2012-03-26 13:04:10 -07003695 mFindRequest = new WebViewCore.FindAllRequest(find);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003696 if (isAsync) {
Victoria Lease0b8413b2012-03-26 13:04:10 -07003697 mWebViewCore.sendMessage(EventHub.FIND_ALL, mFindRequest);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003698 return 0; // no need to wait for response
3699 }
Victoria Lease0b8413b2012-03-26 13:04:10 -07003700 synchronized(mFindRequest) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003701 try {
Victoria Lease0b8413b2012-03-26 13:04:10 -07003702 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL, mFindRequest);
3703 while (mFindRequest.mMatchCount == -1) {
3704 mFindRequest.wait();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003705 }
3706 }
3707 catch (InterruptedException e) {
3708 return 0;
3709 }
Victoria Lease0b8413b2012-03-26 13:04:10 -07003710 return mFindRequest.mMatchCount;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003711 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003712 }
3713
3714 /**
3715 * Start an ActionMode for finding text in this WebView. Only works if this
3716 * WebView is attached to the view system.
3717 * @param text If non-null, will be the initial text to search for.
3718 * Otherwise, the last String searched for in this WebView will
3719 * be used to start.
3720 * @param showIme If true, show the IME, assuming the user will begin typing.
3721 * If false and text is non-null, perform a find all.
3722 * @return boolean True if the find dialog is shown, false otherwise.
3723 */
Jonathan Dixon19274f52012-07-12 18:03:43 -07003724 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003725 public boolean showFindDialog(String text, boolean showIme) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003726 FindActionModeCallback callback = new FindActionModeCallback(mContext);
Jonathan Dixon3c909522012-02-28 18:45:06 +00003727 if (mWebView.getParent() == null || mWebView.startActionMode(callback) == null) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003728 // Could not start the action mode, so end Find on page
3729 return false;
3730 }
3731 mCachedOverlappingActionModeHeight = -1;
3732 mFindCallback = callback;
3733 setFindIsUp(true);
Ben Murdoch52c9f7f2013-01-18 00:50:37 +00003734 mFindCallback.setWebView(getWebView());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003735 if (showIme) {
3736 mFindCallback.showSoftInput();
3737 } else if (text != null) {
3738 mFindCallback.setText(text);
3739 mFindCallback.findAll();
3740 return true;
3741 }
3742 if (text == null) {
Victoria Lease0b8413b2012-03-26 13:04:10 -07003743 text = mFindRequest == null ? null : mFindRequest.mSearchText;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003744 }
3745 if (text != null) {
3746 mFindCallback.setText(text);
3747 mFindCallback.findAll();
3748 }
3749 return true;
3750 }
3751
3752 /**
3753 * Keep track of the find callback so that we can remove its titlebar if
3754 * necessary.
3755 */
3756 private FindActionModeCallback mFindCallback;
3757
3758 /**
3759 * Toggle whether the find dialog is showing, for both native and Java.
3760 */
3761 private void setFindIsUp(boolean isUp) {
3762 mFindIsUp = isUp;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003763 }
3764
3765 // Used to know whether the find dialog is open. Affects whether
3766 // or not we draw the highlights for matches.
3767 private boolean mFindIsUp;
3768
Victoria Lease0b8413b2012-03-26 13:04:10 -07003769 // Keep track of the last find request sent.
3770 private WebViewCore.FindAllRequest mFindRequest = null;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003771
3772 /**
3773 * Return the first substring consisting of the address of a physical
3774 * location. Currently, only addresses in the United States are detected,
3775 * and consist of:
3776 * - a house number
3777 * - a street name
3778 * - a street type (Road, Circle, etc), either spelled out or abbreviated
3779 * - a city name
3780 * - a state or territory, either spelled out or two-letter abbr.
3781 * - an optional 5 digit or 9 digit zip code.
3782 *
3783 * All names must be correctly capitalized, and the zip code, if present,
3784 * must be valid for the state. The street type must be a standard USPS
3785 * spelling or abbreviation. The state or territory must also be spelled
3786 * or abbreviated using USPS standards. The house number may not exceed
3787 * five digits.
3788 * @param addr The string to search for addresses.
3789 *
3790 * @return the address, or if no address is found, return null.
3791 */
3792 public static String findAddress(String addr) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003793 return findAddress(addr, false);
3794 }
3795
3796 /**
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003797 * Return the first substring consisting of the address of a physical
3798 * location. Currently, only addresses in the United States are detected,
3799 * and consist of:
3800 * - a house number
3801 * - a street name
3802 * - a street type (Road, Circle, etc), either spelled out or abbreviated
3803 * - a city name
3804 * - a state or territory, either spelled out or two-letter abbr.
3805 * - an optional 5 digit or 9 digit zip code.
3806 *
3807 * Names are optionally capitalized, and the zip code, if present,
3808 * must be valid for the state. The street type must be a standard USPS
3809 * spelling or abbreviation. The state or territory must also be spelled
3810 * or abbreviated using USPS standards. The house number may not exceed
3811 * five digits.
3812 * @param addr The string to search for addresses.
3813 * @param caseInsensitive addr Set to true to make search ignore case.
3814 *
3815 * @return the address, or if no address is found, return null.
3816 */
3817 public static String findAddress(String addr, boolean caseInsensitive) {
3818 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
3819 }
3820
Jonathan Dixon19b80112012-03-02 18:18:00 +00003821 /**
3822 * See {@link WebView#clearMatches()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003823 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003824 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003825 public void clearMatches() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003826 if (mNativeClass == 0)
3827 return;
3828 mWebViewCore.removeMessages(EventHub.FIND_ALL);
3829 mWebViewCore.sendMessage(EventHub.FIND_ALL, null);
3830 }
3831
3832
3833 /**
3834 * Called when the find ActionMode ends.
3835 */
Ben Murdoch52c9f7f2013-01-18 00:50:37 +00003836 @Override
3837 public void notifyFindDialogDismissed() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003838 mFindCallback = null;
3839 mCachedOverlappingActionModeHeight = -1;
3840 if (mWebViewCore == null) {
3841 return;
3842 }
3843 clearMatches();
3844 setFindIsUp(false);
3845 // Now that the dialog has been removed, ensure that we scroll to a
3846 // location that is not beyond the end of the page.
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003847 pinScrollTo(getScrollX(), getScrollY(), false, 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003848 invalidate();
3849 }
3850
3851 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00003852 * See {@link WebView#documentHasImages(Message)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003853 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00003854 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003855 public void documentHasImages(Message response) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003856 if (response == null) {
3857 return;
3858 }
3859 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
3860 }
3861
3862 /**
3863 * Request the scroller to abort any ongoing animation
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003864 */
3865 public void stopScroll() {
3866 mScroller.forceFinished(true);
3867 mLastVelocity = 0;
3868 }
3869
3870 @Override
3871 public void computeScroll() {
3872 if (mScroller.computeScrollOffset()) {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003873 int oldX = getScrollX();
3874 int oldY = getScrollY();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003875 int x = mScroller.getCurrX();
3876 int y = mScroller.getCurrY();
3877 invalidate(); // So we draw again
3878
3879 if (!mScroller.isFinished()) {
3880 int rangeX = computeMaxScrollX();
3881 int rangeY = computeMaxScrollY();
3882 int overflingDistance = mOverflingDistance;
3883
3884 // Use the layer's scroll data if needed.
3885 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
3886 oldX = mScrollingLayerRect.left;
3887 oldY = mScrollingLayerRect.top;
3888 rangeX = mScrollingLayerRect.right;
3889 rangeY = mScrollingLayerRect.bottom;
3890 // No overscrolling for layers.
3891 overflingDistance = 0;
George Mountfcff68f2012-03-20 10:21:57 -07003892 } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
3893 oldX = getTextScrollX();
3894 oldY = getTextScrollY();
3895 rangeX = getMaxTextScrollX();
3896 rangeY = getMaxTextScrollY();
3897 overflingDistance = 0;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003898 }
3899
Jonathan Dixon3c909522012-02-28 18:45:06 +00003900 mWebViewPrivate.overScrollBy(x - oldX, y - oldY, oldX, oldY,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003901 rangeX, rangeY,
3902 overflingDistance, overflingDistance, false);
3903
3904 if (mOverScrollGlow != null) {
3905 mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
3906 }
3907 } else {
George Mountfcff68f2012-03-20 10:21:57 -07003908 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003909 // Update the layer position instead of WebView.
3910 scrollLayerTo(x, y);
George Mountfcff68f2012-03-20 10:21:57 -07003911 } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
3912 scrollEditText(x, y);
3913 } else {
3914 setScrollXRaw(x);
3915 setScrollYRaw(y);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003916 }
3917 abortAnimation();
3918 nativeSetIsScrolling(false);
3919 if (!mBlockWebkitViewMessages) {
3920 WebViewCore.resumePriority();
3921 if (!mSelectingText) {
3922 WebViewCore.resumeUpdatePicture(mWebViewCore);
3923 }
3924 }
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003925 if (oldX != getScrollX() || oldY != getScrollY()) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003926 sendOurVisibleRect();
3927 }
3928 }
3929 } else {
Jonathan Dixon3c909522012-02-28 18:45:06 +00003930 mWebViewPrivate.super_computeScroll();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003931 }
3932 }
3933
3934 private void scrollLayerTo(int x, int y) {
George Mountbcd5dd72012-03-01 08:39:03 -08003935 int dx = mScrollingLayerRect.left - x;
3936 int dy = mScrollingLayerRect.top - y;
Chris Craik5f3c08a2012-05-01 15:07:39 -07003937 if ((dx == 0 && dy == 0) || mNativeClass == 0) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003938 return;
3939 }
3940 if (mSelectingText) {
George Mountb49f2bb2012-06-01 14:29:37 -07003941 if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) {
3942 mSelectCursorBase.offset(dx, dy);
3943 mSelectCursorBaseTextQuad.offset(dx, dy);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003944 }
George Mountb49f2bb2012-06-01 14:29:37 -07003945 if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) {
3946 mSelectCursorExtent.offset(dx, dy);
3947 mSelectCursorExtentTextQuad.offset(dx, dy);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003948 }
3949 }
George Mountf70276a2012-03-12 14:22:10 -07003950 if (mAutoCompletePopup != null &&
3951 mCurrentScrollingLayerId == mEditTextLayerId) {
George Mount7102eb22012-04-10 13:41:51 -07003952 mEditTextContentBounds.offset(dx, dy);
George Mountf70276a2012-03-12 14:22:10 -07003953 mAutoCompletePopup.resetRect();
George Mountbcd5dd72012-03-01 08:39:03 -08003954 }
Chris Craik5f3c08a2012-05-01 15:07:39 -07003955 nativeScrollLayer(mNativeClass, mCurrentScrollingLayerId, x, y);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003956 mScrollingLayerRect.left = x;
3957 mScrollingLayerRect.top = y;
3958 mWebViewCore.sendMessage(WebViewCore.EventHub.SCROLL_LAYER, mCurrentScrollingLayerId,
3959 mScrollingLayerRect);
Jonathan Dixon3c909522012-02-28 18:45:06 +00003960 mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003961 invalidate();
3962 }
3963
3964 private static int computeDuration(int dx, int dy) {
3965 int distance = Math.max(Math.abs(dx), Math.abs(dy));
3966 int duration = distance * 1000 / STD_SPEED;
3967 return Math.min(duration, MAX_DURATION);
3968 }
3969
3970 // helper to pin the scrollBy parameters (already in view coordinates)
3971 // returns true if the scroll was changed
3972 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003973 return pinScrollTo(getScrollX() + dx, getScrollY() + dy, animate, animationDuration);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003974 }
3975 // helper to pin the scrollTo parameters (already in view coordinates)
3976 // returns true if the scroll was changed
3977 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
Victoria Lease3beeb1c2012-03-09 09:03:40 -08003978 abortAnimation();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003979 x = pinLocX(x);
3980 y = pinLocY(y);
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003981 int dx = x - getScrollX();
3982 int dy = y - getScrollY();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003983
3984 if ((dx | dy) == 0) {
3985 return false;
3986 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003987 if (animate) {
3988 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00003989 mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003990 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003991 invalidate();
3992 } else {
Jonathan Dixon3c909522012-02-28 18:45:06 +00003993 mWebView.scrollTo(x, y);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08003994 }
3995 return true;
3996 }
3997
3998 // Scale from content to view coordinates, and pin.
3999 // Also called by jni webview.cpp
4000 private boolean setContentScrollBy(int cx, int cy, boolean animate) {
4001 if (mDrawHistory) {
4002 // disallow WebView to change the scroll position as History Picture
4003 // is used in the view system.
4004 // TODO: as we switchOutDrawHistory when trackball or navigation
4005 // keys are hit, this should be safe. Right?
4006 return false;
4007 }
4008 cx = contentToViewDimension(cx);
4009 cy = contentToViewDimension(cy);
4010 if (mHeightCanMeasure) {
4011 // move our visible rect according to scroll request
4012 if (cy != 0) {
4013 Rect tempRect = new Rect();
4014 calcOurVisibleRect(tempRect);
4015 tempRect.offset(cx, cy);
Jonathan Dixon3c909522012-02-28 18:45:06 +00004016 mWebView.requestRectangleOnScreen(tempRect);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004017 }
4018 // FIXME: We scroll horizontally no matter what because currently
4019 // ScrollView and ListView will not scroll horizontally.
4020 // FIXME: Why do we only scroll horizontally if there is no
4021 // vertical scroll?
4022// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
4023 return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
4024 } else {
4025 return pinScrollBy(cx, cy, animate, 0);
4026 }
4027 }
4028
4029 /**
4030 * Called by CallbackProxy when the page starts loading.
4031 * @param url The URL of the page which has started loading.
4032 */
4033 /* package */ void onPageStarted(String url) {
4034 // every time we start a new page, we want to reset the
4035 // WebView certificate: if the new site is secure, we
4036 // will reload it and get a new certificate set;
4037 // if the new site is not secure, the certificate must be
4038 // null, and that will be the case
Jonathan Dixon3c909522012-02-28 18:45:06 +00004039 mWebView.setCertificate(null);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004040
alanvfdfd0d82012-08-09 18:05:17 -07004041 if (isAccessibilityInjectionEnabled()) {
alanv525823a2012-05-16 20:01:51 -07004042 getAccessibilityInjector().onPageStarted(url);
4043 }
George Mount647827a2012-05-09 11:37:13 -07004044
4045 // Don't start out editing.
4046 mIsEditingText = false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004047 }
4048
4049 /**
4050 * Called by CallbackProxy when the page finishes loading.
4051 * @param url The URL of the page which has finished loading.
4052 */
4053 /* package */ void onPageFinished(String url) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004054 mZoomManager.onPageFinished(url);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004055
alanvfdfd0d82012-08-09 18:05:17 -07004056 if (isAccessibilityInjectionEnabled()) {
alanv525823a2012-05-16 20:01:51 -07004057 getAccessibilityInjector().onPageFinished(url);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004058 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004059 }
4060
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004061 // scale from content to view coordinates, and pin
John Recka60a1892012-04-19 10:48:20 -07004062 private void contentScrollTo(int cx, int cy, boolean animate) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004063 if (mDrawHistory) {
4064 // disallow WebView to change the scroll position as History Picture
4065 // is used in the view system.
4066 return;
4067 }
John Recka60a1892012-04-19 10:48:20 -07004068 int vx = contentToViewX(cx);
4069 int vy = contentToViewY(cy);
4070 pinScrollTo(vx, vy, animate, 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004071 }
4072
4073 /**
4074 * These are from webkit, and are in content coordinate system (unzoomed)
4075 */
4076 private void contentSizeChanged(boolean updateLayout) {
4077 // suppress 0,0 since we usually see real dimensions soon after
4078 // this avoids drawing the prev content in a funny place. If we find a
4079 // way to consolidate these notifications, this check may become
4080 // obsolete
4081 if ((mContentWidth | mContentHeight) == 0) {
4082 return;
4083 }
4084
4085 if (mHeightCanMeasure) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00004086 if (mWebView.getMeasuredHeight() != contentToViewDimension(mContentHeight)
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004087 || updateLayout) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00004088 mWebView.requestLayout();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004089 }
4090 } else if (mWidthCanMeasure) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00004091 if (mWebView.getMeasuredWidth() != contentToViewDimension(mContentWidth)
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004092 || updateLayout) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00004093 mWebView.requestLayout();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004094 }
4095 } else {
4096 // If we don't request a layout, try to send our view size to the
4097 // native side to ensure that WebCore has the correct dimensions.
4098 sendViewSizeZoom(false);
4099 }
4100 }
4101
4102 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00004103 * See {@link WebView#setWebViewClient(WebViewClient)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004104 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00004105 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004106 public void setWebViewClient(WebViewClient client) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004107 mCallbackProxy.setWebViewClient(client);
4108 }
4109
4110 /**
4111 * Gets the WebViewClient
4112 * @return the current WebViewClient instance.
4113 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00004114 * This is an implementation detail.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004115 */
4116 public WebViewClient getWebViewClient() {
4117 return mCallbackProxy.getWebViewClient();
4118 }
4119
4120 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00004121 * See {@link WebView#setDownloadListener(DownloadListener)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004122 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00004123 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004124 public void setDownloadListener(DownloadListener listener) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004125 mCallbackProxy.setDownloadListener(listener);
4126 }
4127
4128 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00004129 * See {@link WebView#setWebChromeClient(WebChromeClient)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004130 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00004131 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004132 public void setWebChromeClient(WebChromeClient client) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004133 mCallbackProxy.setWebChromeClient(client);
4134 }
4135
4136 /**
4137 * Gets the chrome handler.
4138 * @return the current WebChromeClient instance.
4139 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00004140 * This is an implementation detail.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004141 */
4142 public WebChromeClient getWebChromeClient() {
4143 return mCallbackProxy.getWebChromeClient();
4144 }
4145
4146 /**
4147 * Set the back/forward list client. This is an implementation of
4148 * WebBackForwardListClient for handling new items and changes in the
4149 * history index.
4150 * @param client An implementation of WebBackForwardListClient.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004151 */
4152 public void setWebBackForwardListClient(WebBackForwardListClient client) {
4153 mCallbackProxy.setWebBackForwardListClient(client);
4154 }
4155
4156 /**
4157 * Gets the WebBackForwardListClient.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004158 */
4159 public WebBackForwardListClient getWebBackForwardListClient() {
4160 return mCallbackProxy.getWebBackForwardListClient();
4161 }
4162
4163 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00004164 * See {@link WebView#setPictureListener(PictureListener)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004165 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00004166 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004167 @Deprecated
4168 public void setPictureListener(PictureListener listener) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004169 mPictureListener = listener;
4170 }
4171
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004172 /* FIXME: Debug only! Remove for SDK! */
4173 public void externalRepresentation(Message callback) {
4174 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
4175 }
4176
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004177 /* FIXME: Debug only! Remove for SDK! */
4178 public void documentAsText(Message callback) {
4179 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
4180 }
4181
4182 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00004183 * See {@link WebView#addJavascriptInterface(Object, String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004184 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00004185 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004186 public void addJavascriptInterface(Object object, String name) {
Selim Gurun94740e62012-09-04 16:45:53 -07004187
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004188 if (object == null) {
4189 return;
4190 }
4191 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
Selim Gurune91d5be2012-09-11 16:11:22 -07004192
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004193 arg.mObject = object;
4194 arg.mInterfaceName = name;
Selim Gurune91d5be2012-09-11 16:11:22 -07004195
4196 // starting with JELLY_BEAN_MR1, annotations are mandatory for enabling access to
4197 // methods that are accessible from JS.
4198 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
4199 arg.mRequireAnnotation = true;
4200 } else {
4201 arg.mRequireAnnotation = false;
4202 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004203 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
4204 }
4205
4206 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00004207 * See {@link WebView#removeJavascriptInterface(String)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004208 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00004209 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004210 public void removeJavascriptInterface(String interfaceName) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004211 if (mWebViewCore != null) {
4212 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
4213 arg.mInterfaceName = interfaceName;
4214 mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
4215 }
4216 }
4217
4218 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00004219 * See {@link WebView#getSettings()}
4220 * Note this returns WebSettingsClassic, a sub-class of WebSettings, which can be used
4221 * to access extension APIs.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004222 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00004223 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00004224 public WebSettingsClassic getSettings() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004225 return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
4226 }
4227
Jonathan Dixon19b80112012-03-02 18:18:00 +00004228 /**
4229 * See {@link WebView#getPluginList()}
4230 */
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004231 @Deprecated
4232 public static synchronized PluginList getPluginList() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004233 return new PluginList();
4234 }
4235
Jonathan Dixon19b80112012-03-02 18:18:00 +00004236 /**
4237 * See {@link WebView#refreshPlugins(boolean)}
4238 */
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004239 @Deprecated
4240 public void refreshPlugins(boolean reloadOpenPages) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004241 }
4242
4243 //-------------------------------------------------------------------------
4244 // Override View methods
4245 //-------------------------------------------------------------------------
4246
4247 @Override
4248 protected void finalize() throws Throwable {
4249 try {
John Reckd5e29372012-06-07 11:35:35 -07004250 destroy();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004251 } finally {
4252 super.finalize();
4253 }
4254 }
4255
John Reckb2676f72012-03-02 14:13:06 -08004256 private void drawContent(Canvas canvas) {
4257 if (mDrawHistory) {
4258 canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
4259 canvas.drawPicture(mHistoryPicture);
4260 return;
4261 }
4262 if (mNativeClass == 0) return;
4263
4264 boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
4265 boolean animateScroll = ((!mScroller.isFinished()
4266 || mVelocityTracker != null)
4267 && (mTouchMode != TOUCH_DRAG_MODE ||
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07004268 mHeldMotionless != MOTIONLESS_TRUE));
John Reckb2676f72012-03-02 14:13:06 -08004269 if (mTouchMode == TOUCH_DRAG_MODE) {
4270 if (mHeldMotionless == MOTIONLESS_PENDING) {
4271 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
John Reckb2676f72012-03-02 14:13:06 -08004272 mHeldMotionless = MOTIONLESS_FALSE;
4273 }
4274 if (mHeldMotionless == MOTIONLESS_FALSE) {
4275 mPrivateHandler.sendMessageDelayed(mPrivateHandler
4276 .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
John Reckb2676f72012-03-02 14:13:06 -08004277 mHeldMotionless = MOTIONLESS_PENDING;
4278 }
4279 }
4280 int saveCount = canvas.save();
4281 if (animateZoom) {
4282 mZoomManager.animateZoom(canvas);
4283 } else if (!canvas.isHardwareAccelerated()) {
4284 canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
4285 }
4286
4287 boolean UIAnimationsRunning = false;
4288 // Currently for each draw we compute the animation values;
4289 // We may in the future decide to do that independently.
4290 if (mNativeClass != 0 && !canvas.isHardwareAccelerated()
4291 && nativeEvaluateLayersAnimations(mNativeClass)) {
4292 UIAnimationsRunning = true;
4293 // If we have unfinished (or unstarted) animations,
4294 // we ask for a repaint. We only need to do this in software
4295 // rendering (with hardware rendering we already have a different
4296 // method of requesting a repaint)
4297 mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
4298 invalidate();
4299 }
4300
4301 // decide which adornments to draw
4302 int extras = DRAW_EXTRAS_NONE;
John Reckd4796462012-05-02 18:23:13 -07004303 if (!mFindIsUp && mShowTextSelectionExtra) {
John Reckb2676f72012-03-02 14:13:06 -08004304 extras = DRAW_EXTRAS_SELECTION;
4305 }
4306
4307 calcOurContentVisibleRectF(mVisibleContentRect);
4308 if (canvas.isHardwareAccelerated()) {
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07004309 Rect invScreenRect = mIsWebViewVisible ? mInvScreenRect : null;
4310 Rect screenRect = mIsWebViewVisible ? mScreenRect : null;
John Reckb2676f72012-03-02 14:13:06 -08004311
Teng-Hui Zhu508d7052012-05-03 14:31:55 -07004312 int functor = nativeCreateDrawGLFunction(mNativeClass, invScreenRect,
4313 screenRect, mVisibleContentRect, getScale(), extras);
John Reckb2676f72012-03-02 14:13:06 -08004314 ((HardwareCanvas) canvas).callDrawGLFunction(functor);
4315 if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) {
4316 mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled();
4317 nativeUseHardwareAccelSkia(mHardwareAccelSkia);
4318 }
4319
4320 } else {
4321 DrawFilter df = null;
4322 if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
4323 df = mZoomFilter;
4324 } else if (animateScroll) {
4325 df = mScrollFilter;
4326 }
4327 canvas.setDrawFilter(df);
John Reck33b019b2012-04-28 14:35:59 -07004328 nativeDraw(canvas, mVisibleContentRect, mBackgroundColor, extras);
John Reckb2676f72012-03-02 14:13:06 -08004329 canvas.setDrawFilter(null);
John Reckb2676f72012-03-02 14:13:06 -08004330 }
4331
4332 canvas.restoreToCount(saveCount);
George Mount30d773f2012-04-20 08:34:44 -07004333 drawTextSelectionHandles(canvas);
John Reckb2676f72012-03-02 14:13:06 -08004334
4335 if (extras == DRAW_EXTRAS_CURSOR_RING) {
4336 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
4337 mTouchMode = TOUCH_SHORTPRESS_MODE;
4338 }
4339 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004340 }
4341
4342 /**
4343 * Draw the background when beyond bounds
4344 * @param canvas Canvas to draw into
4345 */
4346 private void drawOverScrollBackground(Canvas canvas) {
4347 if (mOverScrollBackground == null) {
4348 mOverScrollBackground = new Paint();
4349 Bitmap bm = BitmapFactory.decodeResource(
4350 mContext.getResources(),
4351 com.android.internal.R.drawable.status_bar_background);
4352 mOverScrollBackground.setShader(new BitmapShader(bm,
4353 Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
4354 mOverScrollBorder = new Paint();
4355 mOverScrollBorder.setStyle(Paint.Style.STROKE);
4356 mOverScrollBorder.setStrokeWidth(0);
4357 mOverScrollBorder.setColor(0xffbbbbbb);
4358 }
4359
4360 int top = 0;
4361 int right = computeRealHorizontalScrollRange();
4362 int bottom = top + computeRealVerticalScrollRange();
4363 // first draw the background and anchor to the top of the view
4364 canvas.save();
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00004365 canvas.translate(getScrollX(), getScrollY());
4366 canvas.clipRect(-getScrollX(), top - getScrollY(), right - getScrollX(), bottom
4367 - getScrollY(), Region.Op.DIFFERENCE);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004368 canvas.drawPaint(mOverScrollBackground);
4369 canvas.restore();
4370 // then draw the border
4371 canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
4372 // next clip the region for the content
4373 canvas.clipRect(0, top, right, bottom);
4374 }
4375
4376 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00004377 public void onDraw(Canvas canvas) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004378 if (inFullScreenMode()) {
4379 return; // no need to draw anything if we aren't visible.
4380 }
4381 // if mNativeClass is 0, the WebView is either destroyed or not
4382 // initialized. In either case, just draw the background color and return
4383 if (mNativeClass == 0) {
4384 canvas.drawColor(mBackgroundColor);
4385 return;
4386 }
4387
4388 // if both mContentWidth and mContentHeight are 0, it means there is no
4389 // valid Picture passed to WebView yet. This can happen when WebView
4390 // just starts. Draw the background and return.
4391 if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) {
4392 canvas.drawColor(mBackgroundColor);
4393 return;
4394 }
4395
4396 if (canvas.isHardwareAccelerated()) {
4397 mZoomManager.setHardwareAccelerated();
4398 } else {
4399 mWebViewCore.resumeWebKitDraw();
4400 }
4401
4402 int saveCount = canvas.save();
4403 if (mInOverScrollMode && !getSettings()
4404 .getUseWebViewBackgroundForOverscrollBackground()) {
4405 drawOverScrollBackground(canvas);
4406 }
Michael Kolb8116da52012-04-02 16:16:16 -07004407
4408 canvas.translate(0, getTitleHeight());
John Reckb2676f72012-03-02 14:13:06 -08004409 drawContent(canvas);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004410 canvas.restoreToCount(saveCount);
4411
4412 if (AUTO_REDRAW_HACK && mAutoRedraw) {
4413 invalidate();
4414 }
4415 mWebViewCore.signalRepaintDone();
4416
4417 if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) {
4418 invalidate();
4419 }
4420
4421 if (mFocusTransition != null) {
4422 mFocusTransition.draw(canvas);
4423 } else if (shouldDrawHighlightRect()) {
4424 RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
4425 Rect r = new Rect();
4426 while (iter.next(r)) {
4427 canvas.drawRect(r, mTouchHightlightPaint);
4428 }
4429 }
4430 if (DEBUG_TOUCH_HIGHLIGHT) {
4431 if (getSettings().getNavDump()) {
4432 if ((mTouchHighlightX | mTouchHighlightY) != 0) {
4433 if (mTouchCrossHairColor == null) {
4434 mTouchCrossHairColor = new Paint();
4435 mTouchCrossHairColor.setColor(Color.RED);
4436 }
4437 canvas.drawLine(mTouchHighlightX - mNavSlop,
4438 mTouchHighlightY - mNavSlop, mTouchHighlightX
4439 + mNavSlop + 1, mTouchHighlightY + mNavSlop
4440 + 1, mTouchCrossHairColor);
4441 canvas.drawLine(mTouchHighlightX + mNavSlop + 1,
4442 mTouchHighlightY - mNavSlop, mTouchHighlightX
4443 - mNavSlop,
4444 mTouchHighlightY + mNavSlop + 1,
4445 mTouchCrossHairColor);
4446 }
4447 }
4448 }
4449 }
4450
4451 private void removeTouchHighlight() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004452 setTouchHighlightRects(null);
4453 }
4454
4455 @Override
4456 public void setLayoutParams(ViewGroup.LayoutParams params) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00004457 if (params.height == AbsoluteLayout.LayoutParams.WRAP_CONTENT) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004458 mWrapContent = true;
4459 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00004460 mWebViewPrivate.super_setLayoutParams(params);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004461 }
4462
4463 @Override
4464 public boolean performLongClick() {
4465 // performLongClick() is the result of a delayed message. If we switch
4466 // to windows overview, the WebView will be temporarily removed from the
4467 // view system. In that case, do nothing.
Jonathan Dixon3c909522012-02-28 18:45:06 +00004468 if (mWebView.getParent() == null) return false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004469
4470 // A multi-finger gesture can look like a long press; make sure we don't take
4471 // long press actions if we're scaling.
Adam Powell1027ed22012-08-31 17:19:24 -07004472 final ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004473 if (detector != null && detector.isInProgress()) {
4474 return false;
4475 }
4476
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004477 if (mSelectingText) return false; // long click does nothing on selection
4478 /* if long click brings up a context menu, the super function
4479 * returns true and we're done. Otherwise, nothing happened when
4480 * the user clicked. */
Jonathan Dixon3c909522012-02-28 18:45:06 +00004481 if (mWebViewPrivate.super_performLongClick()) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004482 return true;
4483 }
4484 /* In the case where the application hasn't already handled the long
4485 * click action, look for a word under the click. If one is found,
4486 * animate the text selection into view.
4487 * FIXME: no animation code yet */
4488 final boolean isSelecting = selectText();
4489 if (isSelecting) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00004490 mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004491 } else if (focusCandidateIsEditableText()) {
4492 mSelectCallback = new SelectActionModeCallback();
4493 mSelectCallback.setWebView(this);
4494 mSelectCallback.setTextSelected(false);
Jonathan Dixon3c909522012-02-28 18:45:06 +00004495 mWebView.startActionMode(mSelectCallback);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004496 }
4497 return isSelecting;
4498 }
4499
4500 /**
4501 * Select the word at the last click point.
4502 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00004503 * This is an implementation detail.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004504 */
4505 public boolean selectText() {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00004506 int x = viewToContentX(mLastTouchX + getScrollX());
4507 int y = viewToContentY(mLastTouchY + getScrollY());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004508 return selectText(x, y);
4509 }
4510
4511 /**
4512 * Select the word at the indicated content coordinates.
4513 */
4514 boolean selectText(int x, int y) {
George Mount07c9e292012-04-13 13:09:28 -07004515 if (mWebViewCore == null) {
4516 return false;
4517 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004518 mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y);
4519 return true;
4520 }
4521
4522 private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
4523
4524 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00004525 public void onConfigurationChanged(Configuration newConfig) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004526 mCachedOverlappingActionModeHeight = -1;
4527 if (mSelectingText && mOrientation != newConfig.orientation) {
John Reck87170612013-04-30 01:17:15 +00004528 selectionDone();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004529 }
4530 mOrientation = newConfig.orientation;
4531 if (mWebViewCore != null && !mBlockWebkitViewMessages) {
4532 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
4533 }
4534 }
4535
4536 /**
4537 * Keep track of the Callback so we can end its ActionMode or remove its
4538 * titlebar.
4539 */
4540 private SelectActionModeCallback mSelectCallback;
4541
Chris Craikc2c95432012-04-25 15:13:52 -07004542 void setBaseLayer(int layer, boolean showVisualIndicator,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004543 boolean isPictureAfterFirstLayout) {
4544 if (mNativeClass == 0)
4545 return;
4546 boolean queueFull;
John Reckec1b71a2012-05-25 14:02:26 -07004547 final int scrollingLayer = (mTouchMode == TOUCH_DRAG_LAYER_MODE)
4548 ? mCurrentScrollingLayerId : 0;
Chris Craikc2c95432012-04-25 15:13:52 -07004549 queueFull = nativeSetBaseLayer(mNativeClass, layer,
John Reckec1b71a2012-05-25 14:02:26 -07004550 showVisualIndicator, isPictureAfterFirstLayout,
4551 scrollingLayer);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004552
John Reck834f66b2012-03-27 12:52:57 -07004553 if (queueFull) {
Chris Craik07b20112012-03-07 11:55:57 -08004554 mWebViewCore.pauseWebKitDraw();
John Reck834f66b2012-03-27 12:52:57 -07004555 } else {
4556 mWebViewCore.resumeWebKitDraw();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004557 }
4558
4559 if (mHTML5VideoViewProxy != null) {
4560 mHTML5VideoViewProxy.setBaseLayer(layer);
4561 }
4562 }
4563
4564 int getBaseLayer() {
4565 if (mNativeClass == 0) {
4566 return 0;
4567 }
Chris Craikb5dc2152012-05-08 13:44:33 -07004568 return nativeGetBaseLayer(mNativeClass);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004569 }
4570
4571 private void onZoomAnimationStart() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004572 }
4573
4574 private void onZoomAnimationEnd() {
George Mountbcd5dd72012-03-01 08:39:03 -08004575 mPrivateHandler.sendEmptyMessage(RELOCATE_AUTO_COMPLETE_POPUP);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004576 }
4577
4578 void onFixedLengthZoomAnimationStart() {
4579 WebViewCore.pauseUpdatePicture(getWebViewCore());
4580 onZoomAnimationStart();
4581 }
4582
4583 void onFixedLengthZoomAnimationEnd() {
4584 if (!mBlockWebkitViewMessages && !mSelectingText) {
4585 WebViewCore.resumeUpdatePicture(mWebViewCore);
4586 }
4587 onZoomAnimationEnd();
4588 }
4589
4590 private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
4591 Paint.DITHER_FLAG |
4592 Paint.SUBPIXEL_TEXT_FLAG;
4593 private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
4594 Paint.DITHER_FLAG;
4595
4596 private final DrawFilter mZoomFilter =
4597 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
4598 // If we need to trade better quality for speed, set mScrollFilter to null
4599 private final DrawFilter mScrollFilter =
4600 new PaintFlagsDrawFilter(SCROLL_BITS, 0);
4601
George Mount30d773f2012-04-20 08:34:44 -07004602 private class SelectionHandleAlpha {
4603 private int mAlpha = 0;
George Mount9b6eb692012-06-05 09:19:51 -07004604 private int mTargetAlpha = 0;
4605
George Mount30d773f2012-04-20 08:34:44 -07004606 public void setAlpha(int alpha) {
4607 mAlpha = alpha;
George Mount9b6eb692012-06-05 09:19:51 -07004608 // TODO: Use partial invalidate
4609 invalidate();
George Mount30d773f2012-04-20 08:34:44 -07004610 }
4611
4612 public int getAlpha() {
4613 return mAlpha;
4614 }
4615
George Mount9b6eb692012-06-05 09:19:51 -07004616 public void setTargetAlpha(int alpha) {
4617 mTargetAlpha = alpha;
4618 }
4619
4620 public int getTargetAlpha() {
4621 return mTargetAlpha;
4622 }
4623
George Mount30d773f2012-04-20 08:34:44 -07004624 }
4625
4626 private void startSelectingText() {
4627 mSelectingText = true;
John Reckd4796462012-05-02 18:23:13 -07004628 mShowTextSelectionExtra = true;
George Mount9b6eb692012-06-05 09:19:51 -07004629 animateHandles();
George Mount30d773f2012-04-20 08:34:44 -07004630 }
George Mount9b6eb692012-06-05 09:19:51 -07004631
4632 private void animateHandle(boolean canShow, ObjectAnimator animator,
4633 Point selectionPoint, int selectionLayerId,
4634 SelectionHandleAlpha alpha) {
4635 boolean isVisible = canShow && mSelectingText
4636 && ((mSelectionStarted && mSelectDraggingCursor == selectionPoint)
4637 || isHandleVisible(selectionPoint, selectionLayerId));
4638 int targetValue = isVisible ? 255 : 0;
4639 if (targetValue != alpha.getTargetAlpha()) {
4640 alpha.setTargetAlpha(targetValue);
4641 animator.setIntValues(targetValue);
4642 animator.setDuration(SELECTION_HANDLE_ANIMATION_MS);
4643 animator.start();
4644 }
4645 }
4646
4647 private void animateHandles() {
4648 boolean canShowBase = mSelectingText;
4649 boolean canShowExtent = mSelectingText && !mIsCaretSelection;
4650 animateHandle(canShowBase, mBaseHandleAlphaAnimator, mSelectCursorBase,
4651 mSelectCursorBaseLayerId, mBaseAlpha);
4652 animateHandle(canShowExtent, mExtentHandleAlphaAnimator,
4653 mSelectCursorExtent, mSelectCursorExtentLayerId,
4654 mExtentAlpha);
4655 }
4656
George Mount30d773f2012-04-20 08:34:44 -07004657 private void endSelectingText() {
4658 mSelectingText = false;
John Reckd4796462012-05-02 18:23:13 -07004659 mShowTextSelectionExtra = false;
George Mount9b6eb692012-06-05 09:19:51 -07004660 animateHandles();
George Mount30d773f2012-04-20 08:34:44 -07004661 }
4662
George Mount9a676bf2012-03-01 16:28:12 -08004663 private void ensureSelectionHandles() {
4664 if (mSelectHandleCenter == null) {
4665 mSelectHandleCenter = mContext.getResources().getDrawable(
Victoria Lease37563c522012-09-21 09:31:11 -07004666 com.android.internal.R.drawable.text_select_handle_middle).mutate();
George Mount9a676bf2012-03-01 16:28:12 -08004667 mSelectHandleLeft = mContext.getResources().getDrawable(
Victoria Lease37563c522012-09-21 09:31:11 -07004668 com.android.internal.R.drawable.text_select_handle_left).mutate();
George Mount9a676bf2012-03-01 16:28:12 -08004669 mSelectHandleRight = mContext.getResources().getDrawable(
Victoria Lease37563c522012-09-21 09:31:11 -07004670 com.android.internal.R.drawable.text_select_handle_right).mutate();
George Mountb49f2bb2012-06-01 14:29:37 -07004671 // All handles have the same height, so we can save effort with
4672 // this assumption.
4673 mSelectOffset = new Point(0,
George Mountf9c1f992012-03-21 16:06:10 -07004674 -mSelectHandleLeft.getIntrinsicHeight());
George Mount9a676bf2012-03-01 16:28:12 -08004675 }
4676 }
4677
George Mountb49f2bb2012-06-01 14:29:37 -07004678 private void drawHandle(Point point, int handleId, Rect bounds,
George Mount9b6eb692012-06-05 09:19:51 -07004679 int alpha, Canvas canvas) {
George Mountb49f2bb2012-06-01 14:29:37 -07004680 int offset;
4681 int width;
4682 int height;
4683 Drawable drawable;
4684 boolean isLeft = nativeIsHandleLeft(mNativeClass, handleId);
4685 if (isLeft) {
4686 drawable = mSelectHandleLeft;
4687 width = mSelectHandleLeft.getIntrinsicWidth();
4688 height = mSelectHandleLeft.getIntrinsicHeight();
4689 // Magic formula copied from TextView
4690 offset = (width * 3) / 4;
4691 } else {
4692 drawable = mSelectHandleRight;
4693 width = mSelectHandleRight.getIntrinsicWidth();
4694 height = mSelectHandleRight.getIntrinsicHeight();
4695 // Magic formula copied from TextView
4696 offset = width / 4;
4697 }
4698 int x = contentToViewDimension(point.x);
4699 int y = contentToViewDimension(point.y);
4700 bounds.set(x - offset, y, x - offset + width, y + height);
4701 drawable.setBounds(bounds);
George Mount9b6eb692012-06-05 09:19:51 -07004702 drawable.setAlpha(alpha);
George Mountb49f2bb2012-06-01 14:29:37 -07004703 drawable.draw(canvas);
4704 }
4705
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004706 private void drawTextSelectionHandles(Canvas canvas) {
George Mount9b6eb692012-06-05 09:19:51 -07004707 if (mBaseAlpha.getAlpha() == 0 && mExtentAlpha.getAlpha() == 0) {
George Mount30d773f2012-04-20 08:34:44 -07004708 return;
4709 }
George Mount9a676bf2012-03-01 16:28:12 -08004710 ensureSelectionHandles();
George Mount9b6eb692012-06-05 09:19:51 -07004711 if (mIsCaretSelection) {
4712 // Caret handle is centered
4713 int x = contentToViewDimension(mSelectCursorBase.x) -
4714 (mSelectHandleCenter.getIntrinsicWidth() / 2);
4715 int y = contentToViewDimension(mSelectCursorBase.y);
4716 mSelectHandleBaseBounds.set(x, y,
4717 x + mSelectHandleCenter.getIntrinsicWidth(),
4718 y + mSelectHandleCenter.getIntrinsicHeight());
4719 mSelectHandleCenter.setBounds(mSelectHandleBaseBounds);
4720 mSelectHandleCenter.setAlpha(mBaseAlpha.getAlpha());
4721 mSelectHandleCenter.draw(canvas);
4722 } else {
4723 drawHandle(mSelectCursorBase, HANDLE_ID_BASE,
4724 mSelectHandleBaseBounds, mBaseAlpha.getAlpha(), canvas);
4725 drawHandle(mSelectCursorExtent, HANDLE_ID_EXTENT,
4726 mSelectHandleExtentBounds, mExtentAlpha.getAlpha(), canvas);
George Mount30d773f2012-04-20 08:34:44 -07004727 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004728 }
4729
George Mount9b6eb692012-06-05 09:19:51 -07004730 private boolean isHandleVisible(Point selectionPoint, int layerId) {
4731 boolean isVisible = true;
4732 if (mIsEditingText) {
4733 isVisible = mEditTextContentBounds.contains(selectionPoint.x,
4734 selectionPoint.y);
4735 }
4736 if (isVisible) {
4737 isVisible = nativeIsPointVisible(mNativeClass, layerId,
4738 selectionPoint.x, selectionPoint.y);
4739 }
4740 return isVisible;
4741 }
4742
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004743 /**
4744 * Takes an int[4] array as an output param with the values being
4745 * startX, startY, endX, endY
4746 */
4747 private void getSelectionHandles(int[] handles) {
George Mountb49f2bb2012-06-01 14:29:37 -07004748 handles[0] = mSelectCursorBase.x;
4749 handles[1] = mSelectCursorBase.y;
4750 handles[2] = mSelectCursorExtent.x;
4751 handles[3] = mSelectCursorExtent.y;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004752 }
4753
4754 // draw history
4755 private boolean mDrawHistory = false;
4756 private Picture mHistoryPicture = null;
4757 private int mHistoryWidth = 0;
4758 private int mHistoryHeight = 0;
4759
4760 // Only check the flag, can be called from WebCore thread
4761 boolean drawHistory() {
4762 return mDrawHistory;
4763 }
4764
4765 int getHistoryPictureWidth() {
4766 return (mHistoryPicture != null) ? mHistoryPicture.getWidth() : 0;
4767 }
4768
4769 // Should only be called in UI thread
4770 void switchOutDrawHistory() {
4771 if (null == mWebViewCore) return; // CallbackProxy may trigger this
4772 if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) {
4773 mDrawHistory = false;
4774 mHistoryPicture = null;
4775 invalidate();
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00004776 int oldScrollX = getScrollX();
4777 int oldScrollY = getScrollY();
4778 setScrollXRaw(pinLocX(getScrollX()));
4779 setScrollYRaw(pinLocY(getScrollY()));
4780 if (oldScrollX != getScrollX() || oldScrollY != getScrollY()) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00004781 mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldScrollX, oldScrollY);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004782 } else {
4783 sendOurVisibleRect();
4784 }
4785 }
4786 }
4787
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004788 /**
4789 * Delete text from start to end in the focused textfield. If there is no
4790 * focus, or if start == end, silently fail. If start and end are out of
4791 * order, swap them.
4792 * @param start Beginning of selection to delete.
4793 * @param end End of selection to delete.
4794 */
4795 /* package */ void deleteSelection(int start, int end) {
4796 mTextGeneration++;
4797 WebViewCore.TextSelectionData data
4798 = new WebViewCore.TextSelectionData(start, end, 0);
4799 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
4800 data);
4801 }
4802
4803 /**
4804 * Set the selection to (start, end) in the focused textfield. If start and
4805 * end are out of order, swap them.
4806 * @param start Beginning of selection.
4807 * @param end End of selection.
4808 */
4809 /* package */ void setSelection(int start, int end) {
4810 if (mWebViewCore != null) {
4811 mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
4812 }
4813 }
4814
4815 @Override
4816 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
4817 if (mInputConnection == null) {
4818 mInputConnection = new WebViewInputConnection();
George Mount57bd5172012-05-03 10:41:00 -07004819 mAutoCompletePopup = new AutoCompletePopup(this, mInputConnection);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004820 }
4821 mInputConnection.setupEditorInfo(outAttrs);
4822 return mInputConnection;
4823 }
4824
George Mountbcd5dd72012-03-01 08:39:03 -08004825 private void relocateAutoCompletePopup() {
4826 if (mAutoCompletePopup != null) {
4827 mAutoCompletePopup.resetRect();
4828 mAutoCompletePopup.setText(mInputConnection.getEditable());
4829 }
4830 }
4831
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004832 /**
4833 * Called in response to a message from webkit telling us that the soft
4834 * keyboard should be launched.
4835 */
4836 private void displaySoftKeyboard(boolean isTextView) {
4837 InputMethodManager imm = (InputMethodManager)
Jonathan Dixon3c909522012-02-28 18:45:06 +00004838 mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004839
4840 // bring it back to the default level scale so that user can enter text
4841 boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
4842 if (zoom) {
4843 mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
4844 mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false);
4845 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004846 // Used by plugins and contentEditable.
4847 // Also used if the navigation cache is out of date, and
4848 // does not recognize that a textfield is in focus. In that
4849 // case, use WebView as the targeted view.
4850 // see http://b/issue?id=2457459
Jonathan Dixon3c909522012-02-28 18:45:06 +00004851 imm.showSoftInput(mWebView, 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004852 }
4853
4854 // Called by WebKit to instruct the UI to hide the keyboard
4855 private void hideSoftKeyboard() {
4856 InputMethodManager imm = InputMethodManager.peekInstance();
John Reckb2676f72012-03-02 14:13:06 -08004857 if (imm != null && (imm.isActive(mWebView))) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00004858 imm.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004859 }
4860 }
4861
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004862 /**
George Mountbcd5dd72012-03-01 08:39:03 -08004863 * Called by AutoCompletePopup to find saved form data associated with the
4864 * textfield
4865 * @param name Name of the textfield.
4866 * @param nodePointer Pointer to the node of the textfield, so it can be
4867 * compared to the currently focused textfield when the data is
4868 * retrieved.
4869 * @param autoFillable true if WebKit has determined this field is part of
4870 * a form that can be auto filled.
4871 * @param autoComplete true if the attribute "autocomplete" is set to true
4872 * on the textfield.
4873 */
4874 /* package */ void requestFormData(String name, int nodePointer,
4875 boolean autoFillable, boolean autoComplete) {
4876 if (mWebViewCore.getSettings().getSaveFormData()) {
4877 Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
4878 update.arg1 = nodePointer;
4879 RequestFormData updater = new RequestFormData(name, getUrl(),
4880 update, autoFillable, autoComplete);
4881 Thread t = new Thread(updater);
4882 t.start();
4883 }
4884 }
4885
4886 /*
4887 * This class requests an Adapter for the AutoCompletePopup which shows past
4888 * entries stored in the database. It is a Runnable so that it can be done
4889 * in its own thread, without slowing down the UI.
4890 */
4891 private class RequestFormData implements Runnable {
4892 private String mName;
4893 private String mUrl;
4894 private Message mUpdateMessage;
4895 private boolean mAutoFillable;
4896 private boolean mAutoComplete;
4897 private WebSettingsClassic mWebSettings;
4898
4899 public RequestFormData(String name, String url, Message msg,
4900 boolean autoFillable, boolean autoComplete) {
4901 mName = name;
4902 mUrl = WebTextView.urlForAutoCompleteData(url);
4903 mUpdateMessage = msg;
4904 mAutoFillable = autoFillable;
4905 mAutoComplete = autoComplete;
4906 mWebSettings = getSettings();
4907 }
4908
4909 @Override
4910 public void run() {
4911 ArrayList<String> pastEntries = new ArrayList<String>();
4912
4913 if (mAutoFillable) {
4914 // Note that code inside the adapter click handler in AutoCompletePopup depends
4915 // on the AutoFill item being at the top of the drop down list. If you change
4916 // the order, make sure to do it there too!
4917 if (mWebSettings != null && mWebSettings.getAutoFillProfile() != null) {
4918 pastEntries.add(mWebView.getResources().getText(
4919 com.android.internal.R.string.autofill_this_form).toString() +
4920 " " +
4921 mAutoFillData.getPreviewString());
4922 mAutoCompletePopup.setIsAutoFillProfileSet(true);
4923 } else {
4924 // There is no autofill profile set up yet, so add an option that
4925 // will invite the user to set their profile up.
4926 pastEntries.add(mWebView.getResources().getText(
4927 com.android.internal.R.string.setup_autofill).toString());
4928 mAutoCompletePopup.setIsAutoFillProfileSet(false);
4929 }
4930 }
4931
4932 if (mAutoComplete) {
4933 pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
4934 }
4935
4936 if (pastEntries.size() > 0) {
4937 ArrayAdapter<String> adapter = new ArrayAdapter<String>(
4938 mContext,
4939 com.android.internal.R.layout.web_text_view_dropdown,
4940 pastEntries);
4941 mUpdateMessage.obj = adapter;
4942 mUpdateMessage.sendToTarget();
4943 }
4944 }
4945 }
4946
4947 /**
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004948 * Dump the display tree to "/sdcard/displayTree.txt"
4949 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00004950 * debug only
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004951 */
4952 public void dumpDisplayTree() {
4953 nativeDumpDisplayTree(getUrl());
4954 }
4955
4956 /**
4957 * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
4958 * "/sdcard/domTree.txt"
4959 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00004960 * debug only
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004961 */
4962 public void dumpDomTree(boolean toFile) {
4963 mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
4964 }
4965
4966 /**
4967 * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
4968 * to "/sdcard/renderTree.txt"
4969 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00004970 * debug only
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004971 */
4972 public void dumpRenderTree(boolean toFile) {
4973 mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
4974 }
4975
4976 /**
4977 * Called by DRT on UI thread, need to proxy to WebCore thread.
4978 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00004979 * debug only
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004980 */
Steve Block5ba2efe2011-08-03 13:57:49 +01004981 public void setUseMockDeviceOrientation() {
4982 mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_DEVICE_ORIENTATION);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08004983 }
4984
4985 /**
Steve Blockf3f60d92012-05-16 12:41:18 +01004986 * Sets use of the Geolocation mock client. Also resets that client. Called
4987 * by DRT on UI thread, need to proxy to WebCore thread.
4988 *
4989 * debug only
4990 */
4991 public void setUseMockGeolocation() {
4992 mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_GEOLOCATION);
4993 }
4994
4995 /**
4996 * Called by DRT on WebCore thread.
4997 *
4998 * debug only
4999 */
5000 public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
5001 mWebViewCore.setMockGeolocationPosition(latitude, longitude, accuracy);
5002 }
5003
5004 /**
5005 * Called by DRT on WebCore thread.
5006 *
5007 * debug only
5008 */
5009 public void setMockGeolocationError(int code, String message) {
5010 mWebViewCore.setMockGeolocationError(code, message);
5011 }
5012
5013 /**
5014 * Called by DRT on WebCore thread.
5015 *
5016 * debug only
5017 */
5018 public void setMockGeolocationPermission(boolean allow) {
5019 mWebViewCore.setMockGeolocationPermission(allow);
5020 }
5021
5022 /**
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005023 * Called by DRT on WebCore thread.
5024 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00005025 * debug only
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005026 */
5027 public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
5028 boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
5029 mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
5030 canProvideGamma, gamma);
5031 }
5032
5033 // This is used to determine long press with the center key. Does not
5034 // affect long press with the trackball/touch.
5035 private boolean mGotCenterDown = false;
5036
5037 @Override
5038 public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
5039 if (mBlockWebkitViewMessages) {
5040 return false;
5041 }
5042 // send complex characters to webkit for use by JS and plugins
5043 if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) {
5044 // pass the key to DOM
George Mountdbef1c52012-03-28 14:17:13 -07005045 sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event);
5046 sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005047 // return true as DOM handles the key
5048 return true;
5049 }
5050 return false;
5051 }
5052
5053 private boolean isEnterActionKey(int keyCode) {
5054 return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
5055 || keyCode == KeyEvent.KEYCODE_ENTER
5056 || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
5057 }
5058
George Mountbcd5dd72012-03-01 08:39:03 -08005059 public boolean onKeyPreIme(int keyCode, KeyEvent event) {
5060 if (mAutoCompletePopup != null) {
5061 return mAutoCompletePopup.onKeyPreIme(keyCode, event);
5062 }
5063 return false;
5064 }
5065
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005066 @Override
5067 public boolean onKeyDown(int keyCode, KeyEvent event) {
5068 if (DebugFlags.WEB_VIEW) {
5069 Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
5070 + "keyCode=" + keyCode
5071 + ", " + event + ", unicode=" + event.getUnicodeChar());
5072 }
5073 if (mIsCaretSelection) {
5074 selectionDone();
5075 }
5076 if (mBlockWebkitViewMessages) {
5077 return false;
5078 }
5079
5080 // don't implement accelerator keys here; defer to host application
5081 if (event.isCtrlPressed()) {
5082 return false;
5083 }
5084
5085 if (mNativeClass == 0) {
5086 return false;
5087 }
5088
5089 // do this hack up front, so it always works, regardless of touch-mode
5090 if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
5091 mAutoRedraw = !mAutoRedraw;
5092 if (mAutoRedraw) {
5093 invalidate();
5094 }
5095 return true;
5096 }
5097
5098 // Bubble up the key event if
5099 // 1. it is a system key; or
5100 // 2. the host application wants to handle it;
5101 if (event.isSystem()
5102 || mCallbackProxy.uiOverrideKeyEvent(event)) {
5103 return false;
5104 }
5105
alanv525823a2012-05-16 20:01:51 -07005106 // See if the accessibility injector needs to handle this event.
alanvfdfd0d82012-08-09 18:05:17 -07005107 if (isAccessibilityInjectionEnabled()
alanv525823a2012-05-16 20:01:51 -07005108 && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
5109 return true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005110 }
5111
5112 if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
5113 if (event.hasNoModifiers()) {
5114 pageUp(false);
5115 return true;
5116 } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
5117 pageUp(true);
5118 return true;
5119 }
5120 }
5121
5122 if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
5123 if (event.hasNoModifiers()) {
5124 pageDown(false);
5125 return true;
5126 } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
5127 pageDown(true);
5128 return true;
5129 }
5130 }
5131
5132 if (keyCode == KeyEvent.KEYCODE_MOVE_HOME && event.hasNoModifiers()) {
5133 pageUp(true);
5134 return true;
5135 }
5136
5137 if (keyCode == KeyEvent.KEYCODE_MOVE_END && event.hasNoModifiers()) {
5138 pageDown(true);
5139 return true;
5140 }
5141
5142 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
5143 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
5144 switchOutDrawHistory();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005145 }
5146
5147 if (isEnterActionKey(keyCode)) {
5148 switchOutDrawHistory();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005149 if (event.getRepeatCount() == 0) {
5150 if (mSelectingText) {
5151 return true; // discard press if copy in progress
5152 }
5153 mGotCenterDown = true;
5154 mPrivateHandler.sendMessageDelayed(mPrivateHandler
5155 .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005156 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005157 }
5158
5159 if (getSettings().getNavDump()) {
5160 switch (keyCode) {
5161 case KeyEvent.KEYCODE_4:
5162 dumpDisplayTree();
5163 break;
5164 case KeyEvent.KEYCODE_5:
5165 case KeyEvent.KEYCODE_6:
5166 dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
5167 break;
5168 case KeyEvent.KEYCODE_7:
5169 case KeyEvent.KEYCODE_8:
5170 dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
5171 break;
5172 }
5173 }
5174
John Reckb2676f72012-03-02 14:13:06 -08005175 // pass the key to DOM
John Reck4fa40372012-03-06 14:31:45 -08005176 sendKeyEvent(event);
John Reckb2676f72012-03-02 14:13:06 -08005177 // return true as DOM handles the key
5178 return true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005179 }
5180
5181 @Override
5182 public boolean onKeyUp(int keyCode, KeyEvent event) {
5183 if (DebugFlags.WEB_VIEW) {
5184 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
5185 + ", " + event + ", unicode=" + event.getUnicodeChar());
5186 }
5187 if (mBlockWebkitViewMessages) {
5188 return false;
5189 }
5190
5191 if (mNativeClass == 0) {
5192 return false;
5193 }
5194
5195 // special CALL handling when cursor node's href is "tel:XXX"
John Reckb2676f72012-03-02 14:13:06 -08005196 if (keyCode == KeyEvent.KEYCODE_CALL
5197 && mInitialHitTestResult != null
5198 && mInitialHitTestResult.getType() == HitTestResult.PHONE_TYPE) {
5199 String text = mInitialHitTestResult.getExtra();
5200 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
5201 mContext.startActivity(intent);
5202 return true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005203 }
5204
5205 // Bubble up the key event if
5206 // 1. it is a system key; or
5207 // 2. the host application wants to handle it;
5208 if (event.isSystem()
5209 || mCallbackProxy.uiOverrideKeyEvent(event)) {
5210 return false;
5211 }
5212
alanv525823a2012-05-16 20:01:51 -07005213 // See if the accessibility injector needs to handle this event.
alanvfdfd0d82012-08-09 18:05:17 -07005214 if (isAccessibilityInjectionEnabled()
alanv525823a2012-05-16 20:01:51 -07005215 && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
5216 return true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005217 }
5218
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005219 if (isEnterActionKey(keyCode)) {
5220 // remove the long press message first
5221 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
5222 mGotCenterDown = false;
5223
5224 if (mSelectingText) {
5225 copySelection();
5226 selectionDone();
5227 return true; // discard press if copy in progress
5228 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005229 }
5230
John Reckb2676f72012-03-02 14:13:06 -08005231 // pass the key to DOM
John Reck4fa40372012-03-06 14:31:45 -08005232 sendKeyEvent(event);
John Reckb2676f72012-03-02 14:13:06 -08005233 // return true as DOM handles the key
5234 return true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005235 }
5236
5237 private boolean startSelectActionMode() {
5238 mSelectCallback = new SelectActionModeCallback();
5239 mSelectCallback.setTextSelected(!mIsCaretSelection);
5240 mSelectCallback.setWebView(this);
Jonathan Dixon3c909522012-02-28 18:45:06 +00005241 if (mWebView.startActionMode(mSelectCallback) == null) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005242 // There is no ActionMode, so do not allow the user to modify a
5243 // selection.
5244 selectionDone();
5245 return false;
5246 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00005247 mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005248 return true;
5249 }
5250
5251 private void showPasteWindow() {
5252 ClipboardManager cm = (ClipboardManager)(mContext
5253 .getSystemService(Context.CLIPBOARD_SERVICE));
5254 if (cm.hasPrimaryClip()) {
George Mountb49f2bb2012-06-01 14:29:37 -07005255 Point cursorPoint = new Point(contentToViewX(mSelectCursorBase.x),
5256 contentToViewY(mSelectCursorBase.y));
John Reck9fcef3d2012-10-11 17:34:42 -07005257 Point cursorTop = calculateBaseCaretTop();
George Mountf9c1f992012-03-21 16:06:10 -07005258 cursorTop.set(contentToViewX(cursorTop.x),
5259 contentToViewY(cursorTop.y));
5260
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005261 int[] location = new int[2];
Jonathan Dixon3c909522012-02-28 18:45:06 +00005262 mWebView.getLocationInWindow(location);
George Mountf9c1f992012-03-21 16:06:10 -07005263 int offsetX = location[0] - getScrollX();
5264 int offsetY = location[1] - getScrollY();
5265 cursorPoint.offset(offsetX, offsetY);
5266 cursorTop.offset(offsetX, offsetY);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005267 if (mPasteWindow == null) {
5268 mPasteWindow = new PastePopupWindow();
5269 }
George Mountf9c1f992012-03-21 16:06:10 -07005270 mPasteWindow.show(cursorPoint, cursorTop, location[0], location[1]);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005271 }
5272 }
5273
George Mountf9c1f992012-03-21 16:06:10 -07005274 /**
5275 * Given segment AB, this finds the point C along AB that is closest to
5276 * point and then returns it scale along AB. The scale factor is AC/AB.
5277 *
5278 * @param x The x coordinate of the point near segment AB that determines
5279 * the scale factor.
5280 * @param y The y coordinate of the point near segment AB that determines
5281 * the scale factor.
5282 * @param a The first point of the line segment.
5283 * @param b The second point of the line segment.
5284 * @return The scale factor AC/AB, where C is the point on AB closest to
5285 * point.
5286 */
5287 private static float scaleAlongSegment(int x, int y, PointF a, PointF b) {
5288 // The bottom line of the text box is line AB
5289 float abX = b.x - a.x;
5290 float abY = b.y - a.y;
5291 float ab2 = (abX * abX) + (abY * abY);
5292
5293 // The line from first point in text bounds to bottom is AP
5294 float apX = x - a.x;
5295 float apY = y - a.y;
5296 float abDotAP = (apX * abX) + (apY * abY);
5297 float scale = abDotAP / ab2;
5298 return scale;
5299 }
5300
John Reck9fcef3d2012-10-11 17:34:42 -07005301 private Point calculateBaseCaretTop() {
5302 return calculateCaretTop(mSelectCursorBase, mSelectCursorBaseTextQuad);
5303 }
5304
5305 private Point calculateDraggingCaretTop() {
5306 return calculateCaretTop(mSelectDraggingCursor, mSelectDraggingTextQuad);
5307 }
5308
George Mountf9c1f992012-03-21 16:06:10 -07005309 /**
5310 * Assuming arbitrary shape of a quadralateral forming text bounds, this
5311 * calculates the top of a caret.
5312 */
John Reck9fcef3d2012-10-11 17:34:42 -07005313 private static Point calculateCaretTop(Point base, QuadF quad) {
5314 float scale = scaleAlongSegment(base.x, base.y, quad.p4, quad.p3);
5315 int x = Math.round(scaleCoordinate(scale, quad.p1.x, quad.p2.x));
5316 int y = Math.round(scaleCoordinate(scale, quad.p1.y, quad.p2.y));
George Mountf9c1f992012-03-21 16:06:10 -07005317 return new Point(x, y);
5318 }
5319
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005320 private void hidePasteButton() {
5321 if (mPasteWindow != null) {
5322 mPasteWindow.hide();
5323 }
5324 }
5325
5326 private void syncSelectionCursors() {
George Mountb49f2bb2012-06-01 14:29:37 -07005327 mSelectCursorBaseLayerId =
5328 nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE,
5329 mSelectCursorBase, mSelectCursorBaseTextQuad);
5330 mSelectCursorExtentLayerId =
5331 nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT,
5332 mSelectCursorExtent, mSelectCursorExtentTextQuad);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005333 }
5334
5335 private boolean setupWebkitSelect() {
5336 syncSelectionCursors();
George Mountf796fdf2012-03-19 14:51:55 -07005337 if (!mIsCaretSelection && !startSelectActionMode()) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005338 selectionDone();
5339 return false;
5340 }
George Mount30d773f2012-04-20 08:34:44 -07005341 startSelectingText();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005342 mTouchMode = TOUCH_DRAG_MODE;
5343 return true;
5344 }
5345
John Reck9fcef3d2012-10-11 17:34:42 -07005346 private void updateWebkitSelection(boolean isSnapped) {
George Mountf1c00f92012-06-04 12:26:27 -07005347 int handleId = (mSelectDraggingCursor == mSelectCursorBase)
5348 ? HANDLE_ID_BASE : HANDLE_ID_EXTENT;
John Reck9fcef3d2012-10-11 17:34:42 -07005349 int x = mSelectDraggingCursor.x;
5350 int y = mSelectDraggingCursor.y;
5351 if (isSnapped) {
5352 // "center" the cursor in the snapping quad
5353 Point top = calculateDraggingCaretTop();
5354 x = Math.round((top.x + x) / 2);
5355 y = Math.round((top.y + y) / 2);
5356 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005357 mWebViewCore.removeMessages(EventHub.SELECT_TEXT);
George Mountf1c00f92012-06-04 12:26:27 -07005358 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT,
John Reck9fcef3d2012-10-11 17:34:42 -07005359 x, y, (Integer)handleId);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005360 }
5361
5362 private void resetCaretTimer() {
5363 mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
5364 if (!mSelectionStarted) {
5365 mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE,
5366 CARET_HANDLE_STAMINA_MS);
5367 }
5368 }
5369
5370 /**
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005371 * Select all of the text in this WebView.
5372 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00005373 * This is an implementation detail.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005374 */
5375 public void selectAll() {
5376 mWebViewCore.sendMessage(EventHub.SELECT_ALL);
5377 }
5378
5379 /**
5380 * Called when the selection has been removed.
5381 */
5382 void selectionDone() {
5383 if (mSelectingText) {
5384 hidePasteButton();
George Mount30d773f2012-04-20 08:34:44 -07005385 endSelectingText();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005386 // finish is idempotent, so this is fine even if selectionDone was
5387 // called by mSelectCallback.onDestroyActionMode
5388 if (mSelectCallback != null) {
5389 mSelectCallback.finish();
5390 mSelectCallback = null;
5391 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005392 invalidate(); // redraw without selection
5393 mAutoScrollX = 0;
5394 mAutoScrollY = 0;
5395 mSentAutoScrollMessage = false;
5396 }
5397 }
5398
5399 /**
5400 * Copy the selection to the clipboard
5401 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00005402 * This is an implementation detail.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005403 */
5404 public boolean copySelection() {
5405 boolean copiedSomething = false;
5406 String selection = getSelection();
5407 if (selection != null && selection != "") {
5408 if (DebugFlags.WEB_VIEW) {
5409 Log.v(LOGTAG, "copySelection \"" + selection + "\"");
5410 }
5411 Toast.makeText(mContext
5412 , com.android.internal.R.string.text_copied
5413 , Toast.LENGTH_SHORT).show();
5414 copiedSomething = true;
Jonathan Dixon3c909522012-02-28 18:45:06 +00005415 ClipboardManager cm = (ClipboardManager)mContext
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005416 .getSystemService(Context.CLIPBOARD_SERVICE);
5417 cm.setText(selection);
5418 int[] handles = new int[4];
5419 getSelectionHandles(handles);
5420 mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles);
5421 }
5422 invalidate(); // remove selection region and pointer
5423 return copiedSomething;
5424 }
5425
5426 /**
5427 * Cut the selected text into the clipboard
5428 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00005429 * This is an implementation detail
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005430 */
5431 public void cutSelection() {
5432 copySelection();
5433 int[] handles = new int[4];
5434 getSelectionHandles(handles);
5435 mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
5436 }
5437
5438 /**
5439 * Paste text from the clipboard to the cursor position.
5440 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00005441 * This is an implementation detail
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005442 */
5443 public void pasteFromClipboard() {
Jonathan Dixon3c909522012-02-28 18:45:06 +00005444 ClipboardManager cm = (ClipboardManager)mContext
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005445 .getSystemService(Context.CLIPBOARD_SERVICE);
5446 ClipData clipData = cm.getPrimaryClip();
5447 if (clipData != null) {
5448 ClipData.Item clipItem = clipData.getItemAt(0);
Nils Holmströmdf337f32013-01-08 15:42:01 +01005449 CharSequence pasteText = clipItem.coerceToText(mContext);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005450 if (mInputConnection != null) {
5451 mInputConnection.replaceSelection(pasteText);
5452 }
5453 }
5454 }
5455
5456 /**
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005457 * Returns the currently highlighted text as a string.
5458 */
5459 String getSelection() {
5460 if (mNativeClass == 0) return "";
5461 return nativeGetSelection();
5462 }
5463
5464 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00005465 public void onAttachedToWindow() {
5466 if (mWebView.hasWindowFocus()) setActive(true);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005467
alanvfdfd0d82012-08-09 18:05:17 -07005468 if (isAccessibilityInjectionEnabled()) {
alanv03e636f2012-10-12 11:42:37 -07005469 getAccessibilityInjector().toggleAccessibilityFeedback(true);
alanv525823a2012-05-16 20:01:51 -07005470 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005471
John Recka5408e62012-03-16 14:18:44 -07005472 updateHwAccelerated();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005473 }
5474
5475 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00005476 public void onDetachedFromWindow() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005477 clearHelpers();
5478 mZoomManager.dismissZoomPicker();
Jonathan Dixon3c909522012-02-28 18:45:06 +00005479 if (mWebView.hasWindowFocus()) setActive(false);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005480
alanvfdfd0d82012-08-09 18:05:17 -07005481 if (isAccessibilityInjectionEnabled()) {
alanv03e636f2012-10-12 11:42:37 -07005482 getAccessibilityInjector().toggleAccessibilityFeedback(false);
alanv525823a2012-05-16 20:01:51 -07005483 }
5484
John Recka5408e62012-03-16 14:18:44 -07005485 updateHwAccelerated();
Romain Guyba6be8a2012-04-23 18:22:09 -07005486
John Reckd5e29372012-06-07 11:35:35 -07005487 ensureFunctorDetached();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005488 }
5489
5490 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00005491 public void onVisibilityChanged(View changedView, int visibility) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005492 // The zoomManager may be null if the webview is created from XML that
5493 // specifies the view's visibility param as not visible (see http://b/2794841)
5494 if (visibility != View.VISIBLE && mZoomManager != null) {
5495 mZoomManager.dismissZoomPicker();
5496 }
5497 updateDrawingState();
5498 }
5499
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005500 void setActive(boolean active) {
5501 if (active) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00005502 if (mWebView.hasFocus()) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005503 // If our window regained focus, and we have focus, then begin
5504 // drawing the cursor ring
John Reckb2676f72012-03-02 14:13:06 -08005505 mDrawCursorRing = true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005506 setFocusControllerActive(true);
5507 } else {
5508 mDrawCursorRing = false;
John Reckb2676f72012-03-02 14:13:06 -08005509 setFocusControllerActive(false);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005510 }
5511 } else {
5512 if (!mZoomManager.isZoomPickerVisible()) {
5513 /*
5514 * The external zoom controls come in their own window, so our
5515 * window loses focus. Our policy is to not draw the cursor ring
5516 * if our window is not focused, but this is an exception since
5517 * the user can still navigate the web page with the zoom
5518 * controls showing.
5519 */
5520 mDrawCursorRing = false;
5521 }
5522 mKeysPressed.clear();
5523 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
5524 mTouchMode = TOUCH_DONE_MODE;
5525 setFocusControllerActive(false);
5526 }
5527 invalidate();
5528 }
5529
5530 // To avoid drawing the cursor ring, and remove the TextView when our window
5531 // loses focus.
5532 @Override
5533 public void onWindowFocusChanged(boolean hasWindowFocus) {
5534 setActive(hasWindowFocus);
5535 if (hasWindowFocus) {
5536 JWebCoreJavaBridge.setActiveWebView(this);
5537 if (mPictureUpdatePausedForFocusChange) {
5538 WebViewCore.resumeUpdatePicture(mWebViewCore);
5539 mPictureUpdatePausedForFocusChange = false;
5540 }
5541 } else {
5542 JWebCoreJavaBridge.removeActiveWebView(this);
5543 final WebSettings settings = getSettings();
5544 if (settings != null && settings.enableSmoothTransition() &&
5545 mWebViewCore != null && !WebViewCore.isUpdatePicturePaused(mWebViewCore)) {
5546 WebViewCore.pauseUpdatePicture(mWebViewCore);
5547 mPictureUpdatePausedForFocusChange = true;
5548 }
5549 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005550 }
5551
5552 /*
5553 * Pass a message to WebCore Thread, telling the WebCore::Page's
5554 * FocusController to be "inactive" so that it will
5555 * not draw the blinking cursor. It gets set to "active" to draw the cursor
5556 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
5557 */
5558 /* package */ void setFocusControllerActive(boolean active) {
5559 if (mWebViewCore == null) return;
5560 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, active ? 1 : 0, 0);
5561 // Need to send this message after the document regains focus.
5562 if (active && mListBoxMessage != null) {
5563 mWebViewCore.sendMessage(mListBoxMessage);
5564 mListBoxMessage = null;
5565 }
5566 }
5567
5568 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00005569 public void onFocusChanged(boolean focused, int direction,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005570 Rect previouslyFocusedRect) {
5571 if (DebugFlags.WEB_VIEW) {
5572 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
5573 }
5574 if (focused) {
John Reckb2676f72012-03-02 14:13:06 -08005575 mDrawCursorRing = true;
5576 setFocusControllerActive(true);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005577 } else {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005578 mDrawCursorRing = false;
John Reckb2676f72012-03-02 14:13:06 -08005579 setFocusControllerActive(false);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005580 mKeysPressed.clear();
5581 }
John Reck413fab32012-03-07 11:02:15 -08005582 if (!mTouchHighlightRegion.isEmpty()) {
5583 mWebView.invalidate(mTouchHighlightRegion.getBounds());
5584 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005585 }
5586
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07005587 // updateRectsForGL() happens almost every draw call, in order to avoid creating
5588 // any object in this code path, we move the local variable out to be a private
5589 // final member, and we marked them as mTemp*.
5590 private final Point mTempVisibleRectOffset = new Point();
5591 private final Rect mTempVisibleRect = new Rect();
5592
5593 void updateRectsForGL() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005594 // Use the getGlobalVisibleRect() to get the intersection among the parents
5595 // visible == false means we're clipped - send a null rect down to indicate that
5596 // we should not draw
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07005597 boolean visible = mWebView.getGlobalVisibleRect(mTempVisibleRect, mTempVisibleRectOffset);
5598 mInvScreenRect.set(mTempVisibleRect);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005599 if (visible) {
5600 // Then need to invert the Y axis, just for GL
Jonathan Dixon3c909522012-02-28 18:45:06 +00005601 View rootView = mWebView.getRootView();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005602 int rootViewHeight = rootView.getHeight();
Teng-Hui Zhu508d7052012-05-03 14:31:55 -07005603 mScreenRect.set(mInvScreenRect);
5604 int savedWebViewBottom = mInvScreenRect.bottom;
5605 mInvScreenRect.bottom = rootViewHeight - mInvScreenRect.top - getVisibleTitleHeightImpl();
5606 mInvScreenRect.top = rootViewHeight - savedWebViewBottom;
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07005607 mIsWebViewVisible = true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005608 } else {
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07005609 mIsWebViewVisible = false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005610 }
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07005611
5612 mTempVisibleRect.offset(-mTempVisibleRectOffset.x, -mTempVisibleRectOffset.y);
5613 viewToContentVisibleRect(mVisibleContentRect, mTempVisibleRect);
5614
5615 nativeUpdateDrawGLFunction(mNativeClass, mIsWebViewVisible ? mInvScreenRect : null,
5616 mIsWebViewVisible ? mScreenRect : null,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005617 mVisibleContentRect, getScale());
5618 }
5619
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07005620 // Input : viewRect, rect in view/screen coordinate.
5621 // Output: contentRect, rect in content/document coordinate.
5622 private void viewToContentVisibleRect(RectF contentRect, Rect viewRect) {
5623 contentRect.left = viewToContentXf(viewRect.left) / mWebView.getScaleX();
5624 // viewToContentY will remove the total height of the title bar. Add
5625 // the visible height back in to account for the fact that if the title
5626 // bar is partially visible, the part of the visible rect which is
5627 // displaying our content is displaced by that amount.
5628 contentRect.top = viewToContentYf(viewRect.top + getVisibleTitleHeightImpl())
5629 / mWebView.getScaleY();
5630 contentRect.right = viewToContentXf(viewRect.right) / mWebView.getScaleX();
5631 contentRect.bottom = viewToContentYf(viewRect.bottom) / mWebView.getScaleY();
5632 }
5633
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005634 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00005635 public boolean setFrame(int left, int top, int right, int bottom) {
5636 boolean changed = mWebViewPrivate.super_setFrame(left, top, right, bottom);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005637 if (!changed && mHeightCanMeasure) {
5638 // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
5639 // in WebViewCore after we get the first layout. We do call
5640 // requestLayout() when we get contentSizeChanged(). But the View
5641 // system won't call onSizeChanged if the dimension is not changed.
5642 // In this case, we need to call sendViewSizeZoom() explicitly to
5643 // notify the WebKit about the new dimensions.
5644 sendViewSizeZoom(false);
5645 }
Teng-Hui Zhu658e9992012-05-07 16:39:13 -07005646 updateRectsForGL();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005647 return changed;
5648 }
5649
5650 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00005651 public void onSizeChanged(int w, int h, int ow, int oh) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005652 // adjust the max viewport width depending on the view dimensions. This
5653 // is to ensure the scaling is not going insane. So do not shrink it if
5654 // the view size is temporarily smaller, e.g. when soft keyboard is up.
5655 int newMaxViewportWidth = (int) (Math.max(w, h) / mZoomManager.getDefaultMinZoomScale());
5656 if (newMaxViewportWidth > sMaxViewportWidth) {
5657 sMaxViewportWidth = newMaxViewportWidth;
5658 }
5659
5660 mZoomManager.onSizeChanged(w, h, ow, oh);
5661
5662 if (mLoadedPicture != null && mDelaySetPicture == null) {
5663 // Size changes normally result in a new picture
5664 // Re-set the loaded picture to simulate that
5665 // However, do not update the base layer as that hasn't changed
5666 setNewPicture(mLoadedPicture, false);
5667 }
George Mount312cad62012-05-08 10:14:05 -07005668 if (mIsEditingText) {
5669 scrollEditIntoView();
5670 }
George Mountbcd5dd72012-03-01 08:39:03 -08005671 relocateAutoCompletePopup();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005672 }
5673
George Mount312cad62012-05-08 10:14:05 -07005674 /**
5675 * Scrolls the edit field into view using the minimum scrolling necessary.
5676 * If the edit field is too large to fit in the visible window, the caret
5677 * dimensions are used so that at least the caret is visible.
5678 * A buffer of EDIT_RECT_BUFFER in view pixels is used to offset the
5679 * edit rectangle to ensure a margin with the edge of the screen.
5680 */
5681 private void scrollEditIntoView() {
5682 Rect visibleRect = new Rect(viewToContentX(getScrollX()),
5683 viewToContentY(getScrollY()),
5684 viewToContentX(getScrollX() + getWidth()),
5685 viewToContentY(getScrollY() + getViewHeightWithTitle()));
5686 if (visibleRect.contains(mEditTextContentBounds)) {
5687 return; // no need to scroll
5688 }
5689 syncSelectionCursors();
George Mountf7a1a842012-05-21 13:00:14 -07005690 nativeFindMaxVisibleRect(mNativeClass, mEditTextLayerId, visibleRect);
George Mount312cad62012-05-08 10:14:05 -07005691 final int buffer = Math.max(1, viewToContentDimension(EDIT_RECT_BUFFER));
5692 Rect showRect = new Rect(
5693 Math.max(0, mEditTextContentBounds.left - buffer),
5694 Math.max(0, mEditTextContentBounds.top - buffer),
5695 mEditTextContentBounds.right + buffer,
5696 mEditTextContentBounds.bottom + buffer);
John Reck9fcef3d2012-10-11 17:34:42 -07005697 Point caretTop = calculateBaseCaretTop();
George Mount312cad62012-05-08 10:14:05 -07005698 if (visibleRect.width() < mEditTextContentBounds.width()) {
5699 // The whole edit won't fit in the width, so use the caret rect
George Mountb49f2bb2012-06-01 14:29:37 -07005700 if (mSelectCursorBase.x < caretTop.x) {
5701 showRect.left = Math.max(0, mSelectCursorBase.x - buffer);
George Mount312cad62012-05-08 10:14:05 -07005702 showRect.right = caretTop.x + buffer;
5703 } else {
5704 showRect.left = Math.max(0, caretTop.x - buffer);
George Mountb49f2bb2012-06-01 14:29:37 -07005705 showRect.right = mSelectCursorBase.x + buffer;
George Mount312cad62012-05-08 10:14:05 -07005706 }
5707 }
5708 if (visibleRect.height() < mEditTextContentBounds.height()) {
5709 // The whole edit won't fit in the height, so use the caret rect
George Mountb49f2bb2012-06-01 14:29:37 -07005710 if (mSelectCursorBase.y > caretTop.y) {
George Mount312cad62012-05-08 10:14:05 -07005711 showRect.top = Math.max(0, caretTop.y - buffer);
George Mountb49f2bb2012-06-01 14:29:37 -07005712 showRect.bottom = mSelectCursorBase.y + buffer;
George Mount312cad62012-05-08 10:14:05 -07005713 } else {
George Mountb49f2bb2012-06-01 14:29:37 -07005714 showRect.top = Math.max(0, mSelectCursorBase.y - buffer);
George Mount312cad62012-05-08 10:14:05 -07005715 showRect.bottom = caretTop.y + buffer;
5716 }
5717 }
5718
5719 if (visibleRect.contains(showRect)) {
5720 return; // no need to scroll
5721 }
5722
George Mountf7a1a842012-05-21 13:00:14 -07005723 int scrollX = viewToContentX(getScrollX());
George Mount312cad62012-05-08 10:14:05 -07005724 if (visibleRect.left > showRect.left) {
George Mountf7a1a842012-05-21 13:00:14 -07005725 // We are scrolled too far
5726 scrollX += showRect.left - visibleRect.left;
George Mount312cad62012-05-08 10:14:05 -07005727 } else if (visibleRect.right < showRect.right) {
George Mountf7a1a842012-05-21 13:00:14 -07005728 // We aren't scrolled enough to include the right
5729 scrollX += showRect.right - visibleRect.right;
George Mount312cad62012-05-08 10:14:05 -07005730 }
George Mountf7a1a842012-05-21 13:00:14 -07005731 int scrollY = viewToContentY(getScrollY());
George Mount312cad62012-05-08 10:14:05 -07005732 if (visibleRect.top > showRect.top) {
George Mountf7a1a842012-05-21 13:00:14 -07005733 scrollY += showRect.top - visibleRect.top;
George Mount312cad62012-05-08 10:14:05 -07005734 } else if (visibleRect.bottom < showRect.bottom) {
George Mountf7a1a842012-05-21 13:00:14 -07005735 scrollY += showRect.bottom - visibleRect.bottom;
George Mount312cad62012-05-08 10:14:05 -07005736 }
5737
5738 contentScrollTo(scrollX, scrollY, false);
5739 }
5740
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005741 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00005742 public void onScrollChanged(int l, int t, int oldl, int oldt) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005743 if (!mInOverScrollMode) {
5744 sendOurVisibleRect();
5745 // update WebKit if visible title bar height changed. The logic is same
5746 // as getVisibleTitleHeightImpl.
5747 int titleHeight = getTitleHeight();
5748 if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
5749 sendViewSizeZoom(false);
5750 }
5751 }
5752 }
5753
5754 @Override
5755 public boolean dispatchKeyEvent(KeyEvent event) {
5756 switch (event.getAction()) {
5757 case KeyEvent.ACTION_DOWN:
5758 mKeysPressed.add(Integer.valueOf(event.getKeyCode()));
5759 break;
5760 case KeyEvent.ACTION_MULTIPLE:
5761 // Always accept the action.
5762 break;
5763 case KeyEvent.ACTION_UP:
5764 int location = mKeysPressed.indexOf(Integer.valueOf(event.getKeyCode()));
5765 if (location == -1) {
5766 // We did not receive the key down for this key, so do not
5767 // handle the key up.
5768 return false;
5769 } else {
5770 // We did receive the key down. Handle the key up, and
5771 // remove it from our pressed keys.
5772 mKeysPressed.remove(location);
5773 }
5774 break;
5775 default:
5776 // Accept the action. This should not happen, unless a new
5777 // action is added to KeyEvent.
5778 break;
5779 }
John Reckb2676f72012-03-02 14:13:06 -08005780 return mWebViewPrivate.super_dispatchKeyEvent(event);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005781 }
kiwon72034952013-02-07 20:02:39 +09005782
5783 private static final int SNAP_BOUND = 16;
5784 private static int sChannelDistance = 16;
5785 private int mFirstTouchX = -1; // the first touched point
5786 private int mFirstTouchY = -1;
5787 private int mDistanceX = 0;
5788 private int mDistanceY = 0;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005789
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005790 private boolean inFullScreenMode() {
5791 return mFullScreenHolder != null;
5792 }
5793
5794 private void dismissFullScreenMode() {
5795 if (inFullScreenMode()) {
5796 mFullScreenHolder.hide();
5797 mFullScreenHolder = null;
5798 invalidate();
5799 }
5800 }
5801
5802 void onPinchToZoomAnimationStart() {
5803 // cancel the single touch handling
5804 cancelTouch();
5805 onZoomAnimationStart();
5806 }
5807
5808 void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) {
5809 onZoomAnimationEnd();
5810 // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as
5811 // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE
5812 // as it may trigger the unwanted fling.
5813 mTouchMode = TOUCH_PINCH_DRAG;
5814 mConfirmMove = true;
5815 startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
5816 }
5817
5818 // See if there is a layer at x, y and switch to TOUCH_DRAG_LAYER_MODE if a
5819 // layer is found.
5820 private void startScrollingLayer(float x, float y) {
Chris Craik5f3c08a2012-05-01 15:07:39 -07005821 if (mNativeClass == 0)
5822 return;
5823
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00005824 int contentX = viewToContentX((int) x + getScrollX());
5825 int contentY = viewToContentY((int) y + getScrollY());
Chris Craik5f3c08a2012-05-01 15:07:39 -07005826 mCurrentScrollingLayerId = nativeScrollableLayer(mNativeClass,
5827 contentX, contentY, mScrollingLayerRect, mScrollingLayerBounds);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005828 if (mCurrentScrollingLayerId != 0) {
5829 mTouchMode = TOUCH_DRAG_LAYER_MODE;
5830 }
5831 }
5832
5833 // 1/(density * density) used to compute the distance between points.
5834 // Computed in init().
5835 private float DRAG_LAYER_INVERSE_DENSITY_SQUARED;
5836
5837 // The distance between two points reported in onTouchEvent scaled by the
5838 // density of the screen.
5839 private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
5840
5841 @Override
5842 public boolean onHoverEvent(MotionEvent event) {
5843 if (mNativeClass == 0) {
5844 return false;
5845 }
John Reckb2676f72012-03-02 14:13:06 -08005846 int x = viewToContentX((int) event.getX() + getScrollX());
5847 int y = viewToContentY((int) event.getY() + getScrollY());
5848 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, x, y);
alanveebebc92012-06-15 18:59:11 -07005849 mWebViewPrivate.super_onHoverEvent(event);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005850 return true;
5851 }
5852
5853 @Override
5854 public boolean onTouchEvent(MotionEvent ev) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00005855 if (mNativeClass == 0 || (!mWebView.isClickable() && !mWebView.isLongClickable())) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005856 return false;
5857 }
5858
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07005859 if (mInputDispatcher == null) {
5860 return false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005861 }
5862
John Reck435e6f52012-05-16 14:56:20 -07005863 if (mWebView.isFocusable() && mWebView.isFocusableInTouchMode()
5864 && !mWebView.isFocused()) {
5865 mWebView.requestFocus();
5866 }
5867
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07005868 if (mInputDispatcher.postPointerEvent(ev, getScrollX(),
5869 getScrollY() - getTitleHeight(), mZoomManager.getInvScale())) {
John Recke666b102012-05-04 15:30:42 -07005870 mInputDispatcher.dispatchUiEvents();
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07005871 return true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005872 } else {
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07005873 Log.w(LOGTAG, "mInputDispatcher rejected the event!");
5874 return false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005875 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005876 }
5877
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005878 /*
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07005879 * Common code for single touch and multi-touch.
5880 * (x, y) denotes current focus point, which is the touch point for single touch
5881 * and the middle point for multi-touch.
5882 */
5883 private void handleTouchEventCommon(MotionEvent event, int action, int x, int y) {
Adam Powell1027ed22012-08-31 17:19:24 -07005884 ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07005885
5886 long eventTime = event.getEventTime();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005887
5888 // Due to the touch screen edge effect, a touch closer to the edge
5889 // always snapped to the edge. As getViewWidth() can be different from
5890 // getWidth() due to the scrollbar, adjusting the point to match
5891 // getViewWidth(). Same applied to the height.
5892 x = Math.min(x, getViewWidth() - 1);
5893 y = Math.min(y, getViewHeightWithTitle() - 1);
5894
5895 int deltaX = mLastTouchX - x;
5896 int deltaY = mLastTouchY - y;
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00005897 int contentX = viewToContentX(x + getScrollX());
5898 int contentY = viewToContentY(y + getScrollY());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005899
5900 switch (action) {
5901 case MotionEvent.ACTION_DOWN: {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005902 mConfirmMove = false;
kiwon72034952013-02-07 20:02:39 +09005903
5904 // Channel Scrolling
5905 mFirstTouchX = x;
5906 mFirstTouchY = y;
5907 mDistanceX = mDistanceY = 0;
5908
George Mountf70276a2012-03-12 14:22:10 -07005909 if (!mEditTextScroller.isFinished()) {
5910 mEditTextScroller.abortAnimation();
5911 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005912 if (!mScroller.isFinished()) {
5913 // stop the current scroll animation, but if this is
5914 // the start of a fling, allow it to add to the current
5915 // fling's velocity
5916 mScroller.abortAnimation();
5917 mTouchMode = TOUCH_DRAG_START_MODE;
5918 mConfirmMove = true;
5919 nativeSetIsScrolling(false);
5920 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
5921 mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
John Reckb2676f72012-03-02 14:13:06 -08005922 removeTouchHighlight();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005923 if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
5924 mTouchMode = TOUCH_DOUBLE_TAP_MODE;
5925 } else {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005926 mTouchMode = TOUCH_INIT_MODE;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005927 }
5928 } else { // the normal case
5929 mTouchMode = TOUCH_INIT_MODE;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005930 if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
5931 EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
5932 (eventTime - mLastTouchUpTime), eventTime);
5933 }
5934 mSelectionStarted = false;
5935 if (mSelectingText) {
George Mountf9c1f992012-03-21 16:06:10 -07005936 ensureSelectionHandles();
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00005937 int shiftedY = y - getTitleHeight() + getScrollY();
5938 int shiftedX = x + getScrollX();
George Mountb49f2bb2012-06-01 14:29:37 -07005939 if (mSelectHandleBaseBounds.contains(shiftedX, shiftedY)) {
5940 mSelectionStarted = true;
5941 mSelectDraggingCursor = mSelectCursorBase;
5942 mSelectDraggingTextQuad = mSelectCursorBaseTextQuad;
5943 if (mIsCaretSelection) {
5944 mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
5945 hidePasteButton();
5946 }
5947 } else if (mSelectHandleExtentBounds
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005948 .contains(shiftedX, shiftedY)) {
5949 mSelectionStarted = true;
George Mountb49f2bb2012-06-01 14:29:37 -07005950 mSelectDraggingCursor = mSelectCursorExtent;
5951 mSelectDraggingTextQuad = mSelectCursorExtentTextQuad;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005952 } else if (mIsCaretSelection) {
5953 selectionDone();
5954 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005955 if (DebugFlags.WEB_VIEW) {
5956 Log.v(LOGTAG, "select=" + contentX + "," + contentY);
5957 }
5958 }
5959 }
5960 // Trigger the link
5961 if (!mSelectingText && (mTouchMode == TOUCH_INIT_MODE
5962 || mTouchMode == TOUCH_DOUBLE_TAP_MODE)) {
5963 mPrivateHandler.sendEmptyMessageDelayed(
5964 SWITCH_TO_SHORTPRESS, TAP_TIMEOUT);
5965 mPrivateHandler.sendEmptyMessageDelayed(
5966 SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005967 }
5968 startTouch(x, y, eventTime);
George Mountf70276a2012-03-12 14:22:10 -07005969 if (mIsEditingText) {
George Mount7102eb22012-04-10 13:41:51 -07005970 mTouchInEditText = mEditTextContentBounds
5971 .contains(contentX, contentY);
George Mountf70276a2012-03-12 14:22:10 -07005972 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005973 break;
5974 }
5975 case MotionEvent.ACTION_MOVE: {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005976 if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY)
5977 >= mTouchSlopSquare) {
5978 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
5979 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
5980 mConfirmMove = true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005981 if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
5982 mTouchMode = TOUCH_INIT_MODE;
5983 }
John Reckb2676f72012-03-02 14:13:06 -08005984 removeTouchHighlight();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005985 }
5986 if (mSelectingText && mSelectionStarted) {
5987 if (DebugFlags.WEB_VIEW) {
5988 Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
5989 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00005990 ViewParent parent = mWebView.getParent();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08005991 if (parent != null) {
5992 parent.requestDisallowInterceptTouchEvent(true);
5993 }
5994 if (deltaX != 0 || deltaY != 0) {
George Mount7102eb22012-04-10 13:41:51 -07005995 int handleX = contentX +
George Mountb49f2bb2012-06-01 14:29:37 -07005996 viewToContentDimension(mSelectOffset.x);
George Mount7102eb22012-04-10 13:41:51 -07005997 int handleY = contentY +
George Mountb49f2bb2012-06-01 14:29:37 -07005998 viewToContentDimension(mSelectOffset.y);
George Mount7102eb22012-04-10 13:41:51 -07005999 mSelectDraggingCursor.set(handleX, handleY);
6000 boolean inCursorText =
6001 mSelectDraggingTextQuad.containsPoint(handleX, handleY);
6002 boolean inEditBounds = mEditTextContentBounds
6003 .contains(handleX, handleY);
George Mount557748d2012-04-04 13:56:49 -07006004 if (mIsEditingText && !inEditBounds) {
6005 beginScrollEdit();
6006 } else {
6007 endScrollEdit();
6008 }
John Reck9fcef3d2012-10-11 17:34:42 -07006009 boolean snapped = false;
George Mount7102eb22012-04-10 13:41:51 -07006010 if (inCursorText || (mIsEditingText && !inEditBounds)) {
6011 snapDraggingCursor();
John Reck9fcef3d2012-10-11 17:34:42 -07006012 snapped = true;
George Mount7102eb22012-04-10 13:41:51 -07006013 }
John Reck9fcef3d2012-10-11 17:34:42 -07006014 updateWebkitSelection(snapped);
George Mount7102eb22012-04-10 13:41:51 -07006015 if (!inCursorText && mIsEditingText && inEditBounds) {
6016 // Visually snap even if we have moved the handle.
6017 snapDraggingCursor();
6018 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006019 mLastTouchX = x;
6020 mLastTouchY = y;
6021 invalidate();
6022 }
6023 break;
6024 }
6025
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07006026 if (mTouchMode == TOUCH_DONE_MODE) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006027 // no dragging during scroll zoom animation, or when prevent
6028 // default is yes
6029 break;
6030 }
6031 if (mVelocityTracker == null) {
6032 Log.e(LOGTAG, "Got null mVelocityTracker when "
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006033 + " mTouchMode = " + mTouchMode);
6034 } else {
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07006035 mVelocityTracker.addMovement(event);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006036 }
6037
6038 if (mTouchMode != TOUCH_DRAG_MODE &&
George Mountfcff68f2012-03-20 10:21:57 -07006039 mTouchMode != TOUCH_DRAG_LAYER_MODE &&
6040 mTouchMode != TOUCH_DRAG_TEXT_MODE) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006041
6042 if (!mConfirmMove) {
6043 break;
6044 }
6045
kiwon72034952013-02-07 20:02:39 +09006046 if ((detector == null || !detector.isInProgress())
6047 && SNAP_NONE == mSnapScrollMode) {
6048 int ax = Math.abs(x - mFirstTouchX);
6049 int ay = Math.abs(y - mFirstTouchY);
6050 if (ax < SNAP_BOUND && ay < SNAP_BOUND) {
6051 break;
6052 } else if (ax < SNAP_BOUND) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006053 mSnapScrollMode = SNAP_Y;
kiwon72034952013-02-07 20:02:39 +09006054 } else if (ay < SNAP_BOUND) {
6055 mSnapScrollMode = SNAP_X;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006056 }
6057 }
6058
6059 mTouchMode = TOUCH_DRAG_MODE;
6060 mLastTouchX = x;
6061 mLastTouchY = y;
6062 deltaX = 0;
6063 deltaY = 0;
6064
6065 startScrollingLayer(x, y);
6066 startDrag();
6067 }
6068
6069 // do pan
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006070 boolean keepScrollBarsVisible = false;
6071 if (deltaX == 0 && deltaY == 0) {
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07006072 keepScrollBarsVisible = true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006073 } else {
kiwon72034952013-02-07 20:02:39 +09006074 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
6075 mDistanceX += Math.abs(deltaX);
6076 mDistanceY += Math.abs(deltaY);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006077 if (mSnapScrollMode == SNAP_X) {
kiwon72034952013-02-07 20:02:39 +09006078 if (mDistanceY > sChannelDistance) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006079 mSnapScrollMode = SNAP_NONE;
kiwon72034952013-02-07 20:02:39 +09006080 } else if (mDistanceX > sChannelDistance) {
6081 mDistanceX = mDistanceY = 0;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006082 }
6083 } else {
kiwon72034952013-02-07 20:02:39 +09006084 if (mDistanceX > sChannelDistance) {
6085 mSnapScrollMode = SNAP_NONE;
6086 } else if (mDistanceY > sChannelDistance) {
6087 mDistanceX = mDistanceY = 0;
6088 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006089 }
6090 }
6091 if (mSnapScrollMode != SNAP_NONE) {
6092 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
6093 deltaY = 0;
6094 } else {
6095 deltaX = 0;
6096 }
6097 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006098 if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) {
6099 mHeldMotionless = MOTIONLESS_FALSE;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006100 } else {
6101 mHeldMotionless = MOTIONLESS_TRUE;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006102 keepScrollBarsVisible = true;
6103 }
6104
6105 mLastTouchTime = eventTime;
George Mountfcff68f2012-03-20 10:21:57 -07006106 boolean allDrag = doDrag(deltaX, deltaY);
6107 if (allDrag) {
6108 mLastTouchX = x;
6109 mLastTouchY = y;
6110 } else {
6111 int contentDeltaX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
6112 int roundedDeltaX = contentToViewDimension(contentDeltaX);
6113 int contentDeltaY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
6114 int roundedDeltaY = contentToViewDimension(contentDeltaY);
6115 mLastTouchX -= roundedDeltaX;
6116 mLastTouchY -= roundedDeltaY;
6117 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006118 }
6119
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006120 break;
6121 }
6122 case MotionEvent.ACTION_UP: {
kiwon72034952013-02-07 20:02:39 +09006123 mFirstTouchX = mFirstTouchY = -1;
George Mount1461a7c2012-06-07 10:41:41 -07006124 if (mIsEditingText && mSelectionStarted) {
6125 endScrollEdit();
6126 mPrivateHandler.sendEmptyMessageDelayed(SCROLL_HANDLE_INTO_VIEW,
6127 TEXT_SCROLL_FIRST_SCROLL_MS);
6128 if (!mConfirmMove && mIsCaretSelection) {
6129 showPasteWindow();
6130 stopTouch();
6131 break;
6132 }
George Mountf796fdf2012-03-19 14:51:55 -07006133 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006134 mLastTouchUpTime = eventTime;
6135 if (mSentAutoScrollMessage) {
6136 mAutoScrollX = mAutoScrollY = 0;
6137 }
6138 switch (mTouchMode) {
6139 case TOUCH_DOUBLE_TAP_MODE: // double tap
6140 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
6141 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07006142 mTouchMode = TOUCH_DONE_MODE;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006143 break;
6144 case TOUCH_INIT_MODE: // tap
6145 case TOUCH_SHORTPRESS_START_MODE:
6146 case TOUCH_SHORTPRESS_MODE:
6147 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
6148 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07006149 if (!mConfirmMove) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006150 if (mSelectingText) {
6151 // tapping on selection or controls does nothing
6152 if (!mSelectionStarted) {
6153 selectionDone();
6154 }
6155 break;
6156 }
6157 // only trigger double tap if the WebView is
6158 // scalable
6159 if (mTouchMode == TOUCH_INIT_MODE
6160 && (canZoomIn() || canZoomOut())) {
6161 mPrivateHandler.sendEmptyMessageDelayed(
6162 RELEASE_SINGLE_TAP, ViewConfiguration
6163 .getDoubleTapTimeout());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006164 }
6165 break;
6166 }
6167 case TOUCH_DRAG_MODE:
6168 case TOUCH_DRAG_LAYER_MODE:
George Mountfcff68f2012-03-20 10:21:57 -07006169 case TOUCH_DRAG_TEXT_MODE:
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006170 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006171 // if the user waits a while w/o moving before the
6172 // up, we don't want to do a fling
6173 if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
6174 if (mVelocityTracker == null) {
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07006175 Log.e(LOGTAG, "Got null mVelocityTracker");
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006176 } else {
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07006177 mVelocityTracker.addMovement(event);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006178 }
6179 // set to MOTIONLESS_IGNORE so that it won't keep
6180 // removing and sending message in
6181 // drawCoreAndCursorRing()
6182 mHeldMotionless = MOTIONLESS_IGNORE;
6183 doFling();
6184 break;
6185 } else {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006186 if (mScroller.springBack(getScrollX(), getScrollY(), 0,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006187 computeMaxScrollX(), 0,
6188 computeMaxScrollY())) {
6189 invalidate();
6190 }
6191 }
6192 // redraw in high-quality, as we're done dragging
6193 mHeldMotionless = MOTIONLESS_TRUE;
6194 invalidate();
6195 // fall through
6196 case TOUCH_DRAG_START_MODE:
6197 // TOUCH_DRAG_START_MODE should not happen for the real
6198 // device as we almost certain will get a MOVE. But this
6199 // is possible on emulator.
6200 mLastVelocity = 0;
6201 WebViewCore.resumePriority();
6202 if (!mSelectingText) {
6203 WebViewCore.resumeUpdatePicture(mWebViewCore);
6204 }
6205 break;
6206 }
6207 stopTouch();
6208 break;
6209 }
6210 case MotionEvent.ACTION_CANCEL: {
6211 if (mTouchMode == TOUCH_DRAG_MODE) {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006212 mScroller.springBack(getScrollX(), getScrollY(), 0,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006213 computeMaxScrollX(), 0, computeMaxScrollY());
6214 invalidate();
6215 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006216 cancelTouch();
6217 break;
6218 }
6219 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006220 }
6221
George Mount557748d2012-04-04 13:56:49 -07006222 /**
6223 * Returns the text scroll speed in content pixels per millisecond based on
6224 * the touch location.
6225 * @param coordinate The x or y touch coordinate in content space
6226 * @param min The minimum coordinate (x or y) of the edit content bounds
6227 * @param max The maximum coordinate (x or y) of the edit content bounds
6228 */
6229 private static float getTextScrollSpeed(int coordinate, int min, int max) {
6230 if (coordinate < min) {
6231 return (coordinate - min) * TEXT_SCROLL_RATE;
6232 } else if (coordinate >= max) {
6233 return (coordinate - max + 1) * TEXT_SCROLL_RATE;
6234 } else {
6235 return 0.0f;
6236 }
6237 }
6238
George Mountf1c00f92012-06-04 12:26:27 -07006239 private static int getSelectionCoordinate(int coordinate, int min, int max) {
6240 return Math.max(Math.min(coordinate, max), min);
6241 }
6242
George Mount557748d2012-04-04 13:56:49 -07006243 private void beginScrollEdit() {
6244 if (mLastEditScroll == 0) {
6245 mLastEditScroll = SystemClock.uptimeMillis() -
6246 TEXT_SCROLL_FIRST_SCROLL_MS;
6247 scrollEditWithCursor();
6248 }
6249 }
6250
George Mount1461a7c2012-06-07 10:41:41 -07006251 private void scrollDraggedSelectionHandleIntoView() {
6252 if (mSelectDraggingCursor == null) {
6253 return;
6254 }
6255 int x = mSelectDraggingCursor.x;
6256 int y = mSelectDraggingCursor.y;
6257 if (!mEditTextContentBounds.contains(x,y)) {
6258 int left = Math.min(0, x - mEditTextContentBounds.left - EDIT_RECT_BUFFER);
6259 int right = Math.max(0, x - mEditTextContentBounds.right + EDIT_RECT_BUFFER);
6260 int deltaX = left + right;
6261 int above = Math.min(0, y - mEditTextContentBounds.top - EDIT_RECT_BUFFER);
6262 int below = Math.max(0, y - mEditTextContentBounds.bottom + EDIT_RECT_BUFFER);
6263 int deltaY = above + below;
6264 if (deltaX != 0 || deltaY != 0) {
6265 int scrollX = getTextScrollX() + deltaX;
6266 int scrollY = getTextScrollY() + deltaY;
6267 scrollX = clampBetween(scrollX, 0, getMaxTextScrollX());
6268 scrollY = clampBetween(scrollY, 0, getMaxTextScrollY());
6269 scrollEditText(scrollX, scrollY);
6270 }
6271 }
6272 }
6273
George Mount557748d2012-04-04 13:56:49 -07006274 private void endScrollEdit() {
6275 mLastEditScroll = 0;
6276 }
6277
George Mount1461a7c2012-06-07 10:41:41 -07006278 private static int clampBetween(int value, int min, int max) {
6279 return Math.max(min, Math.min(value, max));
6280 }
6281
George Mount557748d2012-04-04 13:56:49 -07006282 private static int getTextScrollDelta(float speed, long deltaT) {
6283 float distance = speed * deltaT;
6284 int intDistance = (int)Math.floor(distance);
6285 float probability = distance - intDistance;
6286 if (Math.random() < probability) {
6287 intDistance++;
6288 }
6289 return intDistance;
6290 }
6291 /**
6292 * Scrolls edit text a distance based on the last touch point,
6293 * the last scroll time, and the edit text content bounds.
6294 */
6295 private void scrollEditWithCursor() {
6296 if (mLastEditScroll != 0) {
George Mountb49f2bb2012-06-01 14:29:37 -07006297 int x = viewToContentX(mLastTouchX + getScrollX() + mSelectOffset.x);
George Mount557748d2012-04-04 13:56:49 -07006298 float scrollSpeedX = getTextScrollSpeed(x, mEditTextContentBounds.left,
6299 mEditTextContentBounds.right);
George Mountb49f2bb2012-06-01 14:29:37 -07006300 int y = viewToContentY(mLastTouchY + getScrollY() + mSelectOffset.y);
George Mount557748d2012-04-04 13:56:49 -07006301 float scrollSpeedY = getTextScrollSpeed(y, mEditTextContentBounds.top,
6302 mEditTextContentBounds.bottom);
6303 if (scrollSpeedX == 0.0f && scrollSpeedY == 0.0f) {
6304 endScrollEdit();
6305 } else {
6306 long currentTime = SystemClock.uptimeMillis();
6307 long timeSinceLastUpdate = currentTime - mLastEditScroll;
6308 int deltaX = getTextScrollDelta(scrollSpeedX, timeSinceLastUpdate);
6309 int deltaY = getTextScrollDelta(scrollSpeedY, timeSinceLastUpdate);
George Mountc6c83ce2012-06-05 14:22:22 -07006310 int scrollX = getTextScrollX() + deltaX;
George Mount1461a7c2012-06-07 10:41:41 -07006311 scrollX = clampBetween(scrollX, 0, getMaxTextScrollX());
George Mountc6c83ce2012-06-05 14:22:22 -07006312 int scrollY = getTextScrollY() + deltaY;
George Mount1461a7c2012-06-07 10:41:41 -07006313 scrollY = clampBetween(scrollY, 0, getMaxTextScrollY());
George Mountc6c83ce2012-06-05 14:22:22 -07006314
George Mount557748d2012-04-04 13:56:49 -07006315 mLastEditScroll = currentTime;
George Mountc6c83ce2012-06-05 14:22:22 -07006316 if (scrollX == getTextScrollX() && scrollY == getTextScrollY()) {
George Mount557748d2012-04-04 13:56:49 -07006317 // By probability no text scroll this time. Try again later.
6318 mPrivateHandler.sendEmptyMessageDelayed(SCROLL_EDIT_TEXT,
6319 TEXT_SCROLL_FIRST_SCROLL_MS);
6320 } else {
George Mountf1c00f92012-06-04 12:26:27 -07006321 int selectionX = getSelectionCoordinate(x,
6322 mEditTextContentBounds.left, mEditTextContentBounds.right);
6323 int selectionY = getSelectionCoordinate(y,
6324 mEditTextContentBounds.top, mEditTextContentBounds.bottom);
6325 int oldX = mSelectDraggingCursor.x;
6326 int oldY = mSelectDraggingCursor.y;
6327 mSelectDraggingCursor.set(selectionX, selectionY);
John Reck9fcef3d2012-10-11 17:34:42 -07006328 updateWebkitSelection(false);
George Mount1461a7c2012-06-07 10:41:41 -07006329 scrollEditText(scrollX, scrollY);
George Mountf1c00f92012-06-04 12:26:27 -07006330 mSelectDraggingCursor.set(oldX, oldY);
George Mount557748d2012-04-04 13:56:49 -07006331 }
6332 }
6333 }
6334 }
6335
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006336 private void startTouch(float x, float y, long eventTime) {
6337 // Remember where the motion event started
6338 mStartTouchX = mLastTouchX = Math.round(x);
6339 mStartTouchY = mLastTouchY = Math.round(y);
6340 mLastTouchTime = eventTime;
6341 mVelocityTracker = VelocityTracker.obtain();
6342 mSnapScrollMode = SNAP_NONE;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006343 }
6344
6345 private void startDrag() {
6346 WebViewCore.reducePriority();
6347 // to get better performance, pause updating the picture
6348 WebViewCore.pauseUpdatePicture(mWebViewCore);
6349 nativeSetIsScrolling(true);
6350
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006351 if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
6352 || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) {
6353 mZoomManager.invokeZoomPicker();
6354 }
6355 }
6356
George Mountfcff68f2012-03-20 10:21:57 -07006357 private boolean doDrag(int deltaX, int deltaY) {
6358 boolean allDrag = true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006359 if ((deltaX | deltaY) != 0) {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006360 int oldX = getScrollX();
6361 int oldY = getScrollY();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006362 int rangeX = computeMaxScrollX();
6363 int rangeY = computeMaxScrollY();
George Mountfcff68f2012-03-20 10:21:57 -07006364 final int contentX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
6365 final int contentY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006366
George Mountfcff68f2012-03-20 10:21:57 -07006367 // Assume page scrolling and change below if we're wrong
6368 mTouchMode = TOUCH_DRAG_MODE;
6369
6370 // Check special scrolling before going to main page scrolling.
6371 if (mIsEditingText && mTouchInEditText && canTextScroll(deltaX, deltaY)) {
6372 // Edit text scrolling
6373 oldX = getTextScrollX();
6374 rangeX = getMaxTextScrollX();
6375 deltaX = contentX;
6376 oldY = getTextScrollY();
6377 rangeY = getMaxTextScrollY();
6378 deltaY = contentY;
6379 mTouchMode = TOUCH_DRAG_TEXT_MODE;
6380 allDrag = false;
6381 } else if (mCurrentScrollingLayerId != 0) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006382 // Check the scrolling bounds to see if we will actually do any
6383 // scrolling. The rectangle is in document coordinates.
6384 final int maxX = mScrollingLayerRect.right;
6385 final int maxY = mScrollingLayerRect.bottom;
George Mount1461a7c2012-06-07 10:41:41 -07006386 final int resultX = clampBetween(maxX, 0,
6387 mScrollingLayerRect.left + contentX);
6388 final int resultY = clampBetween(maxY, 0,
6389 mScrollingLayerRect.top + contentY);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006390
John Reck919c89e2012-06-07 14:45:34 -07006391 if (resultX != mScrollingLayerRect.left
6392 || resultY != mScrollingLayerRect.top
6393 || (contentX | contentY) == 0) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006394 // In case we switched to dragging the page.
6395 mTouchMode = TOUCH_DRAG_LAYER_MODE;
6396 deltaX = contentX;
6397 deltaY = contentY;
6398 oldX = mScrollingLayerRect.left;
6399 oldY = mScrollingLayerRect.top;
6400 rangeX = maxX;
6401 rangeY = maxY;
George Mountfcff68f2012-03-20 10:21:57 -07006402 allDrag = false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006403 }
6404 }
6405
6406 if (mOverScrollGlow != null) {
6407 mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
6408 }
6409
Jonathan Dixon3c909522012-02-28 18:45:06 +00006410 mWebViewPrivate.overScrollBy(deltaX, deltaY, oldX, oldY,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006411 rangeX, rangeY,
6412 mOverscrollDistance, mOverscrollDistance, true);
6413 if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
6414 invalidate();
6415 }
6416 }
6417 mZoomManager.keepZoomPickerVisible();
George Mountfcff68f2012-03-20 10:21:57 -07006418 return allDrag;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006419 }
6420
6421 private void stopTouch() {
6422 if (mScroller.isFinished() && !mSelectingText
George Mountfcff68f2012-03-20 10:21:57 -07006423 && (mTouchMode == TOUCH_DRAG_MODE
6424 || mTouchMode == TOUCH_DRAG_LAYER_MODE)) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006425 WebViewCore.resumePriority();
6426 WebViewCore.resumeUpdatePicture(mWebViewCore);
6427 nativeSetIsScrolling(false);
6428 }
6429
6430 // we also use mVelocityTracker == null to tell us that we are
6431 // not "moving around", so we can take the slower/prettier
6432 // mode in the drawing code
6433 if (mVelocityTracker != null) {
6434 mVelocityTracker.recycle();
6435 mVelocityTracker = null;
6436 }
6437
6438 // Release any pulled glows
6439 if (mOverScrollGlow != null) {
6440 mOverScrollGlow.releaseAll();
6441 }
6442
6443 if (mSelectingText) {
6444 mSelectionStarted = false;
6445 syncSelectionCursors();
6446 if (mIsCaretSelection) {
6447 resetCaretTimer();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006448 }
6449 invalidate();
6450 }
6451 }
6452
6453 private void cancelTouch() {
6454 // we also use mVelocityTracker == null to tell us that we are
6455 // not "moving around", so we can take the slower/prettier
6456 // mode in the drawing code
6457 if (mVelocityTracker != null) {
6458 mVelocityTracker.recycle();
6459 mVelocityTracker = null;
6460 }
6461
6462 if ((mTouchMode == TOUCH_DRAG_MODE
6463 || mTouchMode == TOUCH_DRAG_LAYER_MODE) && !mSelectingText) {
6464 WebViewCore.resumePriority();
6465 WebViewCore.resumeUpdatePicture(mWebViewCore);
6466 nativeSetIsScrolling(false);
6467 }
6468 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
6469 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
6470 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
John Reckb2676f72012-03-02 14:13:06 -08006471 removeTouchHighlight();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006472 mHeldMotionless = MOTIONLESS_TRUE;
6473 mTouchMode = TOUCH_DONE_MODE;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006474 }
6475
George Mount7102eb22012-04-10 13:41:51 -07006476 private void snapDraggingCursor() {
6477 float scale = scaleAlongSegment(
6478 mSelectDraggingCursor.x, mSelectDraggingCursor.y,
6479 mSelectDraggingTextQuad.p4, mSelectDraggingTextQuad.p3);
6480 // clamp scale to ensure point is on the bottom segment
6481 scale = Math.max(0.0f, scale);
6482 scale = Math.min(scale, 1.0f);
6483 float newX = scaleCoordinate(scale,
6484 mSelectDraggingTextQuad.p4.x, mSelectDraggingTextQuad.p3.x);
6485 float newY = scaleCoordinate(scale,
6486 mSelectDraggingTextQuad.p4.y, mSelectDraggingTextQuad.p3.y);
George Mount80729762012-05-01 08:37:58 -07006487 int x = Math.round(newX);
6488 int y = Math.round(newY);
6489 if (mIsEditingText) {
George Mount1461a7c2012-06-07 10:41:41 -07006490 x = clampBetween(x, mEditTextContentBounds.left,
6491 mEditTextContentBounds.right);
6492 y = clampBetween(y, mEditTextContentBounds.top,
6493 mEditTextContentBounds.bottom);
George Mount80729762012-05-01 08:37:58 -07006494 }
George Mount7102eb22012-04-10 13:41:51 -07006495 mSelectDraggingCursor.set(x, y);
George Mountf9c1f992012-03-21 16:06:10 -07006496 }
6497
6498 private static float scaleCoordinate(float scale, float coord1, float coord2) {
6499 float diff = coord2 - coord1;
6500 return coord1 + (scale * diff);
6501 }
6502
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006503 @Override
6504 public boolean onGenericMotionEvent(MotionEvent event) {
6505 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
6506 switch (event.getAction()) {
6507 case MotionEvent.ACTION_SCROLL: {
6508 final float vscroll;
6509 final float hscroll;
6510 if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
6511 vscroll = 0;
6512 hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
6513 } else {
6514 vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
6515 hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
6516 }
6517 if (hscroll != 0 || vscroll != 0) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00006518 final int vdelta = (int) (vscroll *
6519 mWebViewPrivate.getVerticalScrollFactor());
6520 final int hdelta = (int) (hscroll *
6521 mWebViewPrivate.getHorizontalScrollFactor());
John Reckf2e61152012-08-20 17:13:47 -07006522
6523 abortAnimation();
6524 int oldTouchMode = mTouchMode;
6525 startScrollingLayer(event.getX(), event.getY());
6526 doDrag(hdelta, vdelta);
6527 mTouchMode = oldTouchMode;
6528 return true;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006529 }
6530 }
6531 }
6532 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00006533 return mWebViewPrivate.super_onGenericMotionEvent(event);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006534 }
6535
6536 private long mTrackballFirstTime = 0;
6537 private long mTrackballLastTime = 0;
6538 private float mTrackballRemainsX = 0.0f;
6539 private float mTrackballRemainsY = 0.0f;
6540 private int mTrackballXMove = 0;
6541 private int mTrackballYMove = 0;
6542 private boolean mSelectingText = false;
John Reckd4796462012-05-02 18:23:13 -07006543 private boolean mShowTextSelectionExtra = false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006544 private boolean mSelectionStarted = false;
6545 private static final int TRACKBALL_KEY_TIMEOUT = 1000;
6546 private static final int TRACKBALL_TIMEOUT = 200;
6547 private static final int TRACKBALL_WAIT = 100;
6548 private static final int TRACKBALL_SCALE = 400;
6549 private static final int TRACKBALL_SCROLL_COUNT = 5;
6550 private static final int TRACKBALL_MOVE_COUNT = 10;
6551 private static final int TRACKBALL_MULTIPLIER = 3;
6552 private static final int SELECT_CURSOR_OFFSET = 16;
6553 private static final int SELECT_SCROLL = 5;
6554 private int mSelectX = 0;
6555 private int mSelectY = 0;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006556 private boolean mTrackballDown = false;
6557 private long mTrackballUpTime = 0;
6558 private long mLastCursorTime = 0;
6559 private Rect mLastCursorBounds;
George Mount9b6eb692012-06-05 09:19:51 -07006560 private SelectionHandleAlpha mBaseAlpha = new SelectionHandleAlpha();
6561 private SelectionHandleAlpha mExtentAlpha = new SelectionHandleAlpha();
6562 private ObjectAnimator mBaseHandleAlphaAnimator =
6563 ObjectAnimator.ofInt(mBaseAlpha, "alpha", 0);
6564 private ObjectAnimator mExtentHandleAlphaAnimator =
6565 ObjectAnimator.ofInt(mExtentAlpha, "alpha", 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006566
6567 // Set by default; BrowserActivity clears to interpret trackball data
6568 // directly for movement. Currently, the framework only passes
6569 // arrow key events, not trackball events, from one child to the next
6570 private boolean mMapTrackballToArrowKeys = true;
6571
6572 private DrawData mDelaySetPicture;
6573 private DrawData mLoadedPicture;
6574
Jonathan Dixon19274f52012-07-12 18:03:43 -07006575 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006576 public void setMapTrackballToArrowKeys(boolean setMap) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006577 mMapTrackballToArrowKeys = setMap;
6578 }
6579
6580 void resetTrackballTime() {
6581 mTrackballLastTime = 0;
6582 }
6583
6584 @Override
6585 public boolean onTrackballEvent(MotionEvent ev) {
6586 long time = ev.getEventTime();
6587 if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
6588 if (ev.getY() > 0) pageDown(true);
6589 if (ev.getY() < 0) pageUp(true);
6590 return true;
6591 }
6592 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
6593 if (mSelectingText) {
6594 return true; // discard press if copy in progress
6595 }
6596 mTrackballDown = true;
6597 if (mNativeClass == 0) {
6598 return false;
6599 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006600 if (DebugFlags.WEB_VIEW) {
6601 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
6602 + " time=" + time
6603 + " mLastCursorTime=" + mLastCursorTime);
6604 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00006605 if (mWebView.isInTouchMode()) mWebView.requestFocusFromTouch();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006606 return false; // let common code in onKeyDown at it
6607 }
6608 if (ev.getAction() == MotionEvent.ACTION_UP) {
6609 // LONG_PRESS_CENTER is set in common onKeyDown
6610 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
6611 mTrackballDown = false;
6612 mTrackballUpTime = time;
6613 if (mSelectingText) {
6614 copySelection();
6615 selectionDone();
6616 return true; // discard press if copy in progress
6617 }
6618 if (DebugFlags.WEB_VIEW) {
6619 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
6620 + " time=" + time
6621 );
6622 }
6623 return false; // let common code in onKeyUp at it
6624 }
6625 if ((mMapTrackballToArrowKeys && (ev.getMetaState() & KeyEvent.META_SHIFT_ON) == 0) ||
6626 AccessibilityManager.getInstance(mContext).isEnabled()) {
6627 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
6628 return false;
6629 }
6630 if (mTrackballDown) {
6631 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
6632 return true; // discard move if trackball is down
6633 }
6634 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
6635 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
6636 return true;
6637 }
6638 // TODO: alternatively we can do panning as touch does
6639 switchOutDrawHistory();
6640 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
6641 if (DebugFlags.WEB_VIEW) {
6642 Log.v(LOGTAG, "onTrackballEvent time="
6643 + time + " last=" + mTrackballLastTime);
6644 }
6645 mTrackballFirstTime = time;
6646 mTrackballXMove = mTrackballYMove = 0;
6647 }
6648 mTrackballLastTime = time;
6649 if (DebugFlags.WEB_VIEW) {
6650 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
6651 }
6652 mTrackballRemainsX += ev.getX();
6653 mTrackballRemainsY += ev.getY();
6654 doTrackball(time, ev.getMetaState());
6655 return true;
6656 }
6657
6658 private int scaleTrackballX(float xRate, int width) {
6659 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
6660 int nextXMove = xMove;
6661 if (xMove > 0) {
6662 if (xMove > mTrackballXMove) {
6663 xMove -= mTrackballXMove;
6664 }
6665 } else if (xMove < mTrackballXMove) {
6666 xMove -= mTrackballXMove;
6667 }
6668 mTrackballXMove = nextXMove;
6669 return xMove;
6670 }
6671
6672 private int scaleTrackballY(float yRate, int height) {
6673 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
6674 int nextYMove = yMove;
6675 if (yMove > 0) {
6676 if (yMove > mTrackballYMove) {
6677 yMove -= mTrackballYMove;
6678 }
6679 } else if (yMove < mTrackballYMove) {
6680 yMove -= mTrackballYMove;
6681 }
6682 mTrackballYMove = nextYMove;
6683 return yMove;
6684 }
6685
6686 private int keyCodeToSoundsEffect(int keyCode) {
6687 switch(keyCode) {
6688 case KeyEvent.KEYCODE_DPAD_UP:
6689 return SoundEffectConstants.NAVIGATION_UP;
6690 case KeyEvent.KEYCODE_DPAD_RIGHT:
6691 return SoundEffectConstants.NAVIGATION_RIGHT;
6692 case KeyEvent.KEYCODE_DPAD_DOWN:
6693 return SoundEffectConstants.NAVIGATION_DOWN;
6694 case KeyEvent.KEYCODE_DPAD_LEFT:
6695 return SoundEffectConstants.NAVIGATION_LEFT;
6696 }
John Reck4fa40372012-03-06 14:31:45 -08006697 return 0;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006698 }
6699
6700 private void doTrackball(long time, int metaState) {
6701 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
6702 if (elapsed == 0) {
6703 elapsed = TRACKBALL_TIMEOUT;
6704 }
6705 float xRate = mTrackballRemainsX * 1000 / elapsed;
6706 float yRate = mTrackballRemainsY * 1000 / elapsed;
6707 int viewWidth = getViewWidth();
6708 int viewHeight = getViewHeight();
6709 float ax = Math.abs(xRate);
6710 float ay = Math.abs(yRate);
6711 float maxA = Math.max(ax, ay);
6712 if (DebugFlags.WEB_VIEW) {
6713 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
6714 + " xRate=" + xRate
6715 + " yRate=" + yRate
6716 + " mTrackballRemainsX=" + mTrackballRemainsX
6717 + " mTrackballRemainsY=" + mTrackballRemainsY);
6718 }
6719 int width = mContentWidth - viewWidth;
6720 int height = mContentHeight - viewHeight;
6721 if (width < 0) width = 0;
6722 if (height < 0) height = 0;
6723 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
6724 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
6725 maxA = Math.max(ax, ay);
6726 int count = Math.max(0, (int) maxA);
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006727 int oldScrollX = getScrollX();
6728 int oldScrollY = getScrollY();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006729 if (count > 0) {
6730 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
6731 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
6732 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
6733 KeyEvent.KEYCODE_DPAD_RIGHT;
6734 count = Math.min(count, TRACKBALL_MOVE_COUNT);
6735 if (DebugFlags.WEB_VIEW) {
6736 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
6737 + " count=" + count
6738 + " mTrackballRemainsX=" + mTrackballRemainsX
6739 + " mTrackballRemainsY=" + mTrackballRemainsY);
6740 }
John Reckb2676f72012-03-02 14:13:06 -08006741 if (mNativeClass != 0) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006742 for (int i = 0; i < count; i++) {
6743 letPageHandleNavKey(selectKeyCode, time, true, metaState);
6744 }
6745 letPageHandleNavKey(selectKeyCode, time, false, metaState);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006746 }
6747 mTrackballRemainsX = mTrackballRemainsY = 0;
6748 }
6749 if (count >= TRACKBALL_SCROLL_COUNT) {
6750 int xMove = scaleTrackballX(xRate, width);
6751 int yMove = scaleTrackballY(yRate, height);
6752 if (DebugFlags.WEB_VIEW) {
6753 Log.v(LOGTAG, "doTrackball pinScrollBy"
6754 + " count=" + count
6755 + " xMove=" + xMove + " yMove=" + yMove
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006756 + " mScrollX-oldScrollX=" + (getScrollX()-oldScrollX)
6757 + " mScrollY-oldScrollY=" + (getScrollY()-oldScrollY)
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006758 );
6759 }
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006760 if (Math.abs(getScrollX() - oldScrollX) > Math.abs(xMove)) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006761 xMove = 0;
6762 }
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006763 if (Math.abs(getScrollY() - oldScrollY) > Math.abs(yMove)) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006764 yMove = 0;
6765 }
6766 if (xMove != 0 || yMove != 0) {
6767 pinScrollBy(xMove, yMove, true, 0);
6768 }
6769 }
6770 }
6771
6772 /**
6773 * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}.
6774 * @return Maximum horizontal scroll position within real content
6775 */
6776 int computeMaxScrollX() {
6777 return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
6778 }
6779
6780 /**
6781 * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}.
6782 * @return Maximum vertical scroll position within real content
6783 */
6784 int computeMaxScrollY() {
6785 return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
6786 - getViewHeightWithTitle(), 0);
6787 }
6788
6789 boolean updateScrollCoordinates(int x, int y) {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006790 int oldX = getScrollX();
6791 int oldY = getScrollY();
6792 setScrollXRaw(x);
6793 setScrollYRaw(y);
6794 if (oldX != getScrollX() || oldY != getScrollY()) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00006795 mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006796 return true;
6797 } else {
6798 return false;
6799 }
6800 }
6801
Jonathan Dixon19274f52012-07-12 18:03:43 -07006802 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006803 public void flingScroll(int vx, int vy) {
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006804 mScroller.fling(getScrollX(), getScrollY(), vx, vy, 0, computeMaxScrollX(), 0,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006805 computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
6806 invalidate();
6807 }
6808
6809 private void doFling() {
6810 if (mVelocityTracker == null) {
6811 return;
6812 }
6813 int maxX = computeMaxScrollX();
6814 int maxY = computeMaxScrollY();
6815
6816 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
6817 int vx = (int) mVelocityTracker.getXVelocity();
6818 int vy = (int) mVelocityTracker.getYVelocity();
6819
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006820 int scrollX = getScrollX();
6821 int scrollY = getScrollY();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006822 int overscrollDistance = mOverscrollDistance;
6823 int overflingDistance = mOverflingDistance;
6824
6825 // Use the layer's scroll data if applicable.
6826 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
6827 scrollX = mScrollingLayerRect.left;
6828 scrollY = mScrollingLayerRect.top;
6829 maxX = mScrollingLayerRect.right;
6830 maxY = mScrollingLayerRect.bottom;
6831 // No overscrolling for layers.
6832 overscrollDistance = overflingDistance = 0;
George Mountfcff68f2012-03-20 10:21:57 -07006833 } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
6834 scrollX = getTextScrollX();
6835 scrollY = getTextScrollY();
6836 maxX = getMaxTextScrollX();
6837 maxY = getMaxTextScrollY();
6838 // No overscrolling for edit text.
6839 overscrollDistance = overflingDistance = 0;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006840 }
6841
6842 if (mSnapScrollMode != SNAP_NONE) {
6843 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
6844 vy = 0;
6845 } else {
6846 vx = 0;
6847 }
6848 }
6849 if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
6850 WebViewCore.resumePriority();
6851 if (!mSelectingText) {
6852 WebViewCore.resumeUpdatePicture(mWebViewCore);
6853 }
6854 if (mScroller.springBack(scrollX, scrollY, 0, maxX, 0, maxY)) {
6855 invalidate();
6856 }
6857 return;
6858 }
6859 float currentVelocity = mScroller.getCurrVelocity();
6860 float velocity = (float) Math.hypot(vx, vy);
6861 if (mLastVelocity > 0 && currentVelocity > 0 && velocity
6862 > mLastVelocity * MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION) {
6863 float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
6864 - Math.atan2(vy, vx)));
6865 final float circle = (float) (Math.PI) * 2.0f;
6866 if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
6867 vx += currentVelocity * mLastVelX / mLastVelocity;
6868 vy += currentVelocity * mLastVelY / mLastVelocity;
6869 velocity = (float) Math.hypot(vx, vy);
6870 if (DebugFlags.WEB_VIEW) {
6871 Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
6872 }
6873 } else if (DebugFlags.WEB_VIEW) {
6874 Log.v(LOGTAG, "doFling missed " + deltaR / circle);
6875 }
6876 } else if (DebugFlags.WEB_VIEW) {
6877 Log.v(LOGTAG, "doFling start last=" + mLastVelocity
6878 + " current=" + currentVelocity
6879 + " vx=" + vx + " vy=" + vy
6880 + " maxX=" + maxX + " maxY=" + maxY
6881 + " scrollX=" + scrollX + " scrollY=" + scrollY
6882 + " layer=" + mCurrentScrollingLayerId);
6883 }
6884
6885 // Allow sloppy flings without overscrolling at the edges.
6886 if ((scrollX == 0 || scrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
6887 vx = 0;
6888 }
6889 if ((scrollY == 0 || scrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
6890 vy = 0;
6891 }
6892
6893 if (overscrollDistance < overflingDistance) {
6894 if ((vx > 0 && scrollX == -overscrollDistance) ||
6895 (vx < 0 && scrollX == maxX + overscrollDistance)) {
6896 vx = 0;
6897 }
6898 if ((vy > 0 && scrollY == -overscrollDistance) ||
6899 (vy < 0 && scrollY == maxY + overscrollDistance)) {
6900 vy = 0;
6901 }
6902 }
6903
6904 mLastVelX = vx;
6905 mLastVelY = vy;
6906 mLastVelocity = velocity;
6907
6908 // no horizontal overscroll if the content just fits
6909 mScroller.fling(scrollX, scrollY, -vx, -vy, 0, maxX, 0, maxY,
6910 maxX == 0 ? 0 : overflingDistance, overflingDistance);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006911
6912 invalidate();
6913 }
6914
6915 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00006916 * See {@link WebView#getZoomControls()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006917 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00006918 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006919 @Deprecated
6920 public View getZoomControls() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006921 if (!getSettings().supportZoom()) {
6922 Log.w(LOGTAG, "This WebView doesn't support zoom.");
6923 return null;
6924 }
6925 return mZoomManager.getExternalZoomPicker();
6926 }
6927
6928 void dismissZoomControl() {
6929 mZoomManager.dismissZoomPicker();
6930 }
6931
6932 float getDefaultZoomScale() {
6933 return mZoomManager.getDefaultScale();
6934 }
6935
6936 /**
6937 * Return the overview scale of the WebView
6938 * @return The overview scale.
6939 */
6940 float getZoomOverviewScale() {
6941 return mZoomManager.getZoomOverviewScale();
6942 }
6943
6944 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00006945 * See {@link WebView#canZoomIn()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006946 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00006947 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006948 public boolean canZoomIn() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006949 return mZoomManager.canZoomIn();
6950 }
6951
6952 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00006953 * See {@link WebView#canZoomOut()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006954 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00006955 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006956 public boolean canZoomOut() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006957 return mZoomManager.canZoomOut();
6958 }
6959
6960 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00006961 * See {@link WebView#zoomIn()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006962 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00006963 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006964 public boolean zoomIn() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006965 return mZoomManager.zoomIn();
6966 }
6967
6968 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00006969 * See {@link WebView#zoomOut()}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006970 */
Jonathan Dixon19b80112012-03-02 18:18:00 +00006971 @Override
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006972 public boolean zoomOut() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006973 return mZoomManager.zoomOut();
6974 }
6975
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006976 /*
6977 * Return true if the rect (e.g. plugin) is fully visible and maximized
6978 * inside the WebView.
6979 */
6980 boolean isRectFitOnScreen(Rect rect) {
6981 final int rectWidth = rect.width();
6982 final int rectHeight = rect.height();
6983 final int viewWidth = getViewWidth();
6984 final int viewHeight = getViewHeightWithTitle();
6985 float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight / rectHeight);
6986 scale = mZoomManager.computeScaleWithLimits(scale);
6987 return !mZoomManager.willScaleTriggerZoom(scale)
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00006988 && contentToViewX(rect.left) >= getScrollX()
6989 && contentToViewX(rect.right) <= getScrollX() + viewWidth
6990 && contentToViewY(rect.top) >= getScrollY()
6991 && contentToViewY(rect.bottom) <= getScrollY() + viewHeight;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08006992 }
6993
6994 /*
6995 * Maximize and center the rectangle, specified in the document coordinate
6996 * space, inside the WebView. If the zoom doesn't need to be changed, do an
6997 * animated scroll to center it. If the zoom needs to be changed, find the
6998 * zoom center and do a smooth zoom transition. The rect is in document
6999 * coordinates
7000 */
7001 void centerFitRect(Rect rect) {
7002 final int rectWidth = rect.width();
7003 final int rectHeight = rect.height();
7004 final int viewWidth = getViewWidth();
7005 final int viewHeight = getViewHeightWithTitle();
7006 float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight
7007 / rectHeight);
7008 scale = mZoomManager.computeScaleWithLimits(scale);
7009 if (!mZoomManager.willScaleTriggerZoom(scale)) {
7010 pinScrollTo(contentToViewX(rect.left + rectWidth / 2) - viewWidth / 2,
7011 contentToViewY(rect.top + rectHeight / 2) - viewHeight / 2,
7012 true, 0);
7013 } else {
7014 float actualScale = mZoomManager.getScale();
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00007015 float oldScreenX = rect.left * actualScale - getScrollX();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007016 float rectViewX = rect.left * scale;
7017 float rectViewWidth = rectWidth * scale;
7018 float newMaxWidth = mContentWidth * scale;
7019 float newScreenX = (viewWidth - rectViewWidth) / 2;
7020 // pin the newX to the WebView
7021 if (newScreenX > rectViewX) {
7022 newScreenX = rectViewX;
7023 } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) {
7024 newScreenX = viewWidth - (newMaxWidth - rectViewX);
7025 }
7026 float zoomCenterX = (oldScreenX * scale - newScreenX * actualScale)
7027 / (scale - actualScale);
7028 float oldScreenY = rect.top * actualScale + getTitleHeight()
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00007029 - getScrollY();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007030 float rectViewY = rect.top * scale + getTitleHeight();
7031 float rectViewHeight = rectHeight * scale;
7032 float newMaxHeight = mContentHeight * scale + getTitleHeight();
7033 float newScreenY = (viewHeight - rectViewHeight) / 2;
7034 // pin the newY to the WebView
7035 if (newScreenY > rectViewY) {
7036 newScreenY = rectViewY;
7037 } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) {
7038 newScreenY = viewHeight - (newMaxHeight - rectViewY);
7039 }
7040 float zoomCenterY = (oldScreenY * scale - newScreenY * actualScale)
7041 / (scale - actualScale);
7042 mZoomManager.setZoomCenter(zoomCenterX, zoomCenterY);
7043 mZoomManager.startZoomAnimation(scale, false);
7044 }
7045 }
7046
7047 // Called by JNI to handle a touch on a node representing an email address,
7048 // address, or phone number
7049 private void overrideLoading(String url) {
7050 mCallbackProxy.uiOverrideUrlLoading(url);
7051 }
7052
7053 @Override
7054 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
John Reck1612fab2012-08-27 16:44:39 -07007055 // Check if we are destroyed
7056 if (mWebViewCore == null) return false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007057 // FIXME: If a subwindow is showing find, and the user touches the
7058 // background window, it can steal focus.
7059 if (mFindIsUp) return false;
7060 boolean result = false;
John Reckb2676f72012-03-02 14:13:06 -08007061 result = mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
John Recka4eddb52012-04-13 12:42:32 -07007062 if (mWebViewCore.getSettings().getNeedInitialFocus()
7063 && !mWebView.isInTouchMode()) {
John Reckb2676f72012-03-02 14:13:06 -08007064 // For cases such as GMail, where we gain focus from a direction,
7065 // we want to move to the first available link.
7066 // FIXME: If there are no visible links, we may not want to
7067 int fakeKeyDirection = 0;
7068 switch(direction) {
7069 case View.FOCUS_UP:
7070 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
7071 break;
7072 case View.FOCUS_DOWN:
7073 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
7074 break;
7075 case View.FOCUS_LEFT:
7076 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
7077 break;
7078 case View.FOCUS_RIGHT:
7079 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
7080 break;
7081 default:
7082 return result;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007083 }
John Recka4eddb52012-04-13 12:42:32 -07007084 mWebViewCore.sendMessage(EventHub.SET_INITIAL_FOCUS, fakeKeyDirection);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007085 }
7086 return result;
7087 }
7088
7089 @Override
Jonathan Dixon3c909522012-02-28 18:45:06 +00007090 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007091 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
7092 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
7093 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
7094 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
7095
7096 int measuredHeight = heightSize;
7097 int measuredWidth = widthSize;
7098
7099 // Grab the content size from WebViewCore.
7100 int contentHeight = contentToViewDimension(mContentHeight);
7101 int contentWidth = contentToViewDimension(mContentWidth);
7102
7103// Log.d(LOGTAG, "------- measure " + heightMode);
7104
7105 if (heightMode != MeasureSpec.EXACTLY) {
7106 mHeightCanMeasure = true;
7107 measuredHeight = contentHeight;
7108 if (heightMode == MeasureSpec.AT_MOST) {
7109 // If we are larger than the AT_MOST height, then our height can
7110 // no longer be measured and we should scroll internally.
7111 if (measuredHeight > heightSize) {
7112 measuredHeight = heightSize;
7113 mHeightCanMeasure = false;
Jonathan Dixon3c909522012-02-28 18:45:06 +00007114 measuredHeight |= View.MEASURED_STATE_TOO_SMALL;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007115 }
7116 }
7117 } else {
7118 mHeightCanMeasure = false;
7119 }
7120 if (mNativeClass != 0) {
7121 nativeSetHeightCanMeasure(mHeightCanMeasure);
7122 }
7123 // For the width, always use the given size unless unspecified.
7124 if (widthMode == MeasureSpec.UNSPECIFIED) {
7125 mWidthCanMeasure = true;
7126 measuredWidth = contentWidth;
7127 } else {
7128 if (measuredWidth < contentWidth) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007129 measuredWidth |= View.MEASURED_STATE_TOO_SMALL;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007130 }
7131 mWidthCanMeasure = false;
7132 }
7133
7134 synchronized (this) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007135 mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007136 }
7137 }
7138
7139 @Override
7140 public boolean requestChildRectangleOnScreen(View child,
7141 Rect rect,
7142 boolean immediate) {
7143 if (mNativeClass == 0) {
7144 return false;
7145 }
7146 // don't scroll while in zoom animation. When it is done, we will adjust
John Reckb2676f72012-03-02 14:13:06 -08007147 // the necessary components
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007148 if (mZoomManager.isFixedLengthAnimationInProgress()) {
7149 return false;
7150 }
7151
7152 rect.offset(child.getLeft() - child.getScrollX(),
7153 child.getTop() - child.getScrollY());
7154
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00007155 Rect content = new Rect(viewToContentX(getScrollX()),
7156 viewToContentY(getScrollY()),
7157 viewToContentX(getScrollX() + getWidth()
Jonathan Dixon3c909522012-02-28 18:45:06 +00007158 - mWebView.getVerticalScrollbarWidth()),
Jonathan Dixon0dc0da62012-02-23 18:08:01 +00007159 viewToContentY(getScrollY() + getViewHeightWithTitle()));
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007160 int screenTop = contentToViewY(content.top);
7161 int screenBottom = contentToViewY(content.bottom);
7162 int height = screenBottom - screenTop;
7163 int scrollYDelta = 0;
7164
7165 if (rect.bottom > screenBottom) {
7166 int oneThirdOfScreenHeight = height / 3;
7167 if (rect.height() > 2 * oneThirdOfScreenHeight) {
7168 // If the rectangle is too tall to fit in the bottom two thirds
7169 // of the screen, place it at the top.
7170 scrollYDelta = rect.top - screenTop;
7171 } else {
7172 // If the rectangle will still fit on screen, we want its
7173 // top to be in the top third of the screen.
7174 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
7175 }
7176 } else if (rect.top < screenTop) {
7177 scrollYDelta = rect.top - screenTop;
7178 }
7179
7180 int screenLeft = contentToViewX(content.left);
7181 int screenRight = contentToViewX(content.right);
7182 int width = screenRight - screenLeft;
7183 int scrollXDelta = 0;
7184
7185 if (rect.right > screenRight && rect.left > screenLeft) {
7186 if (rect.width() > width) {
7187 scrollXDelta += (rect.left - screenLeft);
7188 } else {
7189 scrollXDelta += (rect.right - screenRight);
7190 }
7191 } else if (rect.left < screenLeft) {
7192 scrollXDelta -= (screenLeft - rect.left);
7193 }
7194
7195 if ((scrollYDelta | scrollXDelta) != 0) {
7196 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
7197 }
7198
7199 return false;
7200 }
7201
7202 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
7203 String replace, int newStart, int newEnd) {
7204 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
7205 arg.mReplace = replace;
7206 arg.mNewStart = newStart;
7207 arg.mNewEnd = newEnd;
7208 mTextGeneration++;
7209 arg.mTextGeneration = mTextGeneration;
George Mountdbef1c52012-03-28 14:17:13 -07007210 sendBatchableInputMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007211 }
7212
7213 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
7214 // check if mWebViewCore has been destroyed
7215 if (mWebViewCore == null) {
7216 return;
7217 }
7218 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
7219 arg.mEvent = event;
7220 arg.mCurrentText = currentText;
7221 // Increase our text generation number, and pass it to webcore thread
7222 mTextGeneration++;
7223 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
7224 // WebKit's document state is not saved until about to leave the page.
7225 // To make sure the host application, like Browser, has the up to date
7226 // document state when it goes to background, we force to save the
7227 // document state.
7228 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
John Reckb2676f72012-03-02 14:13:06 -08007229 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE, null, 1000);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007230 }
7231
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007232 public synchronized WebViewCore getWebViewCore() {
7233 return mWebViewCore;
7234 }
7235
George Mountfcff68f2012-03-20 10:21:57 -07007236 private boolean canTextScroll(int directionX, int directionY) {
7237 int scrollX = getTextScrollX();
7238 int scrollY = getTextScrollY();
7239 int maxScrollX = getMaxTextScrollX();
7240 int maxScrollY = getMaxTextScrollY();
7241 boolean canScrollX = (directionX > 0)
7242 ? (scrollX < maxScrollX)
7243 : (scrollX > 0);
7244 boolean canScrollY = (directionY > 0)
7245 ? (scrollY < maxScrollY)
7246 : (scrollY > 0);
7247 return canScrollX || canScrollY;
7248 }
7249
7250 private int getTextScrollX() {
7251 return -mEditTextContent.left;
7252 }
7253
7254 private int getTextScrollY() {
7255 return -mEditTextContent.top;
7256 }
7257
7258 private int getMaxTextScrollX() {
George Mount7102eb22012-04-10 13:41:51 -07007259 return Math.max(0, mEditTextContent.width() - mEditTextContentBounds.width());
George Mountfcff68f2012-03-20 10:21:57 -07007260 }
7261
7262 private int getMaxTextScrollY() {
George Mount7102eb22012-04-10 13:41:51 -07007263 return Math.max(0, mEditTextContent.height() - mEditTextContentBounds.height());
George Mountfcff68f2012-03-20 10:21:57 -07007264 }
7265
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007266 //-------------------------------------------------------------------------
7267 // Methods can be called from a separate thread, like WebViewCore
7268 // If it needs to call the View system, it has to send message.
7269 //-------------------------------------------------------------------------
7270
7271 /**
7272 * General handler to receive message coming from webkit thread
7273 */
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07007274 class PrivateHandler extends Handler implements WebViewInputDispatcher.UiCallbacks {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007275 @Override
7276 public void handleMessage(Message msg) {
7277 // exclude INVAL_RECT_MSG_ID since it is frequently output
7278 if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
7279 if (msg.what >= FIRST_PRIVATE_MSG_ID
7280 && msg.what <= LAST_PRIVATE_MSG_ID) {
7281 Log.v(LOGTAG, HandlerPrivateDebugString[msg.what
7282 - FIRST_PRIVATE_MSG_ID]);
7283 } else if (msg.what >= FIRST_PACKAGE_MSG_ID
7284 && msg.what <= LAST_PACKAGE_MSG_ID) {
7285 Log.v(LOGTAG, HandlerPackageDebugString[msg.what
7286 - FIRST_PACKAGE_MSG_ID]);
7287 } else {
7288 Log.v(LOGTAG, Integer.toString(msg.what));
7289 }
7290 }
7291 if (mWebViewCore == null) {
7292 // after WebView's destroy() is called, skip handling messages.
7293 return;
7294 }
7295 if (mBlockWebkitViewMessages
7296 && msg.what != WEBCORE_INITIALIZED_MSG_ID) {
7297 // Blocking messages from webkit
7298 return;
7299 }
7300 switch (msg.what) {
7301 case REMEMBER_PASSWORD: {
7302 mDatabase.setUsernamePassword(
7303 msg.getData().getString("host"),
7304 msg.getData().getString("username"),
7305 msg.getData().getString("password"));
7306 ((Message) msg.obj).sendToTarget();
7307 break;
7308 }
7309 case NEVER_REMEMBER_PASSWORD: {
Ben Murdoch99c12e82012-04-25 15:00:17 +01007310 mDatabase.setUsernamePassword(msg.getData().getString("host"), null, null);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007311 ((Message) msg.obj).sendToTarget();
7312 break;
7313 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007314 case SCROLL_SELECT_TEXT: {
7315 if (mAutoScrollX == 0 && mAutoScrollY == 0) {
7316 mSentAutoScrollMessage = false;
7317 break;
7318 }
7319 if (mCurrentScrollingLayerId == 0) {
7320 pinScrollBy(mAutoScrollX, mAutoScrollY, true, 0);
7321 } else {
7322 scrollLayerTo(mScrollingLayerRect.left + mAutoScrollX,
7323 mScrollingLayerRect.top + mAutoScrollY);
7324 }
7325 sendEmptyMessageDelayed(
7326 SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
7327 break;
7328 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007329 case SCROLL_TO_MSG_ID: {
7330 // arg1 = animate, arg2 = onlyIfImeIsShowing
7331 // obj = Point(x, y)
7332 if (msg.arg2 == 1) {
7333 // This scroll is intended to bring the textfield into
7334 // view, but is only necessary if the IME is showing
7335 InputMethodManager imm = InputMethodManager.peekInstance();
7336 if (imm == null || !imm.isAcceptingText()
John Reckb2676f72012-03-02 14:13:06 -08007337 || !imm.isActive(mWebView)) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007338 break;
7339 }
7340 }
7341 final Point p = (Point) msg.obj;
John Recka60a1892012-04-19 10:48:20 -07007342 contentScrollTo(p.x, p.y, msg.arg1 == 1);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007343 break;
7344 }
7345 case UPDATE_ZOOM_RANGE: {
7346 WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj;
7347 // mScrollX contains the new minPrefWidth
7348 mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
7349 break;
7350 }
7351 case UPDATE_ZOOM_DENSITY: {
7352 final float density = (Float) msg.obj;
7353 mZoomManager.updateDefaultZoomDensity(density);
7354 break;
7355 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007356 case NEW_PICTURE_MSG_ID: {
7357 // called for new content
7358 final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
7359 setNewPicture(draw, true);
7360 break;
7361 }
7362 case WEBCORE_INITIALIZED_MSG_ID:
7363 // nativeCreate sets mNativeClass to a non-zero value
7364 String drawableDir = BrowserFrame.getRawResFilename(
7365 BrowserFrame.DRAWABLEDIR, mContext);
Jeff Brown98365d72012-08-19 20:30:52 -07007366 nativeCreate(msg.arg1, drawableDir, ActivityManager.isHighEndGfx());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007367 if (mDelaySetPicture != null) {
7368 setNewPicture(mDelaySetPicture, true);
7369 mDelaySetPicture = null;
7370 }
7371 if (mIsPaused) {
7372 nativeSetPauseDrawing(mNativeClass, true);
7373 }
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07007374 mInputDispatcher = new WebViewInputDispatcher(this,
7375 mWebViewCore.getInputDispatcherCallbacks());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007376 break;
7377 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
7378 // Make sure that the textfield is currently focused
7379 // and representing the same node as the pointer.
7380 if (msg.arg2 == mTextGeneration) {
7381 String text = (String) msg.obj;
7382 if (null == text) {
7383 text = "";
7384 }
John Reckb2676f72012-03-02 14:13:06 -08007385 if (mInputConnection != null &&
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007386 mFieldPointer == msg.arg1) {
7387 mInputConnection.setTextAndKeepSelection(text);
7388 }
7389 }
7390 break;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007391 case UPDATE_TEXT_SELECTION_MSG_ID:
7392 updateTextSelectionFromMessage(msg.arg1, msg.arg2,
7393 (WebViewCore.TextSelectionData) msg.obj);
7394 break;
John Reck4fa40372012-03-06 14:31:45 -08007395 case TAKE_FOCUS:
7396 int direction = msg.arg1;
7397 View focusSearch = mWebView.focusSearch(direction);
7398 if (focusSearch != null && focusSearch != mWebView) {
7399 focusSearch.requestFocus();
7400 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007401 break;
7402 case CLEAR_TEXT_ENTRY:
John Reckb2676f72012-03-02 14:13:06 -08007403 hideSoftKeyboard();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007404 break;
7405 case INVAL_RECT_MSG_ID: {
7406 Rect r = (Rect)msg.obj;
7407 if (r == null) {
7408 invalidate();
7409 } else {
7410 // we need to scale r from content into view coords,
7411 // which viewInvalidate() does for us
7412 viewInvalidate(r.left, r.top, r.right, r.bottom);
7413 }
7414 break;
7415 }
George Mountbcd5dd72012-03-01 08:39:03 -08007416 case REQUEST_FORM_DATA:
7417 if (mFieldPointer == msg.arg1) {
7418 ArrayAdapter<String> adapter = (ArrayAdapter<String>)msg.obj;
7419 mAutoCompletePopup.setAdapter(adapter);
7420 }
7421 break;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007422
7423 case LONG_PRESS_CENTER:
7424 // as this is shared by keydown and trackballdown, reset all
7425 // the states
7426 mGotCenterDown = false;
7427 mTrackballDown = false;
John Recka8ae3e92012-06-13 10:37:40 -07007428 mWebView.performLongClick();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007429 break;
7430
7431 case WEBCORE_NEED_TOUCH_EVENTS:
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07007432 mInputDispatcher.setWebKitWantsTouchEvents(msg.arg1 != 0);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007433 break;
7434
7435 case REQUEST_KEYBOARD:
7436 if (msg.arg1 == 0) {
7437 hideSoftKeyboard();
7438 } else {
7439 displaySoftKeyboard(false);
7440 }
7441 break;
7442
7443 case DRAG_HELD_MOTIONLESS:
7444 mHeldMotionless = MOTIONLESS_TRUE;
7445 invalidate();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007446 break;
7447
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007448 case SCREEN_ON:
Jonathan Dixon3c909522012-02-28 18:45:06 +00007449 mWebView.setKeepScreenOn(msg.arg1 == 1);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007450 break;
7451
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007452 case EXIT_FULLSCREEN_VIDEO:
7453 if (mHTML5VideoViewProxy != null) {
7454 mHTML5VideoViewProxy.exitFullScreenVideo();
7455 }
7456 break;
7457
7458 case SHOW_FULLSCREEN: {
7459 View view = (View) msg.obj;
7460 int orientation = msg.arg1;
7461 int npp = msg.arg2;
7462
7463 if (inFullScreenMode()) {
7464 Log.w(LOGTAG, "Should not have another full screen.");
7465 dismissFullScreenMode();
7466 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00007467 mFullScreenHolder = new PluginFullScreenHolder(WebViewClassic.this, orientation, npp);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007468 mFullScreenHolder.setContentView(view);
7469 mFullScreenHolder.show();
7470 invalidate();
7471
7472 break;
7473 }
7474 case HIDE_FULLSCREEN:
7475 dismissFullScreenMode();
7476 break;
7477
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007478 case SHOW_RECT_MSG_ID: {
7479 WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007480 int left = contentToViewX(data.mLeft);
7481 int width = contentToViewDimension(data.mWidth);
7482 int maxWidth = contentToViewDimension(data.mContentWidth);
7483 int viewWidth = getViewWidth();
Mangesh Ghiwarecf034e32012-05-12 14:46:05 -07007484 int x = (int) (left + data.mXPercentInDoc * width -
7485 data.mXPercentInView * viewWidth);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007486 if (DebugFlags.WEB_VIEW) {
7487 Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" +
7488 width + ",maxWidth=" + maxWidth +
7489 ",viewWidth=" + viewWidth + ",x="
7490 + x + ",xPercentInDoc=" + data.mXPercentInDoc +
7491 ",xPercentInView=" + data.mXPercentInView+ ")");
7492 }
7493 // use the passing content width to cap x as the current
7494 // mContentWidth may not be updated yet
7495 x = Math.max(0,
7496 (Math.min(maxWidth, x + viewWidth)) - viewWidth);
7497 int top = contentToViewY(data.mTop);
7498 int height = contentToViewDimension(data.mHeight);
7499 int maxHeight = contentToViewDimension(data.mContentHeight);
7500 int viewHeight = getViewHeight();
7501 int y = (int) (top + data.mYPercentInDoc * height -
7502 data.mYPercentInView * viewHeight);
7503 if (DebugFlags.WEB_VIEW) {
7504 Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" +
7505 height + ",maxHeight=" + maxHeight +
7506 ",viewHeight=" + viewHeight + ",y="
7507 + y + ",yPercentInDoc=" + data.mYPercentInDoc +
7508 ",yPercentInView=" + data.mYPercentInView+ ")");
7509 }
7510 // use the passing content height to cap y as the current
7511 // mContentHeight may not be updated yet
7512 y = Math.max(0,
7513 (Math.min(maxHeight, y + viewHeight) - viewHeight));
7514 // We need to take into account the visible title height
7515 // when scrolling since y is an absolute view position.
7516 y = Math.max(0, y - getVisibleTitleHeightImpl());
Jonathan Dixon3c909522012-02-28 18:45:06 +00007517 mWebView.scrollTo(x, y);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007518 }
7519 break;
7520
7521 case CENTER_FIT_RECT:
7522 centerFitRect((Rect)msg.obj);
7523 break;
7524
7525 case SET_SCROLLBAR_MODES:
7526 mHorizontalScrollBarMode = msg.arg1;
7527 mVerticalScrollBarMode = msg.arg2;
7528 break;
7529
George Mountbcd5dd72012-03-01 08:39:03 -08007530 case FOCUS_NODE_CHANGED:
George Mountf70276a2012-03-12 14:22:10 -07007531 mIsEditingText = (msg.arg1 == mFieldPointer);
7532 if (mAutoCompletePopup != null && !mIsEditingText) {
7533 mAutoCompletePopup.clearAdapter();
George Mountbcd5dd72012-03-01 08:39:03 -08007534 }
7535 // fall through to HIT_TEST_RESULT
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007536 case HIT_TEST_RESULT:
7537 WebKitHitTest hit = (WebKitHitTest) msg.obj;
7538 mFocusedNode = hit;
7539 setTouchHighlightRects(hit);
7540 setHitTestResult(hit);
7541 break;
7542
7543 case SAVE_WEBARCHIVE_FINISHED:
7544 SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj;
7545 if (saveMessage.mCallback != null) {
7546 saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile);
7547 }
7548 break;
7549
7550 case SET_AUTOFILLABLE:
7551 mAutoFillData = (WebViewCore.AutoFillData) msg.obj;
George Mountbcd5dd72012-03-01 08:39:03 -08007552 if (mInputConnection != null) {
7553 mInputConnection.setAutoFillable(mAutoFillData.getQueryId());
7554 mAutoCompletePopup.setAutoFillQueryId(mAutoFillData.getQueryId());
7555 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007556 break;
7557
7558 case AUTOFILL_COMPLETE:
George Mountbcd5dd72012-03-01 08:39:03 -08007559 if (mAutoCompletePopup != null) {
7560 ArrayList<String> pastEntries = new ArrayList<String>();
7561 mAutoCompletePopup.setAdapter(new ArrayAdapter<String>(
7562 mContext,
7563 com.android.internal.R.layout.web_text_view_dropdown,
7564 pastEntries));
7565 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007566 break;
7567
7568 case COPY_TO_CLIPBOARD:
7569 copyToClipboard((String) msg.obj);
7570 break;
7571
7572 case INIT_EDIT_FIELD:
7573 if (mInputConnection != null) {
7574 TextFieldInitData initData = (TextFieldInitData) msg.obj;
7575 mTextGeneration = 0;
7576 mFieldPointer = initData.mFieldPointer;
7577 mInputConnection.initEditorInfo(initData);
7578 mInputConnection.setTextAndKeepSelection(initData.mText);
George Mount7102eb22012-04-10 13:41:51 -07007579 mEditTextContentBounds.set(initData.mContentBounds);
George Mountf70276a2012-03-12 14:22:10 -07007580 mEditTextLayerId = initData.mNodeLayerId;
7581 nativeMapLayerRect(mNativeClass, mEditTextLayerId,
George Mount7102eb22012-04-10 13:41:51 -07007582 mEditTextContentBounds);
George Mountc6c83ce2012-06-05 14:22:22 -07007583 mEditTextContent.set(initData.mClientRect);
George Mountf70276a2012-03-12 14:22:10 -07007584 relocateAutoCompletePopup();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007585 }
7586 break;
7587
7588 case REPLACE_TEXT:{
7589 String text = (String)msg.obj;
7590 int start = msg.arg1;
7591 int end = msg.arg2;
7592 int cursorPosition = start + text.length();
7593 replaceTextfieldText(start, end, text,
7594 cursorPosition, cursorPosition);
George Mountc4c3bc92012-05-16 13:29:55 -07007595 selectionDone();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007596 break;
7597 }
7598
7599 case UPDATE_MATCH_COUNT: {
Victoria Lease0b8413b2012-03-26 13:04:10 -07007600 WebViewCore.FindAllRequest request = (WebViewCore.FindAllRequest)msg.obj;
7601 if (request == null) {
7602 if (mFindCallback != null) {
7603 mFindCallback.updateMatchCount(0, 0, true);
7604 }
7605 } else if (request == mFindRequest) {
7606 int matchCount, matchIndex;
7607 synchronized (mFindRequest) {
7608 matchCount = request.mMatchCount;
7609 matchIndex = request.mMatchIndex;
7610 }
7611 if (mFindCallback != null) {
7612 mFindCallback.updateMatchCount(matchIndex, matchCount, false);
7613 }
7614 if (mFindListener != null) {
7615 mFindListener.onFindResultReceived(matchIndex, matchCount, true);
7616 }
7617 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007618 break;
7619 }
Victoria Lease0b8413b2012-03-26 13:04:10 -07007620
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007621 case CLEAR_CARET_HANDLE:
George Mount0925a762012-05-16 13:51:15 -07007622 if (mIsCaretSelection) {
7623 selectionDone();
7624 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007625 break;
7626
7627 case KEY_PRESS:
George Mountdbef1c52012-03-28 14:17:13 -07007628 sendBatchableInputMessage(EventHub.KEY_PRESS, msg.arg1, 0, null);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007629 break;
7630
George Mountbcd5dd72012-03-01 08:39:03 -08007631 case RELOCATE_AUTO_COMPLETE_POPUP:
7632 relocateAutoCompletePopup();
7633 break;
7634
7635 case AUTOFILL_FORM:
7636 mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM,
7637 msg.arg1, /* unused */0);
7638 break;
7639
George Mountc6c549d2012-03-15 16:39:16 -07007640 case EDIT_TEXT_SIZE_CHANGED:
7641 if (msg.arg1 == mFieldPointer) {
7642 mEditTextContent.set((Rect)msg.obj);
7643 }
7644 break;
7645
George Mountf796fdf2012-03-19 14:51:55 -07007646 case SHOW_CARET_HANDLE:
7647 if (!mSelectingText && mIsEditingText && mIsCaretSelection) {
7648 setupWebkitSelect();
7649 resetCaretTimer();
7650 showPasteWindow();
7651 }
7652 break;
7653
George Mount7102eb22012-04-10 13:41:51 -07007654 case UPDATE_CONTENT_BOUNDS:
7655 mEditTextContentBounds.set((Rect) msg.obj);
George Mount7a33acf2012-05-02 16:35:01 -07007656 nativeMapLayerRect(mNativeClass, mEditTextLayerId,
7657 mEditTextContentBounds);
George Mount7102eb22012-04-10 13:41:51 -07007658 break;
7659
George Mount557748d2012-04-04 13:56:49 -07007660 case SCROLL_EDIT_TEXT:
7661 scrollEditWithCursor();
7662 break;
7663
George Mount1461a7c2012-06-07 10:41:41 -07007664 case SCROLL_HANDLE_INTO_VIEW:
7665 scrollDraggedSelectionHandleIntoView();
7666 break;
7667
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007668 default:
7669 super.handleMessage(msg);
7670 break;
7671 }
7672 }
Jeff Brown9d3bdbd2012-03-21 11:50:06 -07007673
7674 @Override
7675 public Looper getUiLooper() {
7676 return getLooper();
7677 }
7678
7679 @Override
7680 public void dispatchUiEvent(MotionEvent event, int eventType, int flags) {
7681 onHandleUiEvent(event, eventType, flags);
7682 }
7683
7684 @Override
7685 public Context getContext() {
7686 return WebViewClassic.this.getContext();
7687 }
George Mount7a33acf2012-05-02 16:35:01 -07007688
7689 @Override
7690 public boolean shouldInterceptTouchEvent(MotionEvent event) {
7691 if (!mSelectingText) {
7692 return false;
7693 }
7694 ensureSelectionHandles();
7695 int y = Math.round(event.getY() - getTitleHeight() + getScrollY());
7696 int x = Math.round(event.getX() + getScrollX());
7697 boolean isPressingHandle;
7698 if (mIsCaretSelection) {
7699 isPressingHandle = mSelectHandleCenter.getBounds()
7700 .contains(x, y);
7701 } else {
7702 isPressingHandle =
George Mountb49f2bb2012-06-01 14:29:37 -07007703 mSelectHandleBaseBounds.contains(x, y)
7704 || mSelectHandleExtentBounds.contains(x, y);
George Mount7a33acf2012-05-02 16:35:01 -07007705 }
7706 return isPressingHandle;
7707 }
John Reck84fa2412012-05-03 15:56:58 -07007708
7709 @Override
7710 public void showTapHighlight(boolean show) {
7711 if (mShowTapHighlight != show) {
7712 mShowTapHighlight = show;
7713 invalidate();
7714 }
7715 }
John Reckeb11f472012-05-17 13:47:39 -07007716
7717 @Override
7718 public void clearPreviousHitTest() {
7719 setHitTestResult(null);
7720 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007721 }
7722
7723 private void setHitTestTypeFromUrl(String url) {
7724 String substr = null;
7725 if (url.startsWith(SCHEME_GEO)) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007726 mInitialHitTestResult.setType(HitTestResult.GEO_TYPE);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007727 substr = url.substring(SCHEME_GEO.length());
7728 } else if (url.startsWith(SCHEME_TEL)) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007729 mInitialHitTestResult.setType(HitTestResult.PHONE_TYPE);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007730 substr = url.substring(SCHEME_TEL.length());
7731 } else if (url.startsWith(SCHEME_MAILTO)) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007732 mInitialHitTestResult.setType(HitTestResult.EMAIL_TYPE);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007733 substr = url.substring(SCHEME_MAILTO.length());
7734 } else {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007735 mInitialHitTestResult.setType(HitTestResult.SRC_ANCHOR_TYPE);
7736 mInitialHitTestResult.setExtra(url);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007737 return;
7738 }
7739 try {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007740 mInitialHitTestResult.setExtra(URLDecoder.decode(substr, "UTF-8"));
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007741 } catch (Throwable e) {
7742 Log.w(LOGTAG, "Failed to decode URL! " + substr, e);
Jonathan Dixon3c909522012-02-28 18:45:06 +00007743 mInitialHitTestResult.setType(HitTestResult.UNKNOWN_TYPE);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007744 }
7745 }
7746
7747 private void setHitTestResult(WebKitHitTest hit) {
7748 if (hit == null) {
7749 mInitialHitTestResult = null;
7750 return;
7751 }
7752 mInitialHitTestResult = new HitTestResult();
7753 if (hit.mLinkUrl != null) {
7754 setHitTestTypeFromUrl(hit.mLinkUrl);
7755 if (hit.mImageUrl != null
Jonathan Dixon3c909522012-02-28 18:45:06 +00007756 && mInitialHitTestResult.getType() == HitTestResult.SRC_ANCHOR_TYPE) {
7757 mInitialHitTestResult.setType(HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
7758 mInitialHitTestResult.setExtra(hit.mImageUrl);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007759 }
7760 } else if (hit.mImageUrl != null) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007761 mInitialHitTestResult.setType(HitTestResult.IMAGE_TYPE);
7762 mInitialHitTestResult.setExtra(hit.mImageUrl);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007763 } else if (hit.mEditable) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007764 mInitialHitTestResult.setType(HitTestResult.EDIT_TEXT_TYPE);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007765 } else if (hit.mIntentUrl != null) {
7766 setHitTestTypeFromUrl(hit.mIntentUrl);
7767 }
7768 }
7769
7770 private boolean shouldDrawHighlightRect() {
7771 if (mFocusedNode == null || mInitialHitTestResult == null) {
7772 return false;
7773 }
7774 if (mTouchHighlightRegion.isEmpty()) {
7775 return false;
7776 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00007777 if (mFocusedNode.mHasFocus && !mWebView.isInTouchMode()) {
John Reck413fab32012-03-07 11:02:15 -08007778 return mDrawCursorRing && !mFocusedNode.mEditable;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007779 }
John Reckef109b02012-03-06 18:02:11 -08007780 if (mFocusedNode.mHasFocus && mFocusedNode.mEditable) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007781 return false;
7782 }
John Reck84fa2412012-05-03 15:56:58 -07007783 return mShowTapHighlight;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007784 }
7785
7786
7787 private FocusTransitionDrawable mFocusTransition = null;
7788 static class FocusTransitionDrawable extends Drawable {
7789 Region mPreviousRegion;
7790 Region mNewRegion;
7791 float mProgress = 0;
Jonathan Dixon3c909522012-02-28 18:45:06 +00007792 WebViewClassic mWebView;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007793 Paint mPaint;
7794 int mMaxAlpha;
7795 Point mTranslate;
7796
Jonathan Dixon3c909522012-02-28 18:45:06 +00007797 public FocusTransitionDrawable(WebViewClassic view) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007798 mWebView = view;
7799 mPaint = new Paint(mWebView.mTouchHightlightPaint);
7800 mMaxAlpha = mPaint.getAlpha();
7801 }
7802
7803 @Override
7804 public void setColorFilter(ColorFilter cf) {
7805 }
7806
7807 @Override
7808 public void setAlpha(int alpha) {
7809 }
7810
7811 @Override
7812 public int getOpacity() {
7813 return 0;
7814 }
7815
7816 public void setProgress(float p) {
7817 mProgress = p;
7818 if (mWebView.mFocusTransition == this) {
7819 if (mProgress == 1f)
7820 mWebView.mFocusTransition = null;
7821 mWebView.invalidate();
7822 }
7823 }
7824
7825 public float getProgress() {
7826 return mProgress;
7827 }
7828
7829 @Override
7830 public void draw(Canvas canvas) {
7831 if (mTranslate == null) {
7832 Rect bounds = mPreviousRegion.getBounds();
7833 Point from = new Point(bounds.centerX(), bounds.centerY());
7834 mNewRegion.getBounds(bounds);
7835 Point to = new Point(bounds.centerX(), bounds.centerY());
7836 mTranslate = new Point(from.x - to.x, from.y - to.y);
7837 }
7838 int alpha = (int) (mProgress * mMaxAlpha);
7839 RegionIterator iter = new RegionIterator(mPreviousRegion);
7840 Rect r = new Rect();
7841 mPaint.setAlpha(mMaxAlpha - alpha);
7842 float tx = mTranslate.x * mProgress;
7843 float ty = mTranslate.y * mProgress;
7844 int save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
7845 canvas.translate(-tx, -ty);
7846 while (iter.next(r)) {
7847 canvas.drawRect(r, mPaint);
7848 }
7849 canvas.restoreToCount(save);
7850 iter = new RegionIterator(mNewRegion);
7851 r = new Rect();
7852 mPaint.setAlpha(alpha);
7853 save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
7854 tx = mTranslate.x - tx;
7855 ty = mTranslate.y - ty;
7856 canvas.translate(tx, ty);
7857 while (iter.next(r)) {
7858 canvas.drawRect(r, mPaint);
7859 }
7860 canvas.restoreToCount(save);
7861 }
7862 };
7863
7864 private boolean shouldAnimateTo(WebKitHitTest hit) {
7865 // TODO: Don't be annoying or throw out the animation entirely
7866 return false;
7867 }
7868
7869 private void setTouchHighlightRects(WebKitHitTest hit) {
7870 FocusTransitionDrawable transition = null;
7871 if (shouldAnimateTo(hit)) {
7872 transition = new FocusTransitionDrawable(this);
7873 }
7874 Rect[] rects = hit != null ? hit.mTouchRects : null;
7875 if (!mTouchHighlightRegion.isEmpty()) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00007876 mWebView.invalidate(mTouchHighlightRegion.getBounds());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007877 if (transition != null) {
7878 transition.mPreviousRegion = new Region(mTouchHighlightRegion);
7879 }
7880 mTouchHighlightRegion.setEmpty();
7881 }
7882 if (rects != null) {
7883 mTouchHightlightPaint.setColor(hit.mTapHighlightColor);
7884 for (Rect rect : rects) {
7885 Rect viewRect = contentToViewRect(rect);
7886 // some sites, like stories in nytimes.com, set
7887 // mouse event handler in the top div. It is not
7888 // user friendly to highlight the div if it covers
7889 // more than half of the screen.
7890 if (viewRect.width() < getWidth() >> 1
7891 || viewRect.height() < getHeight() >> 1) {
7892 mTouchHighlightRegion.union(viewRect);
John Reckd855ffd2012-04-12 14:45:49 -07007893 } else if (DebugFlags.WEB_VIEW) {
7894 Log.d(LOGTAG, "Skip the huge selection rect:"
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007895 + viewRect);
7896 }
7897 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00007898 mWebView.invalidate(mTouchHighlightRegion.getBounds());
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007899 if (transition != null && transition.mPreviousRegion != null) {
7900 transition.mNewRegion = new Region(mTouchHighlightRegion);
7901 mFocusTransition = transition;
7902 ObjectAnimator animator = ObjectAnimator.ofFloat(
7903 mFocusTransition, "progress", 1f);
7904 animator.start();
7905 }
7906 }
7907 }
7908
Jonathan Dixon3c909522012-02-28 18:45:06 +00007909 // Interface to allow the profiled WebView to hook the page swap notifications.
7910 public interface PageSwapDelegate {
7911 void onPageSwapOccurred(boolean notifyAnimationStarted);
7912 }
7913
John Reck834f66b2012-03-27 12:52:57 -07007914 long mLastSwapTime;
7915 double mAverageSwapFps;
7916
Jonathan Dixoncd93e152012-03-02 19:19:44 +00007917 /** Called by JNI when pages are swapped (only occurs with hardware
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007918 * acceleration) */
7919 protected void pageSwapCallback(boolean notifyAnimationStarted) {
John Reck834f66b2012-03-27 12:52:57 -07007920 if (DebugFlags.MEASURE_PAGE_SWAP_FPS) {
7921 long now = System.currentTimeMillis();
7922 long diff = now - mLastSwapTime;
7923 mAverageSwapFps = ((1000.0 / diff) + mAverageSwapFps) / 2;
7924 Log.d(LOGTAG, "page swap fps: " + mAverageSwapFps);
7925 mLastSwapTime = now;
7926 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007927 mWebViewCore.resumeWebKitDraw();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007928 if (notifyAnimationStarted) {
7929 mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
7930 }
Jonathan Dixon3c909522012-02-28 18:45:06 +00007931 if (mWebView instanceof PageSwapDelegate) {
7932 // This provides a hook for ProfiledWebView to observe the tile page swaps.
7933 ((PageSwapDelegate) mWebView).onPageSwapOccurred(notifyAnimationStarted);
7934 }
Chris Craik9e5936b2012-04-11 16:45:41 -07007935
7936 if (mPictureListener != null) {
7937 // trigger picture listener for hardware layers. Software layers are
7938 // triggered in setNewPicture
Ben Murdoch1c8d7f02013-02-19 11:13:08 +00007939 Picture picture = mContext.getApplicationInfo().targetSdkVersion <
Ben Murdoch52643e02013-02-26 12:01:00 +00007940 Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null;
Ben Murdoch1c8d7f02013-02-19 11:13:08 +00007941 mPictureListener.onNewPicture(getWebView(), picture);
Chris Craik9e5936b2012-04-11 16:45:41 -07007942 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007943 }
7944
7945 void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) {
7946 if (mNativeClass == 0) {
7947 if (mDelaySetPicture != null) {
7948 throw new IllegalStateException("Tried to setNewPicture with"
7949 + " a delay picture already set! (memory leak)");
7950 }
7951 // Not initialized yet, delay set
7952 mDelaySetPicture = draw;
7953 return;
7954 }
7955 WebViewCore.ViewState viewState = draw.mViewState;
7956 boolean isPictureAfterFirstLayout = viewState != null;
7957
7958 if (updateBaseLayer) {
Chris Craikc2c95432012-04-25 15:13:52 -07007959 setBaseLayer(draw.mBaseLayer,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007960 getSettings().getShowVisualIndicator(),
7961 isPictureAfterFirstLayout);
7962 }
7963 final Point viewSize = draw.mViewSize;
7964 // We update the layout (i.e. request a layout from the
7965 // view system) if the last view size that we sent to
7966 // WebCore matches the view size of the picture we just
7967 // received in the fixed dimension.
7968 final boolean updateLayout = viewSize.x == mLastWidthSent
7969 && viewSize.y == mLastHeightSent;
7970 // Don't send scroll event for picture coming from webkit,
7971 // since the new picture may cause a scroll event to override
7972 // the saved history scroll position.
7973 mSendScrollEvent = false;
7974 recordNewContentSize(draw.mContentSize.x,
7975 draw.mContentSize.y, updateLayout);
7976 if (isPictureAfterFirstLayout) {
7977 // Reset the last sent data here since dealing with new page.
7978 mLastWidthSent = 0;
7979 mZoomManager.onFirstLayout(draw);
7980 int scrollX = viewState.mShouldStartScrolledRight
7981 ? getContentWidth() : viewState.mScrollX;
7982 int scrollY = viewState.mScrollY;
John Recka60a1892012-04-19 10:48:20 -07007983 contentScrollTo(scrollX, scrollY, false);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007984 if (!mDrawHistory) {
John Reckb2676f72012-03-02 14:13:06 -08007985 // As we are on a new page, hide the keyboard
7986 hideSoftKeyboard();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08007987 }
7988 }
7989 mSendScrollEvent = true;
7990
Chris Craikc2c95432012-04-25 15:13:52 -07007991 int functor = 0;
Chris Craik41ee4652012-05-31 15:05:57 -07007992 boolean forceInval = isPictureAfterFirstLayout;
John Reck579f4e92012-04-30 10:50:04 -07007993 ViewRootImpl viewRoot = mWebView.getViewRootImpl();
Chris Craik793b7f82012-07-12 14:01:50 -07007994 if (mWebView.isHardwareAccelerated()
7995 && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE
7996 && viewRoot != null) {
Chris Craikc2c95432012-04-25 15:13:52 -07007997 functor = nativeGetDrawGLFunction(mNativeClass);
Chris Craik1266692b2012-04-30 13:13:41 -07007998 if (functor != 0) {
Chris Craik41ee4652012-05-31 15:05:57 -07007999 // force an invalidate if functor attach not successful
8000 forceInval |= !viewRoot.attachFunctor(functor);
Chris Craik1266692b2012-04-30 13:13:41 -07008001 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008002 }
Chris Craikc2c95432012-04-25 15:13:52 -07008003
John Reck579f4e92012-04-30 10:50:04 -07008004 if (functor == 0
Chris Craik41ee4652012-05-31 15:05:57 -07008005 || forceInval
John Reck579f4e92012-04-30 10:50:04 -07008006 || mWebView.getLayerType() != View.LAYER_TYPE_NONE) {
Chris Craikc2c95432012-04-25 15:13:52 -07008007 // invalidate the screen so that the next repaint will show new content
8008 // TODO: partial invalidate
John Reckca835db2012-03-26 17:18:43 -07008009 mWebView.invalidate();
8010 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008011
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008012 // update the zoom information based on the new picture
Chris Craik02c2f452012-05-15 10:34:33 -07008013 if (mZoomManager.onNewPicture(draw))
8014 invalidate();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008015
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008016 if (isPictureAfterFirstLayout) {
8017 mViewManager.postReadyToDrawAll();
8018 }
George Mount557748d2012-04-04 13:56:49 -07008019 scrollEditWithCursor();
George Mount8b0d90e2012-04-10 12:29:59 -07008020
8021 if (mPictureListener != null) {
Chris Craik9e5936b2012-04-11 16:45:41 -07008022 if (!mWebView.isHardwareAccelerated()
8023 || mWebView.getLayerType() == View.LAYER_TYPE_SOFTWARE) {
8024 // trigger picture listener for software layers. Hardware layers are
8025 // triggered in pageSwapCallback
Ben Murdoch1c8d7f02013-02-19 11:13:08 +00008026 Picture picture = mContext.getApplicationInfo().targetSdkVersion <
Ben Murdoch52643e02013-02-26 12:01:00 +00008027 Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null;
Ben Murdoch1c8d7f02013-02-19 11:13:08 +00008028 mPictureListener.onNewPicture(getWebView(), picture);
Chris Craik9e5936b2012-04-11 16:45:41 -07008029 }
George Mount8b0d90e2012-04-10 12:29:59 -07008030 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008031 }
8032
8033 /**
8034 * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
John Reckb2676f72012-03-02 14:13:06 -08008035 * and UPDATE_TEXT_SELECTION_MSG_ID.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008036 */
8037 private void updateTextSelectionFromMessage(int nodePointer,
8038 int textGeneration, WebViewCore.TextSelectionData data) {
8039 if (textGeneration == mTextGeneration) {
John Reckb2676f72012-03-02 14:13:06 -08008040 if (mInputConnection != null && mFieldPointer == nodePointer) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008041 mInputConnection.setSelection(data.mStart, data.mEnd);
8042 }
8043 }
8044 nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
8045
George Mount899f6092012-05-11 14:48:31 -07008046 if ((data.mSelectionReason == TextSelectionData.REASON_ACCESSIBILITY_INJECTOR)
George Mountc2813fa2012-05-23 14:01:44 -07008047 || (!mSelectingText && data.mStart != data.mEnd
George Mount899f6092012-05-11 14:48:31 -07008048 && data.mSelectionReason != TextSelectionData.REASON_SELECT_WORD)) {
John Reckd4796462012-05-02 18:23:13 -07008049 selectionDone();
8050 mShowTextSelectionExtra = true;
8051 invalidate();
8052 return;
8053 }
8054
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008055 if (data.mSelectTextPtr != 0 &&
8056 (data.mStart != data.mEnd ||
George Mount3281c382012-06-15 11:32:13 -07008057 (mFieldPointer == nodePointer && mFieldPointer != 0) ||
8058 (nodePointer == 0 && data.mStart == 0 && data.mEnd == 0))) {
George Mount4212a182012-06-15 10:27:14 -07008059 mIsEditingText = (mFieldPointer == nodePointer) && nodePointer != 0;
George Mount3281c382012-06-15 11:32:13 -07008060 mIsCaretSelection = (data.mStart == data.mEnd && nodePointer != 0);
George Mountf796fdf2012-03-19 14:51:55 -07008061 if (mIsCaretSelection &&
8062 (mInputConnection == null ||
8063 mInputConnection.getEditable().length() == 0)) {
8064 // There's no text, don't show caret handle.
8065 selectionDone();
8066 } else {
8067 if (!mSelectingText) {
8068 setupWebkitSelect();
George Mountf9c1f992012-03-21 16:06:10 -07008069 } else {
George Mountb49f2bb2012-06-01 14:29:37 -07008070 syncSelectionCursors();
George Mountf796fdf2012-03-19 14:51:55 -07008071 }
George Mount9b6eb692012-06-05 09:19:51 -07008072 animateHandles();
George Mountf796fdf2012-03-19 14:51:55 -07008073 if (mIsCaretSelection) {
8074 resetCaretTimer();
8075 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008076 }
8077 } else {
8078 selectionDone();
8079 }
8080 invalidate();
8081 }
8082
George Mountf70276a2012-03-12 14:22:10 -07008083 private void scrollEditText(int scrollX, int scrollY) {
8084 // Scrollable edit text. Scroll it.
George Mountfcff68f2012-03-20 10:21:57 -07008085 float maxScrollX = getMaxTextScrollX();
George Mountf70276a2012-03-12 14:22:10 -07008086 float scrollPercentX = ((float)scrollX)/maxScrollX;
8087 mEditTextContent.offsetTo(-scrollX, -scrollY);
George Mount1461a7c2012-06-07 10:41:41 -07008088 mWebViewCore.removeMessages(EventHub.SCROLL_TEXT_INPUT);
8089 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0,
George Mountf70276a2012-03-12 14:22:10 -07008090 scrollY, (Float)scrollPercentX);
George Mount9b6eb692012-06-05 09:19:51 -07008091 animateHandles();
George Mountf70276a2012-03-12 14:22:10 -07008092 }
8093
George Mountdbef1c52012-03-28 14:17:13 -07008094 private void beginTextBatch() {
8095 mIsBatchingTextChanges = true;
8096 }
8097
8098 private void commitTextBatch() {
8099 if (mWebViewCore != null) {
8100 mWebViewCore.sendMessages(mBatchedTextChanges);
8101 }
8102 mBatchedTextChanges.clear();
8103 mIsBatchingTextChanges = false;
8104 }
8105
alanv525823a2012-05-16 20:01:51 -07008106 void sendBatchableInputMessage(int what, int arg1, int arg2,
George Mountdbef1c52012-03-28 14:17:13 -07008107 Object obj) {
8108 if (mWebViewCore == null) {
8109 return;
8110 }
8111 Message message = Message.obtain(null, what, arg1, arg2, obj);
8112 if (mIsBatchingTextChanges) {
8113 mBatchedTextChanges.add(message);
8114 } else {
8115 mWebViewCore.sendMessage(message);
8116 }
8117 }
8118
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008119 // Class used to use a dropdown for a <select> element
8120 private class InvokeListBox implements Runnable {
8121 // Whether the listbox allows multiple selection.
8122 private boolean mMultiple;
8123 // Passed in to a list with multiple selection to tell
8124 // which items are selected.
8125 private int[] mSelectedArray;
8126 // Passed in to a list with single selection to tell
8127 // where the initial selection is.
8128 private int mSelection;
8129
8130 private Container[] mContainers;
8131
8132 // Need these to provide stable ids to my ArrayAdapter,
8133 // which normally does not have stable ids. (Bug 1250098)
8134 private class Container extends Object {
8135 /**
8136 * Possible values for mEnabled. Keep in sync with OptionStatus in
8137 * WebViewCore.cpp
8138 */
8139 final static int OPTGROUP = -1;
8140 final static int OPTION_DISABLED = 0;
8141 final static int OPTION_ENABLED = 1;
8142
8143 String mString;
8144 int mEnabled;
8145 int mId;
8146
8147 @Override
8148 public String toString() {
8149 return mString;
8150 }
8151 }
8152
8153 /**
8154 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
8155 * and allow filtering.
8156 */
8157 private class MyArrayListAdapter extends ArrayAdapter<Container> {
8158 public MyArrayListAdapter() {
Jonathan Dixon3c909522012-02-28 18:45:06 +00008159 super(WebViewClassic.this.mContext,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008160 mMultiple ? com.android.internal.R.layout.select_dialog_multichoice :
8161 com.android.internal.R.layout.webview_select_singlechoice,
8162 mContainers);
8163 }
8164
8165 @Override
8166 public View getView(int position, View convertView,
8167 ViewGroup parent) {
8168 // Always pass in null so that we will get a new CheckedTextView
8169 // Otherwise, an item which was previously used as an <optgroup>
8170 // element (i.e. has no check), could get used as an <option>
8171 // element, which needs a checkbox/radio, but it would not have
8172 // one.
8173 convertView = super.getView(position, null, parent);
8174 Container c = item(position);
8175 if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
8176 // ListView does not draw dividers between disabled and
8177 // enabled elements. Use a LinearLayout to provide dividers
8178 LinearLayout layout = new LinearLayout(mContext);
8179 layout.setOrientation(LinearLayout.VERTICAL);
8180 if (position > 0) {
8181 View dividerTop = new View(mContext);
8182 dividerTop.setBackgroundResource(
8183 android.R.drawable.divider_horizontal_bright);
8184 layout.addView(dividerTop);
8185 }
8186
8187 if (Container.OPTGROUP == c.mEnabled) {
8188 // Currently select_dialog_multichoice uses CheckedTextViews.
8189 // If that changes, the class cast will no longer be valid.
8190 if (mMultiple) {
8191 Assert.assertTrue(convertView instanceof CheckedTextView);
8192 ((CheckedTextView) convertView).setCheckMarkDrawable(null);
8193 }
8194 } else {
8195 // c.mEnabled == Container.OPTION_DISABLED
8196 // Draw the disabled element in a disabled state.
8197 convertView.setEnabled(false);
8198 }
8199
8200 layout.addView(convertView);
8201 if (position < getCount() - 1) {
8202 View dividerBottom = new View(mContext);
8203 dividerBottom.setBackgroundResource(
8204 android.R.drawable.divider_horizontal_bright);
8205 layout.addView(dividerBottom);
8206 }
8207 return layout;
8208 }
8209 return convertView;
8210 }
8211
8212 @Override
8213 public boolean hasStableIds() {
8214 // AdapterView's onChanged method uses this to determine whether
8215 // to restore the old state. Return false so that the old (out
8216 // of date) state does not replace the new, valid state.
8217 return false;
8218 }
8219
8220 private Container item(int position) {
8221 if (position < 0 || position >= getCount()) {
8222 return null;
8223 }
8224 return getItem(position);
8225 }
8226
8227 @Override
8228 public long getItemId(int position) {
8229 Container item = item(position);
8230 if (item == null) {
8231 return -1;
8232 }
8233 return item.mId;
8234 }
8235
8236 @Override
8237 public boolean areAllItemsEnabled() {
8238 return false;
8239 }
8240
8241 @Override
8242 public boolean isEnabled(int position) {
8243 Container item = item(position);
8244 if (item == null) {
8245 return false;
8246 }
8247 return Container.OPTION_ENABLED == item.mEnabled;
8248 }
8249 }
8250
8251 private InvokeListBox(String[] array, int[] enabled, int[] selected) {
8252 mMultiple = true;
8253 mSelectedArray = selected;
8254
8255 int length = array.length;
8256 mContainers = new Container[length];
8257 for (int i = 0; i < length; i++) {
8258 mContainers[i] = new Container();
8259 mContainers[i].mString = array[i];
8260 mContainers[i].mEnabled = enabled[i];
8261 mContainers[i].mId = i;
8262 }
8263 }
8264
8265 private InvokeListBox(String[] array, int[] enabled, int selection) {
8266 mSelection = selection;
8267 mMultiple = false;
8268
8269 int length = array.length;
8270 mContainers = new Container[length];
8271 for (int i = 0; i < length; i++) {
8272 mContainers[i] = new Container();
8273 mContainers[i].mString = array[i];
8274 mContainers[i].mEnabled = enabled[i];
8275 mContainers[i].mId = i;
8276 }
8277 }
8278
8279 /*
8280 * Whenever the data set changes due to filtering, this class ensures
8281 * that the checked item remains checked.
8282 */
8283 private class SingleDataSetObserver extends DataSetObserver {
8284 private long mCheckedId;
8285 private ListView mListView;
8286 private Adapter mAdapter;
8287
8288 /*
8289 * Create a new observer.
8290 * @param id The ID of the item to keep checked.
8291 * @param l ListView for getting and clearing the checked states
8292 * @param a Adapter for getting the IDs
8293 */
8294 public SingleDataSetObserver(long id, ListView l, Adapter a) {
8295 mCheckedId = id;
8296 mListView = l;
8297 mAdapter = a;
8298 }
8299
8300 @Override
8301 public void onChanged() {
8302 // The filter may have changed which item is checked. Find the
8303 // item that the ListView thinks is checked.
8304 int position = mListView.getCheckedItemPosition();
8305 long id = mAdapter.getItemId(position);
8306 if (mCheckedId != id) {
8307 // Clear the ListView's idea of the checked item, since
8308 // it is incorrect
8309 mListView.clearChoices();
8310 // Search for mCheckedId. If it is in the filtered list,
8311 // mark it as checked
8312 int count = mAdapter.getCount();
8313 for (int i = 0; i < count; i++) {
8314 if (mAdapter.getItemId(i) == mCheckedId) {
8315 mListView.setItemChecked(i, true);
8316 break;
8317 }
8318 }
8319 }
8320 }
8321 }
8322
8323 @Override
8324 public void run() {
John Reckd4717b82012-05-04 13:13:38 -07008325 if (mWebViewCore == null
8326 || getWebView().getWindowToken() == null
8327 || getWebView().getViewRootImpl() == null) {
8328 // We've been detached and/or destroyed since this was posted
8329 return;
8330 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008331 final ListView listView = (ListView) LayoutInflater.from(mContext)
8332 .inflate(com.android.internal.R.layout.select_dialog, null);
8333 final MyArrayListAdapter adapter = new MyArrayListAdapter();
8334 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
8335 .setView(listView).setCancelable(true)
8336 .setInverseBackgroundForced(true);
8337
8338 if (mMultiple) {
8339 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
8340 @Override
8341 public void onClick(DialogInterface dialog, int which) {
8342 mWebViewCore.sendMessage(
8343 EventHub.LISTBOX_CHOICES,
8344 adapter.getCount(), 0,
8345 listView.getCheckedItemPositions());
8346 }});
8347 b.setNegativeButton(android.R.string.cancel,
8348 new DialogInterface.OnClickListener() {
8349 @Override
8350 public void onClick(DialogInterface dialog, int which) {
8351 mWebViewCore.sendMessage(
8352 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
8353 }});
8354 }
8355 mListBoxDialog = b.create();
8356 listView.setAdapter(adapter);
8357 listView.setFocusableInTouchMode(true);
8358 // There is a bug (1250103) where the checks in a ListView with
8359 // multiple items selected are associated with the positions, not
8360 // the ids, so the items do not properly retain their checks when
8361 // filtered. Do not allow filtering on multiple lists until
8362 // that bug is fixed.
8363
8364 listView.setTextFilterEnabled(!mMultiple);
8365 if (mMultiple) {
8366 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
8367 int length = mSelectedArray.length;
8368 for (int i = 0; i < length; i++) {
8369 listView.setItemChecked(mSelectedArray[i], true);
8370 }
8371 } else {
8372 listView.setOnItemClickListener(new OnItemClickListener() {
8373 @Override
8374 public void onItemClick(AdapterView<?> parent, View v,
8375 int position, long id) {
8376 // Rather than sending the message right away, send it
8377 // after the page regains focus.
8378 mListBoxMessage = Message.obtain(null,
8379 EventHub.SINGLE_LISTBOX_CHOICE, (int) id, 0);
Michael Kolb87a435a2012-03-22 10:33:50 -07008380 if (mListBoxDialog != null) {
8381 mListBoxDialog.dismiss();
8382 mListBoxDialog = null;
8383 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008384 }
8385 });
8386 if (mSelection != -1) {
8387 listView.setSelection(mSelection);
8388 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
8389 listView.setItemChecked(mSelection, true);
8390 DataSetObserver observer = new SingleDataSetObserver(
8391 adapter.getItemId(mSelection), listView, adapter);
8392 adapter.registerDataSetObserver(observer);
8393 }
8394 }
8395 mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
8396 @Override
8397 public void onCancel(DialogInterface dialog) {
Amit Kumarfe6c30e2013-02-24 13:22:45 +00008398 if (mWebViewCore != null) {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008399 mWebViewCore.sendMessage(
8400 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
Amit Kumarfe6c30e2013-02-24 13:22:45 +00008401 }
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008402 mListBoxDialog = null;
8403 }
8404 });
8405 mListBoxDialog.show();
8406 }
8407 }
8408
8409 private Message mListBoxMessage;
8410
8411 /*
8412 * Request a dropdown menu for a listbox with multiple selection.
8413 *
8414 * @param array Labels for the listbox.
8415 * @param enabledArray State for each element in the list. See static
8416 * integers in Container class.
8417 * @param selectedArray Which positions are initally selected.
8418 */
8419 void requestListBox(String[] array, int[] enabledArray, int[]
8420 selectedArray) {
8421 mPrivateHandler.post(
8422 new InvokeListBox(array, enabledArray, selectedArray));
8423 }
8424
8425 /*
8426 * Request a dropdown menu for a listbox with single selection or a single
8427 * <select> element.
8428 *
8429 * @param array Labels for the listbox.
8430 * @param enabledArray State for each element in the list. See static
8431 * integers in Container class.
8432 * @param selection Which position is initally selected.
8433 */
8434 void requestListBox(String[] array, int[] enabledArray, int selection) {
8435 mPrivateHandler.post(
8436 new InvokeListBox(array, enabledArray, selection));
8437 }
8438
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008439 private int getScaledMaxXScroll() {
8440 int width;
8441 if (mHeightCanMeasure == false) {
8442 width = getViewWidth() / 4;
8443 } else {
8444 Rect visRect = new Rect();
8445 calcOurVisibleRect(visRect);
8446 width = visRect.width() / 2;
8447 }
8448 // FIXME the divisor should be retrieved from somewhere
8449 return viewToContentX(width);
8450 }
8451
8452 private int getScaledMaxYScroll() {
8453 int height;
8454 if (mHeightCanMeasure == false) {
8455 height = getViewHeight() / 4;
8456 } else {
8457 Rect visRect = new Rect();
8458 calcOurVisibleRect(visRect);
8459 height = visRect.height() / 2;
8460 }
8461 // FIXME the divisor should be retrieved from somewhere
8462 // the closest thing today is hard-coded into ScrollView.java
8463 // (from ScrollView.java, line 363) int maxJump = height/2;
8464 return Math.round(height * mZoomManager.getInvScale());
8465 }
8466
8467 /**
8468 * Called by JNI to invalidate view
8469 */
8470 private void viewInvalidate() {
8471 invalidate();
8472 }
8473
8474 /**
8475 * Pass the key directly to the page. This assumes that
8476 * nativePageShouldHandleShiftAndArrows() returned true.
8477 */
8478 private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
8479 int keyEventAction;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008480 if (down) {
8481 keyEventAction = KeyEvent.ACTION_DOWN;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008482 } else {
8483 keyEventAction = KeyEvent.ACTION_UP;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008484 }
8485
8486 KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
8487 1, (metaState & KeyEvent.META_SHIFT_ON)
8488 | (metaState & KeyEvent.META_ALT_ON)
8489 | (metaState & KeyEvent.META_SYM_ON)
8490 , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
John Reck4fa40372012-03-06 14:31:45 -08008491 sendKeyEvent(event);
8492 }
8493
8494 private void sendKeyEvent(KeyEvent event) {
8495 int direction = 0;
8496 switch (event.getKeyCode()) {
8497 case KeyEvent.KEYCODE_DPAD_DOWN:
8498 direction = View.FOCUS_DOWN;
8499 break;
8500 case KeyEvent.KEYCODE_DPAD_UP:
8501 direction = View.FOCUS_UP;
8502 break;
8503 case KeyEvent.KEYCODE_DPAD_LEFT:
8504 direction = View.FOCUS_LEFT;
8505 break;
8506 case KeyEvent.KEYCODE_DPAD_RIGHT:
8507 direction = View.FOCUS_RIGHT;
8508 break;
8509 case KeyEvent.KEYCODE_TAB:
8510 direction = event.isShiftPressed() ? View.FOCUS_BACKWARD : View.FOCUS_FORWARD;
8511 break;
8512 }
8513 if (direction != 0 && mWebView.focusSearch(direction) == null) {
8514 // Can't take focus in that direction
8515 direction = 0;
8516 }
8517 int eventHubAction = EventHub.KEY_UP;
8518 if (event.getAction() == KeyEvent.ACTION_DOWN) {
8519 eventHubAction = EventHub.KEY_DOWN;
8520 int sound = keyCodeToSoundsEffect(event.getKeyCode());
8521 if (sound != 0) {
8522 mWebView.playSoundEffect(sound);
8523 }
8524 }
George Mountdbef1c52012-03-28 14:17:13 -07008525 sendBatchableInputMessage(eventHubAction, direction, 0, event);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008526 }
8527
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008528 /**
Jonathan Dixon19b80112012-03-02 18:18:00 +00008529 * See {@link WebView#setBackgroundColor(int)}
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008530 */
8531 @Override
8532 public void setBackgroundColor(int color) {
8533 mBackgroundColor = color;
8534 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
8535 }
8536
8537 /**
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008538 * Enable the communication b/t the webView and VideoViewProxy
8539 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008540 * only used by the Browser
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008541 */
8542 public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
8543 mHTML5VideoViewProxy = proxy;
8544 }
8545
8546 /**
8547 * Set the time to wait between passing touches to WebCore. See also the
8548 * TOUCH_SENT_INTERVAL member for further discussion.
8549 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008550 * This is only used by the DRT test application.
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008551 */
8552 public void setTouchInterval(int interval) {
8553 mCurrentTouchInterval = interval;
8554 }
8555
8556 /**
8557 * Copy text into the clipboard. This is called indirectly from
8558 * WebViewCore.
8559 * @param text The text to put into the clipboard.
8560 */
8561 private void copyToClipboard(String text) {
Jonathan Dixon3c909522012-02-28 18:45:06 +00008562 ClipboardManager cm = (ClipboardManager)mContext
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008563 .getSystemService(Context.CLIPBOARD_SERVICE);
8564 ClipData clip = ClipData.newPlainText(getTitle(), text);
8565 cm.setPrimaryClip(clip);
8566 }
8567
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008568 /*package*/ void autoFillForm(int autoFillQueryId) {
George Mountbcd5dd72012-03-01 08:39:03 -08008569 mPrivateHandler.obtainMessage(AUTOFILL_FORM, autoFillQueryId, 0)
8570 .sendToTarget();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008571 }
8572
8573 /* package */ ViewManager getViewManager() {
8574 return mViewManager;
8575 }
8576
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008577 /** send content invalidate */
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008578 protected void contentInvalidateAll() {
8579 if (mWebViewCore != null && !mBlockWebkitViewMessages) {
8580 mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
8581 }
8582 }
8583
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008584 /** discard all textures from tiles. Used in Profiled WebView */
Jonathan Dixon3c909522012-02-28 18:45:06 +00008585 public void discardAllTextures() {
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008586 nativeDiscardAllTextures();
8587 }
8588
John Recka5408e62012-03-16 14:18:44 -07008589 @Override
8590 public void setLayerType(int layerType, Paint paint) {
8591 updateHwAccelerated();
8592 }
8593
Ben Murdoche623e242012-09-07 20:47:07 +01008594 @Override
8595 public void preDispatchDraw(Canvas canvas) {
8596 // no-op for WebViewClassic.
8597 }
8598
John Recka5408e62012-03-16 14:18:44 -07008599 private void updateHwAccelerated() {
8600 if (mNativeClass == 0) {
8601 return;
8602 }
8603 boolean hwAccelerated = false;
8604 if (mWebView.isHardwareAccelerated()
8605 && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
8606 hwAccelerated = true;
8607 }
Chris Craik00ed0fd2012-04-10 13:10:24 -07008608
8609 // result is of type LayerAndroid::InvalidateFlags, non zero means invalidate/redraw
John Recka5408e62012-03-16 14:18:44 -07008610 int result = nativeSetHwAccelerated(mNativeClass, hwAccelerated);
Chris Craik00ed0fd2012-04-10 13:10:24 -07008611 if (mWebViewCore != null && !mBlockWebkitViewMessages && result != 0) {
8612 mWebViewCore.contentDraw();
John Recka5408e62012-03-16 14:18:44 -07008613 }
8614 }
8615
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008616 /**
8617 * Begin collecting per-tile profiling data
8618 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008619 * only used by profiling tests
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008620 */
8621 public void tileProfilingStart() {
8622 nativeTileProfilingStart();
8623 }
8624 /**
8625 * Return per-tile profiling data
8626 *
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008627 * only used by profiling tests
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008628 */
8629 public float tileProfilingStop() {
8630 return nativeTileProfilingStop();
8631 }
8632
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008633 /** only used by profiling tests */
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008634 public void tileProfilingClear() {
8635 nativeTileProfilingClear();
8636 }
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008637 /** only used by profiling tests */
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008638 public int tileProfilingNumFrames() {
8639 return nativeTileProfilingNumFrames();
8640 }
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008641 /** only used by profiling tests */
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008642 public int tileProfilingNumTilesInFrame(int frame) {
8643 return nativeTileProfilingNumTilesInFrame(frame);
8644 }
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008645 /** only used by profiling tests */
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008646 public int tileProfilingGetInt(int frame, int tile, String key) {
8647 return nativeTileProfilingGetInt(frame, tile, key);
8648 }
Jonathan Dixoncd93e152012-03-02 19:19:44 +00008649 /** only used by profiling tests */
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008650 public float tileProfilingGetFloat(int frame, int tile, String key) {
8651 return nativeTileProfilingGetFloat(frame, tile, key);
8652 }
8653
8654 /**
8655 * Checks the focused content for an editable text field. This can be
8656 * text input or ContentEditable.
8657 * @return true if the focused item is an editable text field.
8658 */
8659 boolean focusCandidateIsEditableText() {
John Reckb2676f72012-03-02 14:13:06 -08008660 if (mFocusedNode != null) {
8661 return mFocusedNode.mEditable;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008662 }
John Reckb2676f72012-03-02 14:13:06 -08008663 return false;
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008664 }
8665
John Reckf72a4462012-03-02 14:21:18 -08008666 // Called via JNI
8667 private void postInvalidate() {
8668 mWebView.postInvalidate();
8669 }
8670
Ben Murdoch2b5d6682012-05-24 13:01:57 +01008671 // Note: must be called before first WebViewClassic is created.
8672 public static void setShouldMonitorWebCoreThread() {
8673 WebViewCore.setShouldMonitorWebCoreThread();
8674 }
8675
John Reck926cf562012-06-14 10:00:31 -07008676 @Override
8677 public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
8678 int layer = getBaseLayer();
8679 if (layer != 0) {
8680 try {
8681 ByteArrayOutputStream stream = new ByteArrayOutputStream();
8682 ViewStateSerializer.dumpLayerHierarchy(layer, stream, level);
8683 stream.close();
8684 byte[] buf = stream.toByteArray();
8685 out.write(new String(buf, "ascii"));
8686 } catch (IOException e) {}
8687 }
8688 }
8689
8690 @Override
8691 public View findHierarchyView(String className, int hashCode) {
8692 if (mNativeClass == 0) return null;
8693 Picture pic = new Picture();
8694 if (!nativeDumpLayerContentToPicture(mNativeClass, className, hashCode, pic)) {
8695 return null;
8696 }
8697 return new PictureWrapperView(getContext(), pic, mWebView);
8698 }
8699
8700 private static class PictureWrapperView extends View {
8701 Picture mPicture;
8702 WebView mWebView;
8703
8704 public PictureWrapperView(Context context, Picture picture, WebView parent) {
8705 super(context);
8706 mPicture = picture;
8707 mWebView = parent;
8708 setWillNotDraw(false);
8709 setRight(mPicture.getWidth());
8710 setBottom(mPicture.getHeight());
8711 }
8712
8713 @Override
8714 protected void onDraw(Canvas canvas) {
8715 canvas.drawPicture(mPicture);
8716 }
8717
8718 @Override
8719 public boolean post(Runnable action) {
8720 return mWebView.post(action);
8721 }
8722 }
8723
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008724 private native void nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008725 private native void nativeDebugDump();
John Reckd5e29372012-06-07 11:35:35 -07008726 private static native void nativeDestroy(int ptr);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008727
John Reck33b019b2012-04-28 14:35:59 -07008728 private native void nativeDraw(Canvas canvas, RectF visibleRect,
8729 int color, int extra);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008730 private native void nativeDumpDisplayTree(String urlOrNull);
8731 private native boolean nativeEvaluateLayersAnimations(int nativeInstance);
Teng-Hui Zhu508d7052012-05-03 14:31:55 -07008732 private native int nativeCreateDrawGLFunction(int nativeInstance, Rect invScreenRect,
8733 Rect screenRect, RectF visibleContentRect, float scale, int extras);
Romain Guyd4caee02012-04-23 20:44:04 -07008734 private native int nativeGetDrawGLFunction(int nativeInstance);
Teng-Hui Zhu508d7052012-05-03 14:31:55 -07008735 private native void nativeUpdateDrawGLFunction(int nativeInstance, Rect invScreenRect,
8736 Rect screenRect, RectF visibleContentRect, float scale);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008737 private native String nativeGetSelection();
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008738 private native void nativeSetHeightCanMeasure(boolean measure);
8739 private native boolean nativeSetBaseLayer(int nativeInstance,
John Reckec1b71a2012-05-25 14:02:26 -07008740 int layer, boolean showVisualIndicator, boolean isPictureAfterFirstLayout,
8741 int scrollingLayer);
Chris Craikb5dc2152012-05-08 13:44:33 -07008742 private native int nativeGetBaseLayer(int nativeInstance);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008743 private native void nativeCopyBaseContentToPicture(Picture pict);
John Reck926cf562012-06-14 10:00:31 -07008744 private native boolean nativeDumpLayerContentToPicture(int nativeInstance,
8745 String className, int layerId, Picture pict);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008746 private native boolean nativeHasContent();
John Reckd5e29372012-06-07 11:35:35 -07008747 private native void nativeStopGL(int ptr);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008748 private native void nativeDiscardAllTextures();
8749 private native void nativeTileProfilingStart();
8750 private native float nativeTileProfilingStop();
8751 private native void nativeTileProfilingClear();
8752 private native int nativeTileProfilingNumFrames();
8753 private native int nativeTileProfilingNumTilesInFrame(int frame);
8754 private native int nativeTileProfilingGetInt(int frame, int tile, String key);
8755 private native float nativeTileProfilingGetFloat(int frame, int tile, String key);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008756
8757 private native void nativeUseHardwareAccelSkia(boolean enabled);
8758
8759 // Returns a pointer to the scrollable LayerAndroid at the given point.
Chris Craik5f3c08a2012-05-01 15:07:39 -07008760 private native int nativeScrollableLayer(int nativeInstance, int x, int y, Rect scrollRect,
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008761 Rect scrollBounds);
8762 /**
8763 * Scroll the specified layer.
Chris Craik5f3c08a2012-05-01 15:07:39 -07008764 * @param nativeInstance Native WebView instance
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008765 * @param layer Id of the layer to scroll, as determined by nativeScrollableLayer.
8766 * @param newX Destination x position to which to scroll.
8767 * @param newY Destination y position to which to scroll.
8768 * @return True if the layer is successfully scrolled.
8769 */
Chris Craik5f3c08a2012-05-01 15:07:39 -07008770 private native boolean nativeScrollLayer(int nativeInstance, int layer, int newX, int newY);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008771 private native void nativeSetIsScrolling(boolean isScrolling);
Chris Craikb5dc2152012-05-08 13:44:33 -07008772 private native int nativeGetBackgroundColor(int nativeInstance);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008773 native boolean nativeSetProperty(String key, String value);
8774 native String nativeGetProperty(String key);
8775 /**
8776 * See {@link ComponentCallbacks2} for the trim levels and descriptions
8777 */
8778 private static native void nativeOnTrimMemory(int level);
8779 private static native void nativeSetPauseDrawing(int instance, boolean pause);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008780 private static native void nativeSetTextSelection(int instance, int selection);
8781 private static native int nativeGetHandleLayerId(int instance, int handle,
George Mountf9c1f992012-03-21 16:06:10 -07008782 Point cursorLocation, QuadF textQuad);
George Mountbcd5dd72012-03-01 08:39:03 -08008783 private static native void nativeMapLayerRect(int instance, int layerId,
8784 Rect rect);
John Recka5408e62012-03-16 14:18:44 -07008785 // Returns 1 if a layer sync is needed, else 0
8786 private static native int nativeSetHwAccelerated(int instance, boolean hwAccelerated);
George Mount4527a8d2012-05-18 11:18:02 -07008787 private static native void nativeFindMaxVisibleRect(int instance, int layerId,
8788 Rect visibleContentRect);
George Mountb49f2bb2012-06-01 14:29:37 -07008789 private static native boolean nativeIsHandleLeft(int instance, int handleId);
George Mount9b6eb692012-06-05 09:19:51 -07008790 private static native boolean nativeIsPointVisible(int instance,
8791 int layerId, int contentX, int contentY);
Jonathan Dixonded37ed92012-02-13 17:26:46 -08008792}