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