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