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