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