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