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