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