blob: 58d8ae75eec583bf2bfbde0d02b7425aa2f28e06 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.content.Context;
20import android.graphics.Canvas;
21import android.graphics.DrawFilter;
22import android.graphics.Paint;
23import android.graphics.PaintFlagsDrawFilter;
24import android.graphics.Picture;
25import android.graphics.Point;
26import android.graphics.Rect;
27import android.graphics.Region;
28import android.os.Handler;
29import android.os.Looper;
30import android.os.Message;
31import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.util.Log;
33import android.util.SparseBooleanArray;
34import android.view.KeyEvent;
35
36import java.util.ArrayList;
37import java.util.HashMap;
38
39import junit.framework.Assert;
40
41final class WebViewCore {
42
43 private static final String LOGTAG = "webcore";
44 static final boolean DEBUG = false;
Dave Bort42bc2ff2009-04-13 15:07:51 -070045 static final boolean LOGV_ENABLED = DEBUG;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046
47 static {
48 // Load libwebcore during static initialization. This happens in the
49 // zygote process so it will be shared read-only across all app
50 // processes.
51 System.loadLibrary("webcore");
52 }
53
54 /*
55 * WebViewCore always executes in the same thread as the native webkit.
56 */
57
58 // The WebView that corresponds to this WebViewCore.
59 private WebView mWebView;
60 // Proxy for handling callbacks from native code
61 private final CallbackProxy mCallbackProxy;
62 // Settings object for maintaining all settings
63 private final WebSettings mSettings;
64 // Context for initializing the BrowserFrame with the proper assets.
65 private final Context mContext;
66 // The pointer to a native view object.
67 private int mNativeClass;
68 // The BrowserFrame is an interface to the native Frame component.
69 private BrowserFrame mBrowserFrame;
70
71 /*
72 * range is from 200 to 10,000. 0 is a special value means device-width. -1
73 * means undefined.
74 */
75 private int mViewportWidth = -1;
76
77 /*
78 * range is from 200 to 10,000. 0 is a special value means device-height. -1
79 * means undefined.
80 */
81 private int mViewportHeight = -1;
82
83 /*
84 * scale in percent, range is from 1 to 1000. 0 means undefined.
85 */
86 private int mViewportInitialScale = 0;
87
88 /*
89 * scale in percent, range is from 1 to 1000. 0 means undefined.
90 */
91 private int mViewportMinimumScale = 0;
92
93 /*
94 * scale in percent, range is from 1 to 1000. 0 means undefined.
95 */
96 private int mViewportMaximumScale = 0;
97
98 private boolean mViewportUserScalable = true;
99
100 private int mRestoredScale = 100;
101 private int mRestoredX = 0;
102 private int mRestoredY = 0;
103
104 private int mWebkitScrollX = 0;
105 private int mWebkitScrollY = 0;
106
107 // The thread name used to identify the WebCore thread and for use in
108 // debugging other classes that require operation within the WebCore thread.
109 /* package */ static final String THREAD_NAME = "WebViewCoreThread";
110
111 public WebViewCore(Context context, WebView w, CallbackProxy proxy) {
112 // No need to assign this in the WebCore thread.
113 mCallbackProxy = proxy;
114 mWebView = w;
115 // This context object is used to initialize the WebViewCore during
116 // subwindow creation.
117 mContext = context;
118
119 // We need to wait for the initial thread creation before sending
120 // a message to the WebCore thread.
121 // XXX: This is the only time the UI thread will wait for the WebCore
122 // thread!
123 synchronized (WebViewCore.class) {
124 if (sWebCoreHandler == null) {
125 // Create a global thread and start it.
126 Thread t = new Thread(new WebCoreThread());
127 t.setName(THREAD_NAME);
128 t.start();
129 try {
130 WebViewCore.class.wait();
131 } catch (InterruptedException e) {
132 Log.e(LOGTAG, "Caught exception while waiting for thread " +
133 "creation.");
134 Log.e(LOGTAG, Log.getStackTraceString(e));
135 }
136 }
137 }
138 // Create an EventHub to handle messages before and after the thread is
139 // ready.
140 mEventHub = new EventHub();
141 // Create a WebSettings object for maintaining all settings
142 mSettings = new WebSettings(mContext);
143 // The WebIconDatabase needs to be initialized within the UI thread so
144 // just request the instance here.
145 WebIconDatabase.getInstance();
146 // Send a message to initialize the WebViewCore.
147 Message init = sWebCoreHandler.obtainMessage(
148 WebCoreThread.INITIALIZE, this);
149 sWebCoreHandler.sendMessage(init);
150 }
151
152 /* Initialize private data within the WebCore thread.
153 */
154 private void initialize() {
155 /* Initialize our private BrowserFrame class to handle all
156 * frame-related functions. We need to create a new view which
157 * in turn creates a C level FrameView and attaches it to the frame.
158 */
159 mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
160 mSettings);
161 // Sync the native settings and also create the WebCore thread handler.
162 mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
163 // Create the handler and transfer messages for the IconDatabase
164 WebIconDatabase.getInstance().createHandler();
165 // The transferMessages call will transfer all pending messages to the
166 // WebCore thread handler.
167 mEventHub.transferMessages();
168
169 // Send a message back to WebView to tell it that we have set up the
170 // WebCore thread.
171 if (mWebView != null) {
172 Message.obtain(mWebView.mPrivateHandler,
173 WebView.WEBCORE_INITIALIZED_MSG_ID,
174 mNativeClass, 0).sendToTarget();
175 }
176
177 }
178
179 /* Handle the initialization of WebViewCore during subwindow creation. This
180 * method is called from the WebCore thread but it is called before the
181 * INITIALIZE message can be handled.
182 */
183 /* package */ void initializeSubwindow() {
184 // Go ahead and initialize the core components.
185 initialize();
186 // Remove the INITIALIZE method so we don't try to initialize twice.
187 sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this);
188 }
189
190 /* Get the BrowserFrame component. This is used for subwindow creation and
191 * is called only from BrowserFrame in the WebCore thread. */
192 /* package */ BrowserFrame getBrowserFrame() {
193 return mBrowserFrame;
194 }
195
196 //-------------------------------------------------------------------------
197 // Common methods
198 //-------------------------------------------------------------------------
199
200 /**
201 * Causes all timers to pause. This applies to all WebViews in the current
202 * app process.
203 */
204 public static void pauseTimers() {
205 if (BrowserFrame.sJavaBridge == null) {
206 throw new IllegalStateException(
207 "No WebView has been created in this process!");
208 }
209 BrowserFrame.sJavaBridge.pause();
210 }
211
212 /**
213 * Resume all timers. This applies to all WebViews in the current process.
214 */
215 public static void resumeTimers() {
216 if (BrowserFrame.sJavaBridge == null) {
217 throw new IllegalStateException(
218 "No WebView has been created in this process!");
219 }
220 BrowserFrame.sJavaBridge.resume();
221 }
222
223 public WebSettings getSettings() {
224 return mSettings;
225 }
226
227 /**
228 * Invoke a javascript alert.
229 * @param message The message displayed in the alert.
230 */
231 protected void jsAlert(String url, String message) {
232 mCallbackProxy.onJsAlert(url, message);
233 }
234
235 /**
236 * Invoke a javascript confirm dialog.
237 * @param message The message displayed in the dialog.
238 * @return True if the user confirmed or false if the user cancelled.
239 */
240 protected boolean jsConfirm(String url, String message) {
241 return mCallbackProxy.onJsConfirm(url, message);
242 }
243
244 /**
245 * Invoke a javascript prompt dialog.
246 * @param message The message to be displayed in the dialog.
247 * @param defaultValue The default value in the prompt input.
248 * @return The input from the user or null to indicate the user cancelled
249 * the dialog.
250 */
251 protected String jsPrompt(String url, String message, String defaultValue) {
252 return mCallbackProxy.onJsPrompt(url, message, defaultValue);
253 }
254
255 /**
256 * Invoke a javascript before unload dialog.
257 * @param url The url that is requesting the dialog.
258 * @param message The message displayed in the dialog.
259 * @return True if the user confirmed or false if the user cancelled. False
260 * will cancel the navigation.
261 */
262 protected boolean jsUnload(String url, String message) {
263 return mCallbackProxy.onJsBeforeUnload(url, message);
264 }
265
266 //-------------------------------------------------------------------------
267 // JNI methods
268 //-------------------------------------------------------------------------
269
270 static native String nativeFindAddress(String addr);
271
272 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 * Empty the picture set.
274 */
275 private native void nativeClearContent();
276
277 /**
278 * Create a flat picture from the set of pictures.
279 */
280 private native void nativeCopyContentToPicture(Picture picture);
281
282 /**
283 * Draw the picture set with a background color. Returns true
284 * if some individual picture took too long to draw and can be
285 * split into parts. Called from the UI thread.
286 */
287 private native boolean nativeDrawContent(Canvas canvas, int color);
288
289 /**
290 * Redraw a portion of the picture set. The Point wh returns the
291 * width and height of the overall picture.
292 */
293 private native boolean nativeRecordContent(Region invalRegion, Point wh);
294
295 /**
296 * Splits slow parts of the picture set. Called from the webkit
297 * thread after nativeDrawContent returns true.
298 */
299 private native void nativeSplitContent();
300
301 private native boolean nativeKey(int keyCode, int unichar,
302 int repeatCount, boolean isShift, boolean isAlt, boolean isDown);
303
304 private native boolean nativeClick();
305
306 private native void nativeSendListBoxChoices(boolean[] choices, int size);
307
308 private native void nativeSendListBoxChoice(int choice);
309
310 /* Tell webkit what its width and height are, for the purposes
311 of layout/line-breaking. These coordinates are in document space,
312 which is the same as View coords unless we have zoomed the document
313 (see nativeSetZoom).
314 screenWidth is used by layout to wrap column around. If viewport uses
315 fixed size, screenWidth can be different from width with zooming.
316 should this be called nativeSetViewPortSize?
317 */
318 private native void nativeSetSize(int width, int height, int screenWidth,
319 float scale, int realScreenWidth, int screenHeight);
320
321 private native int nativeGetContentMinPrefWidth();
322
323 // Start: functions that deal with text editing
324 private native void nativeReplaceTextfieldText(int frame, int node, int x,
325 int y, int oldStart, int oldEnd, String replace, int newStart,
326 int newEnd);
327
328 private native void passToJs(int frame, int node, int x, int y, int gen,
329 String currentText, int keyCode, int keyValue, boolean down,
330 boolean cap, boolean fn, boolean sym);
331
332 private native void nativeSaveDocumentState(int frame);
333
334 private native void nativeSetFinalFocus(int framePtr, int nodePtr, int x,
335 int y, boolean block);
336
337 private native void nativeSetKitFocus(int moveGeneration,
338 int buildGeneration, int framePtr, int nodePtr, int x, int y,
339 boolean ignoreNullFocus);
340
341 private native String nativeRetrieveHref(int framePtr, int nodePtr);
342
343 private native void nativeTouchUp(int touchGeneration,
344 int buildGeneration, int framePtr, int nodePtr, int x, int y,
345 int size, boolean isClick, boolean retry);
346
347 private native boolean nativeHandleTouchEvent(int action, int x, int y);
348
349 private native void nativeUnblockFocus();
350
351 private native void nativeUpdateFrameCache();
352
353 private native void nativeSetSnapAnchor(int x, int y);
354
355 private native void nativeSnapToAnchor();
356
357 private native void nativeSetBackgroundColor(int color);
358
359 private native void nativeDumpDomTree(boolean useFile);
360
361 private native void nativeDumpRenderTree(boolean useFile);
362
363 private native void nativeDumpNavTree();
364
365 private native void nativeRefreshPlugins(boolean reloadOpenPages);
366
367 /**
368 * Delete text from start to end in the focused textfield. If there is no
369 * focus, or if start == end, silently fail. If start and end are out of
370 * order, swap them.
371 * @param start Beginning of selection to delete.
372 * @param end End of selection to delete.
373 */
374 private native void nativeDeleteSelection(int frame, int node, int x, int y,
375 int start, int end);
376
377 /**
378 * Set the selection to (start, end) in the focused textfield. If start and
379 * end are out of order, swap them.
380 * @param start Beginning of selection.
381 * @param end End of selection.
382 */
383 private native void nativeSetSelection(int frame, int node, int x, int y,
384 int start, int end);
385
386 private native String nativeGetSelection(Region sel);
387
388 // Register a scheme to be treated as local scheme so that it can access
389 // local asset files for resources
390 private native void nativeRegisterURLSchemeAsLocal(String scheme);
391
392 // EventHub for processing messages
393 private final EventHub mEventHub;
394 // WebCore thread handler
395 private static Handler sWebCoreHandler;
396 // Class for providing Handler creation inside the WebCore thread.
397 private static class WebCoreThread implements Runnable {
398 // Message id for initializing a new WebViewCore.
399 private static final int INITIALIZE = 0;
400 private static final int REDUCE_PRIORITY = 1;
401 private static final int RESUME_PRIORITY = 2;
402 private static final int CACHE_TICKER = 3;
403 private static final int BLOCK_CACHE_TICKER = 4;
404 private static final int RESUME_CACHE_TICKER = 5;
405
406 private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute
407
408 private static boolean mCacheTickersBlocked = true;
409
410 public void run() {
411 Looper.prepare();
412 Assert.assertNull(sWebCoreHandler);
413 synchronized (WebViewCore.class) {
414 sWebCoreHandler = new Handler() {
415 @Override
416 public void handleMessage(Message msg) {
417 switch (msg.what) {
418 case INITIALIZE:
419 WebViewCore core = (WebViewCore) msg.obj;
420 core.initialize();
421 break;
422
423 case REDUCE_PRIORITY:
424 // 3 is an adjustable number.
425 Process.setThreadPriority(
426 Process.THREAD_PRIORITY_DEFAULT + 3 *
427 Process.THREAD_PRIORITY_LESS_FAVORABLE);
428 break;
429
430 case RESUME_PRIORITY:
431 Process.setThreadPriority(
432 Process.THREAD_PRIORITY_DEFAULT);
433 break;
434
435 case CACHE_TICKER:
436 if (!mCacheTickersBlocked) {
437 CacheManager.endCacheTransaction();
438 CacheManager.startCacheTransaction();
439 sendMessageDelayed(
440 obtainMessage(CACHE_TICKER),
441 CACHE_TICKER_INTERVAL);
442 }
443 break;
444
445 case BLOCK_CACHE_TICKER:
446 if (CacheManager.endCacheTransaction()) {
447 mCacheTickersBlocked = true;
448 }
449 break;
450
451 case RESUME_CACHE_TICKER:
452 if (CacheManager.startCacheTransaction()) {
453 mCacheTickersBlocked = false;
454 }
455 break;
456 }
457 }
458 };
459 WebViewCore.class.notify();
460 }
461 Looper.loop();
462 }
463 }
464
465 static class FocusData {
466 FocusData() {}
467 FocusData(FocusData d) {
468 mMoveGeneration = d.mMoveGeneration;
469 mBuildGeneration = d.mBuildGeneration;
470 mFrame = d.mFrame;
471 mNode = d.mNode;
472 mX = d.mX;
473 mY = d.mY;
474 mIgnoreNullFocus = d.mIgnoreNullFocus;
475 }
476 int mMoveGeneration;
477 int mBuildGeneration;
478 int mFrame;
479 int mNode;
480 int mX;
481 int mY;
482 boolean mIgnoreNullFocus;
483 }
484
485 static class TouchUpData {
486 int mMoveGeneration;
487 int mBuildGeneration;
488 int mFrame;
489 int mNode;
490 int mX;
491 int mY;
492 int mSize;
493 boolean mIsClick;
494 boolean mRetry;
495 }
496
497 static class TouchEventData {
498 int mAction; // MotionEvent.getAction()
499 int mX;
500 int mY;
501 }
502
503 static final String[] HandlerDebugString = {
504 "LOAD_URL", // = 100;
505 "STOP_LOADING", // = 101;
506 "RELOAD", // = 102;
507 "KEY_DOWN", // = 103;
508 "KEY_UP", // = 104;
509 "VIEW_SIZE_CHANGED", // = 105;
510 "GO_BACK_FORWARD", // = 106;
511 "SET_SCROLL_OFFSET", // = 107;
512 "RESTORE_STATE", // = 108;
513 "PAUSE_TIMERS", // = 109;
514 "RESUME_TIMERS", // = 110;
515 "CLEAR_CACHE", // = 111;
516 "CLEAR_HISTORY", // = 112;
517 "SET_SELECTION", // = 113;
518 "REPLACE_TEXT", // = 114;
519 "PASS_TO_JS", // = 115;
520 "SET_GLOBAL_BOUNDS", // = 116;
521 "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117;
522 "CLICK", // = 118;
Grace Klobac6c318d2009-04-01 09:44:18 -0700523 "SET_NETWORK_STATE", // = 119;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 "DOC_HAS_IMAGES", // = 120;
525 "SET_SNAP_ANCHOR", // = 121;
526 "DELETE_SELECTION", // = 122;
527 "LISTBOX_CHOICES", // = 123;
528 "SINGLE_LISTBOX_CHOICE", // = 124;
529 "125",
530 "SET_BACKGROUND_COLOR", // = 126;
531 "UNBLOCK_FOCUS", // = 127;
532 "SAVE_DOCUMENT_STATE", // = 128;
533 "GET_SELECTION", // = 129;
534 "WEBKIT_DRAW", // = 130;
535 "SYNC_SCROLL", // = 131;
536 "REFRESH_PLUGINS", // = 132;
537 "SPLIT_PICTURE_SET", // = 133;
538 "CLEAR_CONTENT", // = 134;
539 "SET_FINAL_FOCUS", // = 135;
540 "SET_KIT_FOCUS", // = 136;
541 "REQUEST_FOCUS_HREF", // = 137;
542 "ADD_JS_INTERFACE", // = 138;
543 "LOAD_DATA", // = 139;
544 "TOUCH_UP", // = 140;
545 "TOUCH_EVENT", // = 141;
546 };
547
548 class EventHub {
549 // Message Ids
550 static final int LOAD_URL = 100;
551 static final int STOP_LOADING = 101;
552 static final int RELOAD = 102;
553 static final int KEY_DOWN = 103;
554 static final int KEY_UP = 104;
555 static final int VIEW_SIZE_CHANGED = 105;
556 static final int GO_BACK_FORWARD = 106;
557 static final int SET_SCROLL_OFFSET = 107;
558 static final int RESTORE_STATE = 108;
559 static final int PAUSE_TIMERS = 109;
560 static final int RESUME_TIMERS = 110;
561 static final int CLEAR_CACHE = 111;
562 static final int CLEAR_HISTORY = 112;
563 static final int SET_SELECTION = 113;
564 static final int REPLACE_TEXT = 114;
565 static final int PASS_TO_JS = 115;
566 static final int SET_GLOBAL_BOUNDS = 116;
567 static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
568 static final int CLICK = 118;
Grace Klobac6c318d2009-04-01 09:44:18 -0700569 static final int SET_NETWORK_STATE = 119;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 static final int DOC_HAS_IMAGES = 120;
571 static final int SET_SNAP_ANCHOR = 121;
572 static final int DELETE_SELECTION = 122;
573 static final int LISTBOX_CHOICES = 123;
574 static final int SINGLE_LISTBOX_CHOICE = 124;
575 static final int SET_BACKGROUND_COLOR = 126;
576 static final int UNBLOCK_FOCUS = 127;
577 static final int SAVE_DOCUMENT_STATE = 128;
578 static final int GET_SELECTION = 129;
579 static final int WEBKIT_DRAW = 130;
580 static final int SYNC_SCROLL = 131;
581 static final int REFRESH_PLUGINS = 132;
582 static final int SPLIT_PICTURE_SET = 133;
583 static final int CLEAR_CONTENT = 134;
584
585 // UI nav messages
586 static final int SET_FINAL_FOCUS = 135;
587 static final int SET_KIT_FOCUS = 136;
588 static final int REQUEST_FOCUS_HREF = 137;
589 static final int ADD_JS_INTERFACE = 138;
590 static final int LOAD_DATA = 139;
591
592 // motion
593 static final int TOUCH_UP = 140;
594 // message used to pass UI touch events to WebCore
595 static final int TOUCH_EVENT = 141;
596
597 // Network-based messaging
598 static final int CLEAR_SSL_PREF_TABLE = 150;
599
600 // Test harness messages
601 static final int REQUEST_EXT_REPRESENTATION = 160;
602 static final int REQUEST_DOC_AS_TEXT = 161;
603
604 // debugging
605 static final int DUMP_DOMTREE = 170;
606 static final int DUMP_RENDERTREE = 171;
607 static final int DUMP_NAVTREE = 172;
608
609 // private message ids
610 private static final int DESTROY = 200;
611
612 // flag values passed to message SET_FINAL_FOCUS
613 static final int NO_FOCUS_CHANGE_BLOCK = 0;
614 static final int BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP = 1;
615
616 // Private handler for WebCore messages.
617 private Handler mHandler;
618 // Message queue for containing messages before the WebCore thread is
619 // ready.
620 private ArrayList<Message> mMessages = new ArrayList<Message>();
621 // Flag for blocking messages. This is used during DESTROY to avoid
622 // posting more messages to the EventHub or to WebView's event handler.
623 private boolean mBlockMessages;
624
625 private int mTid;
626 private int mSavedPriority;
627
628 /**
629 * Prevent other classes from creating an EventHub.
630 */
631 private EventHub() {}
632
633 /**
634 * Transfer all messages to the newly created webcore thread handler.
635 */
636 private void transferMessages() {
637 mTid = Process.myTid();
638 mSavedPriority = Process.getThreadPriority(mTid);
639
640 mHandler = new Handler() {
641 @Override
642 public void handleMessage(Message msg) {
643 if (LOGV_ENABLED) {
644 Log.v(LOGTAG, msg.what < LOAD_URL || msg.what
645 > TOUCH_EVENT ? Integer.toString(msg.what)
646 : HandlerDebugString[msg.what - LOAD_URL]);
647 }
648 switch (msg.what) {
649 case WEBKIT_DRAW:
650 webkitDraw();
651 break;
652
653 case DESTROY:
654 // Time to take down the world. Cancel all pending
655 // loads and destroy the native view and frame.
656 mBrowserFrame.destroy();
657 mBrowserFrame = null;
658 mNativeClass = 0;
659 break;
660
661 case LOAD_URL:
662 loadUrl((String) msg.obj);
663 break;
664
665 case LOAD_DATA:
666 HashMap loadParams = (HashMap) msg.obj;
667 String baseUrl = (String) loadParams.get("baseUrl");
668 if (baseUrl != null) {
669 int i = baseUrl.indexOf(':');
670 if (i > 0) {
671 /*
672 * In 1.0, {@link
673 * WebView#loadDataWithBaseURL} can access
674 * local asset files as long as the data is
675 * valid. In the new WebKit, the restriction
676 * is tightened. To be compatible with 1.0,
677 * we automatically add the scheme of the
678 * baseUrl for local access as long as it is
679 * not http(s)/ftp(s)/about/javascript
680 */
681 String scheme = baseUrl.substring(0, i);
682 if (!scheme.startsWith("http") &&
683 !scheme.startsWith("ftp") &&
684 !scheme.startsWith("about") &&
685 !scheme.startsWith("javascript")) {
686 nativeRegisterURLSchemeAsLocal(scheme);
687 }
688 }
689 }
690 mBrowserFrame.loadData(baseUrl,
691 (String) loadParams.get("data"),
692 (String) loadParams.get("mimeType"),
693 (String) loadParams.get("encoding"),
694 (String) loadParams.get("failUrl"));
695 break;
696
697 case STOP_LOADING:
698 // If the WebCore has committed the load, but not
699 // finished the first layout yet, we need to set
700 // first layout done to trigger the interpreted side sync
701 // up with native side
702 if (mBrowserFrame.committed()
703 && !mBrowserFrame.firstLayoutDone()) {
704 mBrowserFrame.didFirstLayout();
705 }
706 // Do this after syncing up the layout state.
707 stopLoading();
708 break;
709
710 case RELOAD:
711 mBrowserFrame.reload(false);
712 break;
713
714 case KEY_DOWN:
715 key((KeyEvent) msg.obj, true);
716 break;
717
718 case KEY_UP:
719 key((KeyEvent) msg.obj, false);
720 break;
721
722 case CLICK:
723 nativeClick();
724 break;
725
726 case VIEW_SIZE_CHANGED:
727 viewSizeChanged(msg.arg1, msg.arg2,
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700728 ((Float) msg.obj).floatValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 break;
730
731 case SET_SCROLL_OFFSET:
732 // note: these are in document coordinates
733 // (inv-zoom)
734 nativeSetScrollOffset(msg.arg1, msg.arg2);
735 break;
736
737 case SET_GLOBAL_BOUNDS:
738 Rect r = (Rect) msg.obj;
739 nativeSetGlobalBounds(r.left, r.top, r.width(),
740 r.height());
741 break;
742
743 case GO_BACK_FORWARD:
744 // If it is a standard load and the load is not
745 // committed yet, we interpret BACK as RELOAD
746 if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
747 (mBrowserFrame.loadType() ==
748 BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
749 mBrowserFrame.reload(true);
750 } else {
751 mBrowserFrame.goBackOrForward(msg.arg1);
752 }
753 break;
754
755 case RESTORE_STATE:
756 stopLoading();
757 restoreState(msg.arg1);
758 break;
759
760 case PAUSE_TIMERS:
761 mSavedPriority = Process.getThreadPriority(mTid);
762 Process.setThreadPriority(mTid,
763 Process.THREAD_PRIORITY_BACKGROUND);
764 pauseTimers();
765 if (CacheManager.disableTransaction()) {
766 WebCoreThread.mCacheTickersBlocked = true;
767 sWebCoreHandler.removeMessages(
768 WebCoreThread.CACHE_TICKER);
769 }
770 break;
771
772 case RESUME_TIMERS:
773 Process.setThreadPriority(mTid, mSavedPriority);
774 resumeTimers();
775 if (CacheManager.enableTransaction()) {
776 WebCoreThread.mCacheTickersBlocked = false;
777 sWebCoreHandler.sendMessageDelayed(
778 sWebCoreHandler.obtainMessage(
779 WebCoreThread.CACHE_TICKER),
780 WebCoreThread.CACHE_TICKER_INTERVAL);
781 }
782 break;
783
Grace Klobac6c318d2009-04-01 09:44:18 -0700784 case SET_NETWORK_STATE:
785 if (BrowserFrame.sJavaBridge == null) {
786 throw new IllegalStateException("No WebView " +
787 "has been created in this process!");
788 }
789 BrowserFrame.sJavaBridge
790 .setNetworkOnLine(msg.arg1 == 1);
791 break;
792
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 case CLEAR_CACHE:
794 mBrowserFrame.clearCache();
795 if (msg.arg1 == 1) {
796 CacheManager.removeAllCacheFiles();
797 }
798 break;
799
800 case CLEAR_HISTORY:
801 mCallbackProxy.getBackForwardList().
802 close(mBrowserFrame.mNativeFrame);
803 break;
804
805 case REPLACE_TEXT:
806 HashMap jMap = (HashMap) msg.obj;
807 FocusData fData = (FocusData) jMap.get("focusData");
808 String replace = (String) jMap.get("replace");
809 int newStart =
810 ((Integer) jMap.get("start")).intValue();
811 int newEnd =
812 ((Integer) jMap.get("end")).intValue();
813 nativeReplaceTextfieldText(fData.mFrame,
814 fData.mNode, fData.mX, fData.mY, msg.arg1,
815 msg.arg2, replace, newStart, newEnd);
816 break;
817
818 case PASS_TO_JS: {
819 HashMap jsMap = (HashMap) msg.obj;
820 FocusData fDat = (FocusData) jsMap.get("focusData");
821 KeyEvent evt = (KeyEvent) jsMap.get("event");
822 int keyCode = evt.getKeyCode();
823 int keyValue = evt.getUnicodeChar();
824 int generation = msg.arg1;
825 passToJs(fDat.mFrame, fDat.mNode, fDat.mX, fDat.mY,
826 generation,
827 (String) jsMap.get("currentText"),
828 keyCode,
829 keyValue,
830 evt.isDown(),
831 evt.isShiftPressed(), evt.isAltPressed(),
832 evt.isSymPressed());
833 break;
834 }
835
836 case SAVE_DOCUMENT_STATE: {
837 FocusData fDat = (FocusData) msg.obj;
838 nativeSaveDocumentState(fDat.mFrame);
839 break;
840 }
841
842 case CLEAR_SSL_PREF_TABLE:
843 Network.getInstance(mContext)
844 .clearUserSslPrefTable();
845 break;
846
847 case TOUCH_UP:
848 TouchUpData touchUpData = (TouchUpData) msg.obj;
849 nativeTouchUp(touchUpData.mMoveGeneration,
850 touchUpData.mBuildGeneration,
851 touchUpData.mFrame, touchUpData.mNode,
852 touchUpData.mX, touchUpData.mY,
853 touchUpData.mSize, touchUpData.mIsClick,
854 touchUpData.mRetry);
855 break;
856
857 case TOUCH_EVENT: {
858 TouchEventData ted = (TouchEventData) msg.obj;
859 Message.obtain(
860 mWebView.mPrivateHandler,
861 WebView.PREVENT_TOUCH_ID, ted.mAction,
862 nativeHandleTouchEvent(ted.mAction, ted.mX,
863 ted.mY) ? 1 : 0).sendToTarget();
864 break;
865 }
866
867 case ADD_JS_INTERFACE:
868 HashMap map = (HashMap) msg.obj;
869 Object obj = map.get("object");
870 String interfaceName = (String)
871 map.get("interfaceName");
872 mBrowserFrame.addJavascriptInterface(obj,
873 interfaceName);
874 break;
875
876 case REQUEST_EXT_REPRESENTATION:
877 mBrowserFrame.externalRepresentation(
878 (Message) msg.obj);
879 break;
880
881 case REQUEST_DOC_AS_TEXT:
882 mBrowserFrame.documentAsText((Message) msg.obj);
883 break;
884
885 case SET_FINAL_FOCUS:
886 FocusData finalData = (FocusData) msg.obj;
887 nativeSetFinalFocus(finalData.mFrame,
888 finalData.mNode, finalData.mX,
889 finalData.mY, msg.arg1
890 != EventHub.NO_FOCUS_CHANGE_BLOCK);
891 break;
892
893 case UNBLOCK_FOCUS:
894 nativeUnblockFocus();
895 break;
896
897 case SET_KIT_FOCUS:
898 FocusData focusData = (FocusData) msg.obj;
899 nativeSetKitFocus(focusData.mMoveGeneration,
900 focusData.mBuildGeneration,
901 focusData.mFrame, focusData.mNode,
902 focusData.mX, focusData.mY,
903 focusData.mIgnoreNullFocus);
904 break;
905
906 case REQUEST_FOCUS_HREF: {
907 Message hrefMsg = (Message) msg.obj;
908 String res = nativeRetrieveHref(msg.arg1, msg.arg2);
909 hrefMsg.getData().putString("url", res);
910 hrefMsg.sendToTarget();
911 break;
912 }
913
914 case UPDATE_CACHE_AND_TEXT_ENTRY:
915 nativeUpdateFrameCache();
916 // FIXME: this should provide a minimal rectangle
917 if (mWebView != null) {
918 mWebView.postInvalidate();
919 }
920 sendUpdateTextEntry();
921 break;
922
923 case DOC_HAS_IMAGES:
924 Message imageResult = (Message) msg.obj;
925 imageResult.arg1 =
926 mBrowserFrame.documentHasImages() ? 1 : 0;
927 imageResult.sendToTarget();
928 break;
929
930 case SET_SNAP_ANCHOR:
931 nativeSetSnapAnchor(msg.arg1, msg.arg2);
932 break;
933
934 case DELETE_SELECTION:
935 FocusData delData = (FocusData) msg.obj;
936 nativeDeleteSelection(delData.mFrame,
937 delData.mNode, delData.mX,
938 delData.mY, msg.arg1, msg.arg2);
939 break;
940
941 case SET_SELECTION:
942 FocusData selData = (FocusData) msg.obj;
943 nativeSetSelection(selData.mFrame,
944 selData.mNode, selData.mX,
945 selData.mY, msg.arg1, msg.arg2);
946 break;
947
948 case LISTBOX_CHOICES:
949 SparseBooleanArray choices = (SparseBooleanArray)
950 msg.obj;
951 int choicesSize = msg.arg1;
952 boolean[] choicesArray = new boolean[choicesSize];
953 for (int c = 0; c < choicesSize; c++) {
954 choicesArray[c] = choices.get(c);
955 }
956 nativeSendListBoxChoices(choicesArray,
957 choicesSize);
958 break;
959
960 case SINGLE_LISTBOX_CHOICE:
961 nativeSendListBoxChoice(msg.arg1);
962 break;
963
964 case SET_BACKGROUND_COLOR:
965 nativeSetBackgroundColor(msg.arg1);
966 break;
967
968 case GET_SELECTION:
969 String str = nativeGetSelection((Region) msg.obj);
970 Message.obtain(mWebView.mPrivateHandler
971 , WebView.UPDATE_CLIPBOARD, str)
972 .sendToTarget();
973 break;
974
975 case DUMP_DOMTREE:
976 nativeDumpDomTree(msg.arg1 == 1);
977 break;
978
979 case DUMP_RENDERTREE:
980 nativeDumpRenderTree(msg.arg1 == 1);
981 break;
982
983 case DUMP_NAVTREE:
984 nativeDumpNavTree();
985 break;
986
987 case SYNC_SCROLL:
988 mWebkitScrollX = msg.arg1;
989 mWebkitScrollY = msg.arg2;
990 break;
991
992 case REFRESH_PLUGINS:
993 nativeRefreshPlugins(msg.arg1 != 0);
994 break;
995
996 case SPLIT_PICTURE_SET:
997 nativeSplitContent();
998 mSplitPictureIsScheduled = false;
999 break;
1000
1001 case CLEAR_CONTENT:
1002 // Clear the view so that onDraw() will draw nothing
1003 // but white background
1004 // (See public method WebView.clearView)
1005 nativeClearContent();
1006 break;
1007 }
1008 }
1009 };
1010 // Take all queued messages and resend them to the new handler.
1011 synchronized (this) {
1012 int size = mMessages.size();
1013 for (int i = 0; i < size; i++) {
1014 mHandler.sendMessage(mMessages.get(i));
1015 }
1016 mMessages = null;
1017 }
1018 }
1019
1020 /**
1021 * Send a message internally to the queue or to the handler
1022 */
1023 private synchronized void sendMessage(Message msg) {
1024 if (mBlockMessages) {
1025 return;
1026 }
1027 if (mMessages != null) {
1028 mMessages.add(msg);
1029 } else {
1030 mHandler.sendMessage(msg);
1031 }
1032 }
1033
1034 private synchronized void removeMessages(int what) {
1035 if (mBlockMessages) {
1036 return;
1037 }
1038 if (what == EventHub.WEBKIT_DRAW) {
1039 mDrawIsScheduled = false;
1040 }
1041 if (mMessages != null) {
1042 Log.w(LOGTAG, "Not supported in this case.");
1043 } else {
1044 mHandler.removeMessages(what);
1045 }
1046 }
1047
1048 private synchronized void sendMessageDelayed(Message msg, long delay) {
1049 if (mBlockMessages) {
1050 return;
1051 }
1052 mHandler.sendMessageDelayed(msg, delay);
1053 }
1054
1055 /**
1056 * Send a message internally to the front of the queue.
1057 */
1058 private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1059 if (mBlockMessages) {
1060 return;
1061 }
1062 if (mMessages != null) {
1063 mMessages.add(0, msg);
1064 } else {
1065 mHandler.sendMessageAtFrontOfQueue(msg);
1066 }
1067 }
1068
1069 /**
1070 * Remove all the messages.
1071 */
1072 private synchronized void removeMessages() {
1073 // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1074 mDrawIsScheduled = false;
1075 mSplitPictureIsScheduled = false;
1076 if (mMessages != null) {
1077 mMessages.clear();
1078 } else {
1079 mHandler.removeCallbacksAndMessages(null);
1080 }
1081 }
1082
1083 /**
1084 * Block sending messages to the EventHub.
1085 */
1086 private synchronized void blockMessages() {
1087 mBlockMessages = true;
1088 }
1089 }
1090
1091 //-------------------------------------------------------------------------
1092 // Methods called by host activity (in the same thread)
1093 //-------------------------------------------------------------------------
1094
1095 void stopLoading() {
1096 if (LOGV_ENABLED) Log.v(LOGTAG, "CORE stopLoading");
1097 if (mBrowserFrame != null) {
1098 mBrowserFrame.stopLoading();
1099 }
1100 }
1101
1102 //-------------------------------------------------------------------------
1103 // Methods called by WebView
1104 // If it refers to local variable, it needs synchronized().
1105 // If it needs WebCore, it has to send message.
1106 //-------------------------------------------------------------------------
1107
1108 void sendMessage(Message msg) {
1109 mEventHub.sendMessage(msg);
1110 }
1111
1112 void sendMessage(int what) {
1113 mEventHub.sendMessage(Message.obtain(null, what));
1114 }
1115
1116 void sendMessage(int what, Object obj) {
1117 mEventHub.sendMessage(Message.obtain(null, what, obj));
1118 }
1119
1120 void sendMessage(int what, int arg1) {
1121 // just ignore the second argument (make it 0)
1122 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1123 }
1124
1125 void sendMessage(int what, int arg1, int arg2) {
1126 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1127 }
1128
1129 void sendMessage(int what, int arg1, Object obj) {
1130 // just ignore the second argument (make it 0)
1131 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1132 }
1133
1134 void sendMessage(int what, int arg1, int arg2, Object obj) {
1135 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1136 }
1137
1138 void sendMessageDelayed(int what, Object obj, long delay) {
1139 mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1140 }
1141
1142 void removeMessages(int what) {
1143 mEventHub.removeMessages(what);
1144 }
1145
1146 void removeMessages() {
1147 mEventHub.removeMessages();
1148 }
1149
1150 /**
1151 * Removes pending messages and trigger a DESTROY message to send to
1152 * WebCore.
1153 * Called from UI thread.
1154 */
1155 void destroy() {
1156 // We don't want anyone to post a message between removing pending
1157 // messages and sending the destroy message.
1158 synchronized (mEventHub) {
1159 mEventHub.removeMessages();
1160 mEventHub.sendMessageAtFrontOfQueue(
1161 Message.obtain(null, EventHub.DESTROY));
1162 mEventHub.blockMessages();
1163 mWebView = null;
1164 }
1165 }
1166
1167 //-------------------------------------------------------------------------
1168 // WebViewCore private methods
1169 //-------------------------------------------------------------------------
1170
1171 private void loadUrl(String url) {
1172 if (LOGV_ENABLED) Log.v(LOGTAG, " CORE loadUrl " + url);
1173 mBrowserFrame.loadUrl(url);
1174 }
1175
1176 private void key(KeyEvent evt, boolean isDown) {
1177 if (LOGV_ENABLED) {
1178 Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1179 + evt);
1180 }
1181 if (!nativeKey(evt.getKeyCode(), evt.getUnicodeChar(),
1182 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1183 isDown)) {
1184 // bubble up the event handling
1185 mCallbackProxy.onUnhandledKeyEvent(evt);
1186 }
1187 }
1188
1189 // These values are used to avoid requesting a layout based on old values
1190 private int mCurrentViewWidth = 0;
1191 private int mCurrentViewHeight = 0;
1192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193 // notify webkit that our virtual view size changed size (after inv-zoom)
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07001194 private void viewSizeChanged(int w, int h, float scale) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 if (LOGV_ENABLED) Log.v(LOGTAG, "CORE onSizeChanged");
1196 if (w == 0) {
1197 Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1198 return;
1199 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 if (mSettings.getUseWideViewPort()
1201 && (w < mViewportWidth || mViewportWidth == -1)) {
1202 int width = mViewportWidth;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001203 if (mViewportWidth == -1) {
1204 if (mSettings.getLayoutAlgorithm() ==
1205 WebSettings.LayoutAlgorithm.NORMAL) {
1206 width = WebView.ZOOM_OUT_WIDTH;
1207 } else {
1208 /*
1209 * if a page's minimum preferred width is wider than the
1210 * given "w", use it instead to get better layout result. If
1211 * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1212 * wider. If we start a page with screen width, due to the
1213 * delay between {@link #didFirstLayout} and
1214 * {@link #viewSizeChanged},
1215 * {@link #nativeGetContentMinPrefWidth} will return a more
1216 * accurate value than initial 0 to result a better layout.
1217 * In the worse case, the native width will be adjusted when
1218 * next zoom or screen orientation change happens.
1219 */
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07001220 width = Math.max(w, nativeGetContentMinPrefWidth());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001221 }
1222 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07001223 nativeSetSize(width, Math.round((float) width * h / w), w, scale,
1224 w, h);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225 } else {
1226 nativeSetSize(w, h, w, scale, w, h);
1227 }
1228 // Remember the current width and height
1229 boolean needInvalidate = (mCurrentViewWidth == 0);
1230 mCurrentViewWidth = w;
1231 mCurrentViewHeight = h;
1232 if (needInvalidate) {
1233 // ensure {@link #webkitDraw} is called as we were blocking in
1234 // {@link #contentDraw} when mCurrentViewWidth is 0
1235 if (LOGV_ENABLED) Log.v(LOGTAG, "viewSizeChanged");
1236 contentDraw();
1237 }
1238 mEventHub.sendMessage(Message.obtain(null,
1239 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1240 }
1241
1242 private void sendUpdateTextEntry() {
1243 if (mWebView != null) {
1244 Message.obtain(mWebView.mPrivateHandler,
1245 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1246 }
1247 }
1248
1249 // Used to avoid posting more than one draw message.
1250 private boolean mDrawIsScheduled;
1251
1252 // Used to avoid posting more than one split picture message.
1253 private boolean mSplitPictureIsScheduled;
1254
1255 // Used to suspend drawing.
1256 private boolean mDrawIsPaused;
1257
1258 // Used to end scale+scroll mode, accessed by both threads
1259 boolean mEndScaleZoom = false;
1260
1261 public class DrawData {
1262 public DrawData() {
1263 mInvalRegion = new Region();
1264 mWidthHeight = new Point();
1265 }
1266 public Region mInvalRegion;
1267 public Point mViewPoint;
1268 public Point mWidthHeight;
1269 }
1270
1271 private void webkitDraw() {
1272 mDrawIsScheduled = false;
1273 DrawData draw = new DrawData();
1274 if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw start");
1275 if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1276 == false) {
1277 if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw abort");
1278 return;
1279 }
1280 if (mWebView != null) {
1281 // Send the native view size that was used during the most recent
1282 // layout.
1283 draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1284 if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1285 Message.obtain(mWebView.mPrivateHandler,
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07001286 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1288 // as we have the new picture, try to sync the scroll position
1289 Message.obtain(mWebView.mPrivateHandler,
1290 WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1291 mWebkitScrollY).sendToTarget();
1292 mWebkitScrollX = mWebkitScrollY = 0;
1293 }
1294 // nativeSnapToAnchor() needs to be called after NEW_PICTURE_MSG_ID
1295 // is sent, so that scroll will be based on the new content size.
1296 nativeSnapToAnchor();
1297 }
1298 }
1299
1300 ///////////////////////////////////////////////////////////////////////////
1301 // These are called from the UI thread, not our thread
1302
1303 static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1304 Paint.DITHER_FLAG |
1305 Paint.SUBPIXEL_TEXT_FLAG;
1306 static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1307 Paint.DITHER_FLAG;
1308
1309 final DrawFilter mZoomFilter =
1310 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1311 final DrawFilter mScrollFilter =
1312 new PaintFlagsDrawFilter(SCROLL_BITS, 0);
1313
1314 /* package */ void drawContentPicture(Canvas canvas, int color,
1315 boolean animatingZoom,
1316 boolean animatingScroll) {
1317 DrawFilter df = null;
1318 if (animatingZoom) {
1319 df = mZoomFilter;
1320 } else if (animatingScroll) {
1321 df = mScrollFilter;
1322 }
1323 canvas.setDrawFilter(df);
1324 boolean tookTooLong = nativeDrawContent(canvas, color);
1325 canvas.setDrawFilter(null);
1326 if (tookTooLong && mSplitPictureIsScheduled == false) {
1327 mSplitPictureIsScheduled = true;
1328 sendMessage(EventHub.SPLIT_PICTURE_SET);
1329 }
1330 }
1331
1332 /*package*/ Picture copyContentPicture() {
1333 Picture result = new Picture();
1334 nativeCopyContentToPicture(result);
1335 return result;
1336 }
1337
1338 static void pauseUpdate(WebViewCore core) {
1339 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1340 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1341 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1342 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1343 .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1344 // Note: there is one possible failure mode. If pauseUpdate() is called
1345 // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
1346 // of the queue and about to be executed. mDrawIsScheduled may be set to
1347 // false in webkitDraw(). So update won't be blocked. But at least the
1348 // webcore thread priority is still lowered.
1349 if (core != null) {
1350 synchronized (core) {
1351 core.mDrawIsPaused = true;
1352 core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1353 }
1354 }
1355 }
1356
1357 static void resumeUpdate(WebViewCore core) {
1358 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1359 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1360 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1361 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1362 .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1363 if (core != null) {
1364 synchronized (core) {
1365 core.mDrawIsScheduled = false;
1366 core.mDrawIsPaused = false;
1367 if (LOGV_ENABLED) Log.v(LOGTAG, "resumeUpdate");
1368 core.contentDraw();
1369 }
1370 }
1371 }
1372
1373 static void startCacheTransaction() {
1374 sWebCoreHandler.sendMessage(sWebCoreHandler
1375 .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
1376 }
1377
1378 static void endCacheTransaction() {
1379 sWebCoreHandler.sendMessage(sWebCoreHandler
1380 .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
1381 }
1382
1383 //////////////////////////////////////////////////////////////////////////
1384
1385 private void restoreState(int index) {
1386 WebBackForwardList list = mCallbackProxy.getBackForwardList();
1387 int size = list.getSize();
1388 for (int i = 0; i < size; i++) {
1389 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1390 }
1391 mBrowserFrame.mLoadInitFromJava = true;
1392 list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1393 mBrowserFrame.mLoadInitFromJava = false;
1394 }
1395
1396 //-------------------------------------------------------------------------
1397 // Implement abstract methods in WebViewCore, native WebKit callback part
1398 //-------------------------------------------------------------------------
1399
1400 // called from JNI or WebView thread
1401 /* package */ void contentDraw() {
1402 // don't update the Picture until we have an initial width and finish
1403 // the first layout
1404 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1405 return;
1406 }
1407 // only fire an event if this is our first request
1408 synchronized (this) {
1409 if (mDrawIsPaused || mDrawIsScheduled) {
1410 return;
1411 }
1412 mDrawIsScheduled = true;
1413 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1414 }
1415 }
1416
1417 // called by JNI
1418 private void contentScrollBy(int dx, int dy, boolean animate) {
1419 if (!mBrowserFrame.firstLayoutDone()) {
1420 // Will this happen? If yes, we need to do something here.
1421 return;
1422 }
1423 if (mWebView != null) {
1424 Message.obtain(mWebView.mPrivateHandler,
1425 WebView.SCROLL_BY_MSG_ID, dx, dy,
1426 new Boolean(animate)).sendToTarget();
1427 }
1428 }
1429
1430 // called by JNI
1431 private void contentScrollTo(int x, int y) {
1432 if (!mBrowserFrame.firstLayoutDone()) {
1433 /*
1434 * WebKit restore state will be called before didFirstLayout(),
1435 * remember the position as it has to be applied after restoring
1436 * zoom factor which is controlled by screenWidth.
1437 */
1438 mRestoredX = x;
1439 mRestoredY = y;
1440 return;
1441 }
1442 if (mWebView != null) {
1443 Message.obtain(mWebView.mPrivateHandler,
1444 WebView.SCROLL_TO_MSG_ID, x, y).sendToTarget();
1445 }
1446 }
1447
1448 // called by JNI
1449 private void contentSpawnScrollTo(int x, int y) {
1450 if (!mBrowserFrame.firstLayoutDone()) {
1451 /*
1452 * WebKit restore state will be called before didFirstLayout(),
1453 * remember the position as it has to be applied after restoring
1454 * zoom factor which is controlled by screenWidth.
1455 */
1456 mRestoredX = x;
1457 mRestoredY = y;
1458 return;
1459 }
1460 if (mWebView != null) {
1461 Message.obtain(mWebView.mPrivateHandler,
1462 WebView.SPAWN_SCROLL_TO_MSG_ID, x, y).sendToTarget();
1463 }
1464 }
1465
1466 // called by JNI
1467 private void sendMarkNodeInvalid(int node) {
1468 if (mWebView != null) {
1469 Message.obtain(mWebView.mPrivateHandler,
1470 WebView.MARK_NODE_INVALID_ID, node, 0).sendToTarget();
1471 }
1472 }
1473
1474 // called by JNI
1475 private void sendNotifyFocusSet() {
1476 if (mWebView != null) {
1477 Message.obtain(mWebView.mPrivateHandler,
1478 WebView.NOTIFY_FOCUS_SET_MSG_ID).sendToTarget();
1479 }
1480 }
1481
1482 // called by JNI
1483 private void sendNotifyProgressFinished() {
1484 sendUpdateTextEntry();
1485 // as CacheManager can behave based on database transaction, we need to
1486 // call tick() to trigger endTransaction
1487 sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
1488 sWebCoreHandler.sendMessage(sWebCoreHandler
1489 .obtainMessage(WebCoreThread.CACHE_TICKER));
1490 contentDraw();
1491 }
1492
1493 // called by JNI
1494 private void sendRecomputeFocus() {
1495 if (mWebView != null) {
1496 Message.obtain(mWebView.mPrivateHandler,
1497 WebView.RECOMPUTE_FOCUS_MSG_ID).sendToTarget();
1498 }
1499 }
1500
1501 /* Called by JNI. The coordinates are in doc coordinates, so they need to
1502 be scaled before they can be used by the view system, which happens
1503 in WebView since it (and its thread) know the current scale factor.
1504 */
1505 private void sendViewInvalidate(int left, int top, int right, int bottom) {
1506 if (mWebView != null) {
1507 Message.obtain(mWebView.mPrivateHandler,
1508 WebView.INVAL_RECT_MSG_ID,
1509 new Rect(left, top, right, bottom)).sendToTarget();
1510 }
1511 }
1512
1513 /* package */ WebView getWebView() {
1514 return mWebView;
1515 }
1516
1517 private native void setViewportSettingsFromNative();
1518
1519 // called by JNI
1520 private void didFirstLayout() {
1521 // Trick to ensure that the Picture has the exact height for the content
1522 // by forcing to layout with 0 height after the page is ready, which is
1523 // indicated by didFirstLayout. This is essential to get rid of the
1524 // white space in the GMail which uses WebView for message view.
1525 if (mWebView != null && mWebView.mHeightCanMeasure) {
1526 mWebView.mLastHeightSent = 0;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07001527 // Send a negative scale to indicate that WebCore should reuse the
1528 // current scale
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001529 mEventHub.sendMessage(Message.obtain(null,
1530 EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent,
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07001531 mWebView.mLastHeightSent, -1.0f));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 }
1533
1534 mBrowserFrame.didFirstLayout();
1535
1536 // reset the scroll position as it is a new page now
1537 mWebkitScrollX = mWebkitScrollY = 0;
1538
1539 // set the viewport settings from WebKit
1540 setViewportSettingsFromNative();
1541
1542 // infer the values if they are not defined.
1543 if (mViewportWidth == 0) {
1544 if (mViewportInitialScale == 0) {
1545 mViewportInitialScale = 100;
1546 }
1547 if (mViewportMinimumScale == 0) {
1548 mViewportMinimumScale = 100;
1549 }
1550 }
1551 if (mViewportUserScalable == false) {
1552 mViewportInitialScale = 100;
1553 mViewportMinimumScale = 100;
1554 mViewportMaximumScale = 100;
1555 }
1556 if (mViewportMinimumScale > mViewportInitialScale) {
1557 if (mViewportInitialScale == 0) {
1558 mViewportInitialScale = mViewportMinimumScale;
1559 } else {
1560 mViewportMinimumScale = mViewportInitialScale;
1561 }
1562 }
1563 if (mViewportMaximumScale > 0) {
1564 if (mViewportMaximumScale < mViewportInitialScale) {
1565 mViewportMaximumScale = mViewportInitialScale;
1566 } else if (mViewportInitialScale == 0) {
1567 mViewportInitialScale = mViewportMaximumScale;
1568 }
1569 }
1570 if (mViewportWidth < 0 && mViewportInitialScale == 100) {
1571 mViewportWidth = 0;
1572 }
1573
1574 // now notify webview
1575 if (mWebView != null) {
1576 HashMap scaleLimit = new HashMap();
1577 scaleLimit.put("minScale", mViewportMinimumScale);
1578 scaleLimit.put("maxScale", mViewportMaximumScale);
1579
1580 if (mRestoredScale > 0) {
1581 Message.obtain(mWebView.mPrivateHandler,
1582 WebView.DID_FIRST_LAYOUT_MSG_ID, mRestoredScale, 0,
1583 scaleLimit).sendToTarget();
1584 mRestoredScale = 0;
1585 } else {
1586 Message.obtain(mWebView.mPrivateHandler,
1587 WebView.DID_FIRST_LAYOUT_MSG_ID, mViewportInitialScale,
1588 mViewportWidth, scaleLimit).sendToTarget();
1589 }
1590
1591 // if no restored offset, move the new page to (0, 0)
1592 Message.obtain(mWebView.mPrivateHandler, WebView.SCROLL_TO_MSG_ID,
1593 mRestoredX, mRestoredY).sendToTarget();
1594 mRestoredX = mRestoredY = 0;
1595
1596 // force an early draw for quick feedback after the first layout
1597 if (mCurrentViewWidth != 0) {
1598 synchronized (this) {
1599 if (mDrawIsScheduled) {
1600 mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1601 }
1602 mDrawIsScheduled = true;
1603 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1604 EventHub.WEBKIT_DRAW));
1605 }
1606 }
1607 }
1608 }
1609
1610 // called by JNI
1611 private void restoreScale(int scale) {
1612 if (mBrowserFrame.firstLayoutDone() == false) {
1613 mRestoredScale = scale;
1614 }
1615 }
1616
1617 // called by JNI
1618 private void needTouchEvents(boolean need) {
1619 if (mWebView != null) {
1620 Message.obtain(mWebView.mPrivateHandler,
1621 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
1622 .sendToTarget();
1623 }
1624 }
1625
1626 // called by JNI
1627 private void updateTextfield(int ptr, boolean changeToPassword,
1628 String text, int textGeneration) {
1629 if (mWebView != null) {
1630 Message msg = Message.obtain(mWebView.mPrivateHandler,
1631 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
1632 textGeneration, text);
1633 msg.getData().putBoolean("password", changeToPassword);
1634 msg.sendToTarget();
1635 }
1636 }
1637
1638 // these must be in document space (i.e. not scaled/zoomed).
1639 private native void nativeSetScrollOffset(int dx, int dy);
1640
1641 private native void nativeSetGlobalBounds(int x, int y, int w, int h);
1642
1643 // called by JNI
1644 private void requestListBox(String[] array, boolean[] enabledArray,
1645 int[] selectedArray) {
1646 if (mWebView != null) {
1647 mWebView.requestListBox(array, enabledArray, selectedArray);
1648 }
1649 }
1650
1651 // called by JNI
1652 private void requestListBox(String[] array, boolean[] enabledArray,
1653 int selection) {
1654 if (mWebView != null) {
1655 mWebView.requestListBox(array, enabledArray, selection);
1656 }
1657
1658 }
1659}