1 /* 2 * Copyright (C) 2006 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 17 package android.webkit; 18 19 import android.app.ActivityManager; 20 import android.content.Context; 21 import android.content.res.AssetManager; 22 import android.graphics.Bitmap; 23 import android.net.ParseException; 24 import android.net.WebAddress; 25 import android.net.http.SslCertificate; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.util.Log; 29 import android.util.TypedValue; 30 31 import junit.framework.Assert; 32 33 import java.net.URLEncoder; 34 import java.util.HashMap; 35 import java.util.Map; 36 import java.util.Iterator; 37 38 class BrowserFrame extends Handler { 39 40 private static final String LOGTAG = "webkit"; 41 42 /** 43 * Cap the number of LoadListeners that will be instantiated, so 44 * we don't blow the GREF count. Attempting to queue more than 45 * this many requests will prompt an error() callback on the 46 * request's LoadListener 47 */ 48 private final static int MAX_OUTSTANDING_REQUESTS = 300; 49 50 private final CallbackProxy mCallbackProxy; 51 private final WebSettings mSettings; 52 private final Context mContext; 53 private final WebViewDatabase mDatabase; 54 private final WebViewCore mWebViewCore; 55 /* package */ boolean mLoadInitFromJava; 56 private int mLoadType; 57 private boolean mFirstLayoutDone = true; 58 private boolean mCommitted = true; 59 60 // Is this frame the main frame? 61 private boolean mIsMainFrame; 62 63 // Attached Javascript interfaces 64 private Map<String, Object> mJSInterfaceMap; 65 66 // message ids 67 // a message posted when a frame loading is completed 68 static final int FRAME_COMPLETED = 1001; 69 // a message posted when the user decides the policy 70 static final int POLICY_FUNCTION = 1003; 71 72 // Note: need to keep these in sync with FrameLoaderTypes.h in native 73 static final int FRAME_LOADTYPE_STANDARD = 0; 74 static final int FRAME_LOADTYPE_BACK = 1; 75 static final int FRAME_LOADTYPE_FORWARD = 2; 76 static final int FRAME_LOADTYPE_INDEXEDBACKFORWARD = 3; 77 static final int FRAME_LOADTYPE_RELOAD = 4; 78 static final int FRAME_LOADTYPE_RELOADALLOWINGSTALEDATA = 5; 79 static final int FRAME_LOADTYPE_SAME = 6; 80 static final int FRAME_LOADTYPE_REDIRECT = 7; 81 static final int FRAME_LOADTYPE_REPLACE = 8; 82 83 // A progress threshold to switch from history Picture to live Picture 84 private static final int TRANSITION_SWITCH_THRESHOLD = 75; 85 86 // This is a field accessed by native code as well as package classes. 87 /*package*/ int mNativeFrame; 88 89 // Static instance of a JWebCoreJavaBridge to handle timer and cookie 90 // requests from WebCore. 91 static JWebCoreJavaBridge sJavaBridge; 92 93 /** 94 * Create a new BrowserFrame to be used in an application. 95 * @param context An application context to use when retrieving assets. 96 * @param w A WebViewCore used as the view for this frame. 97 * @param proxy A CallbackProxy for posting messages to the UI thread and 98 * querying a client for information. 99 * @param settings A WebSettings object that holds all settings. 100 * XXX: Called by WebCore thread. 101 */ BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy, WebSettings settings, Map<String, Object> javascriptInterfaces)102 public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy, 103 WebSettings settings, Map<String, Object> javascriptInterfaces) { 104 // Create a global JWebCoreJavaBridge to handle timers and 105 // cookies in the WebCore thread. 106 if (sJavaBridge == null) { 107 sJavaBridge = new JWebCoreJavaBridge(context); 108 // set WebCore native cache size 109 ActivityManager am = (ActivityManager) context 110 .getSystemService(Context.ACTIVITY_SERVICE); 111 if (am.getMemoryClass() > 16) { 112 sJavaBridge.setCacheSize(8 * 1024 * 1024); 113 } else { 114 sJavaBridge.setCacheSize(4 * 1024 * 1024); 115 } 116 // initialize CacheManager 117 CacheManager.init(context); 118 // create CookieSyncManager with current Context 119 CookieSyncManager.createInstance(context); 120 // create PluginManager with current Context 121 PluginManager.getInstance(context); 122 } 123 mJSInterfaceMap = javascriptInterfaces; 124 125 mSettings = settings; 126 mContext = context; 127 mCallbackProxy = proxy; 128 mDatabase = WebViewDatabase.getInstance(context); 129 mWebViewCore = w; 130 131 AssetManager am = context.getAssets(); 132 nativeCreateFrame(w, am, proxy.getBackForwardList()); 133 134 if (DebugFlags.BROWSER_FRAME) { 135 Log.v(LOGTAG, "BrowserFrame constructor: this=" + this); 136 } 137 } 138 139 /** 140 * Load a url from the network or the filesystem into the main frame. 141 * Following the same behaviour as Safari, javascript: URLs are not 142 * passed to the main frame, instead they are evaluated immediately. 143 * @param url The url to load. 144 */ loadUrl(String url)145 public void loadUrl(String url) { 146 mLoadInitFromJava = true; 147 if (URLUtil.isJavaScriptUrl(url)) { 148 // strip off the scheme and evaluate the string 149 stringByEvaluatingJavaScriptFromString( 150 url.substring("javascript:".length())); 151 } else { 152 nativeLoadUrl(url); 153 } 154 mLoadInitFromJava = false; 155 } 156 157 /** 158 * Load a url with "POST" method from the network into the main frame. 159 * @param url The url to load. 160 * @param data The data for POST request. 161 */ postUrl(String url, byte[] data)162 public void postUrl(String url, byte[] data) { 163 mLoadInitFromJava = true; 164 nativePostUrl(url, data); 165 mLoadInitFromJava = false; 166 } 167 168 /** 169 * Load the content as if it was loaded by the provided base URL. The 170 * failUrl is used as the history entry for the load data. If null or 171 * an empty string is passed for the failUrl, then no history entry is 172 * created. 173 * 174 * @param baseUrl Base URL used to resolve relative paths in the content 175 * @param data Content to render in the browser 176 * @param mimeType Mimetype of the data being passed in 177 * @param encoding Character set encoding of the provided data. 178 * @param failUrl URL to use if the content fails to load or null. 179 */ loadData(String baseUrl, String data, String mimeType, String encoding, String failUrl)180 public void loadData(String baseUrl, String data, String mimeType, 181 String encoding, String failUrl) { 182 mLoadInitFromJava = true; 183 if (failUrl == null) { 184 failUrl = ""; 185 } 186 if (data == null) { 187 data = ""; 188 } 189 190 // Setup defaults for missing values. These defaults where taken from 191 // WebKit's WebFrame.mm 192 if (baseUrl == null || baseUrl.length() == 0) { 193 baseUrl = "about:blank"; 194 } 195 if (mimeType == null || mimeType.length() == 0) { 196 mimeType = "text/html"; 197 } 198 nativeLoadData(baseUrl, data, mimeType, encoding, failUrl); 199 mLoadInitFromJava = false; 200 } 201 202 /** 203 * Go back or forward the number of steps given. 204 * @param steps A negative or positive number indicating the direction 205 * and number of steps to move. 206 */ goBackOrForward(int steps)207 public void goBackOrForward(int steps) { 208 mLoadInitFromJava = true; 209 nativeGoBackOrForward(steps); 210 mLoadInitFromJava = false; 211 } 212 213 /** 214 * native callback 215 * Report an error to an activity. 216 * @param errorCode The HTTP error code. 217 * @param description A String description. 218 * TODO: Report all errors including resource errors but include some kind 219 * of domain identifier. Change errorCode to an enum for a cleaner 220 * interface. 221 */ reportError(final int errorCode, final String description, final String failingUrl)222 private void reportError(final int errorCode, final String description, 223 final String failingUrl) { 224 // As this is called for the main resource and loading will be stopped 225 // after, reset the state variables. 226 resetLoadingStates(); 227 mCallbackProxy.onReceivedError(errorCode, description, failingUrl); 228 } 229 resetLoadingStates()230 private void resetLoadingStates() { 231 mCommitted = true; 232 mFirstLayoutDone = true; 233 } 234 committed()235 /* package */boolean committed() { 236 return mCommitted; 237 } 238 firstLayoutDone()239 /* package */boolean firstLayoutDone() { 240 return mFirstLayoutDone; 241 } 242 loadType()243 /* package */int loadType() { 244 return mLoadType; 245 } 246 didFirstLayout()247 /* package */void didFirstLayout() { 248 if (!mFirstLayoutDone) { 249 mFirstLayoutDone = true; 250 // ensure {@link WebViewCore#webkitDraw} is called as we were 251 // blocking the update in {@link #loadStarted} 252 mWebViewCore.contentDraw(); 253 } 254 } 255 256 /** 257 * native callback 258 * Indicates the beginning of a new load. 259 * This method will be called once for the main frame. 260 */ loadStarted(String url, Bitmap favicon, int loadType, boolean isMainFrame)261 private void loadStarted(String url, Bitmap favicon, int loadType, 262 boolean isMainFrame) { 263 mIsMainFrame = isMainFrame; 264 265 if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) { 266 mLoadType = loadType; 267 268 if (isMainFrame) { 269 // Call onPageStarted for main frames. 270 mCallbackProxy.onPageStarted(url, favicon); 271 // as didFirstLayout() is only called for the main frame, reset 272 // mFirstLayoutDone only for the main frames 273 mFirstLayoutDone = false; 274 mCommitted = false; 275 // remove pending draw to block update until mFirstLayoutDone is 276 // set to true in didFirstLayout() 277 mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW); 278 } 279 280 // Note: only saves committed form data in standard load 281 if (loadType == FRAME_LOADTYPE_STANDARD 282 && mSettings.getSaveFormData()) { 283 final WebHistoryItem h = mCallbackProxy.getBackForwardList() 284 .getCurrentItem(); 285 if (h != null) { 286 String currentUrl = h.getUrl(); 287 if (currentUrl != null) { 288 mDatabase.setFormData(currentUrl, getFormTextData()); 289 } 290 } 291 } 292 } 293 } 294 295 /** 296 * native callback 297 * Indicates the WebKit has committed to the new load 298 */ transitionToCommitted(int loadType, boolean isMainFrame)299 private void transitionToCommitted(int loadType, boolean isMainFrame) { 300 // loadType is not used yet 301 if (isMainFrame) { 302 mCommitted = true; 303 } 304 } 305 306 /** 307 * native callback 308 * <p> 309 * Indicates the end of a new load. 310 * This method will be called once for the main frame. 311 */ loadFinished(String url, int loadType, boolean isMainFrame)312 private void loadFinished(String url, int loadType, boolean isMainFrame) { 313 // mIsMainFrame and isMainFrame are better be equal!!! 314 315 if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) { 316 if (isMainFrame) { 317 resetLoadingStates(); 318 mCallbackProxy.switchOutDrawHistory(); 319 mCallbackProxy.onPageFinished(url); 320 } 321 } 322 } 323 324 /** 325 * We have received an SSL certificate for the main top-level page. 326 * 327 * !!!Called from the network thread!!! 328 */ certificate(SslCertificate certificate)329 void certificate(SslCertificate certificate) { 330 if (mIsMainFrame) { 331 // we want to make this call even if the certificate is null 332 // (ie, the site is not secure) 333 mCallbackProxy.onReceivedCertificate(certificate); 334 } 335 } 336 337 /** 338 * Destroy all native components of the BrowserFrame. 339 */ destroy()340 public void destroy() { 341 nativeDestroyFrame(); 342 removeCallbacksAndMessages(null); 343 } 344 345 /** 346 * Handle messages posted to us. 347 * @param msg The message to handle. 348 */ 349 @Override handleMessage(Message msg)350 public void handleMessage(Message msg) { 351 switch (msg.what) { 352 case FRAME_COMPLETED: { 353 if (mSettings.getSavePassword() && hasPasswordField()) { 354 WebHistoryItem item = mCallbackProxy.getBackForwardList() 355 .getCurrentItem(); 356 if (item != null) { 357 WebAddress uri = new WebAddress(item.getUrl()); 358 String schemePlusHost = uri.mScheme + uri.mHost; 359 String[] up = 360 mDatabase.getUsernamePassword(schemePlusHost); 361 if (up != null && up[0] != null) { 362 setUsernamePassword(up[0], up[1]); 363 } 364 } 365 } 366 CacheManager.trimCacheIfNeeded(); 367 break; 368 } 369 370 case POLICY_FUNCTION: { 371 nativeCallPolicyFunction(msg.arg1, msg.arg2); 372 break; 373 } 374 375 default: 376 break; 377 } 378 } 379 380 /** 381 * Punch-through for WebCore to set the document 382 * title. Inform the Activity of the new title. 383 * @param title The new title of the document. 384 */ setTitle(String title)385 private void setTitle(String title) { 386 // FIXME: The activity must call getTitle (a native method) to get the 387 // title. We should try and cache the title if we can also keep it in 388 // sync with the document. 389 mCallbackProxy.onReceivedTitle(title); 390 } 391 392 /** 393 * Retrieves the render tree of this frame and puts it as the object for 394 * the message and sends the message. 395 * @param callback the message to use to send the render tree 396 */ externalRepresentation(Message callback)397 public void externalRepresentation(Message callback) { 398 callback.obj = externalRepresentation();; 399 callback.sendToTarget(); 400 } 401 402 /** 403 * Return the render tree as a string 404 */ externalRepresentation()405 private native String externalRepresentation(); 406 407 /** 408 * Retrieves the visual text of the current frame, puts it as the object for 409 * the message and sends the message. 410 * @param callback the message to use to send the visual text 411 */ documentAsText(Message callback)412 public void documentAsText(Message callback) { 413 callback.obj = documentAsText();; 414 callback.sendToTarget(); 415 } 416 417 /** 418 * Return the text drawn on the screen as a string 419 */ documentAsText()420 private native String documentAsText(); 421 422 /* 423 * This method is called by WebCore to inform the frame that 424 * the Javascript window object has been cleared. 425 * We should re-attach any attached js interfaces. 426 */ windowObjectCleared(int nativeFramePointer)427 private void windowObjectCleared(int nativeFramePointer) { 428 if (mJSInterfaceMap != null) { 429 Iterator iter = mJSInterfaceMap.keySet().iterator(); 430 while (iter.hasNext()) { 431 String interfaceName = (String) iter.next(); 432 nativeAddJavascriptInterface(nativeFramePointer, 433 mJSInterfaceMap.get(interfaceName), interfaceName); 434 } 435 } 436 } 437 438 /** 439 * This method is called by WebCore to check whether application 440 * wants to hijack url loading 441 */ handleUrl(String url)442 public boolean handleUrl(String url) { 443 if (mLoadInitFromJava == true) { 444 return false; 445 } 446 if (mCallbackProxy.shouldOverrideUrlLoading(url)) { 447 // if the url is hijacked, reset the state of the BrowserFrame 448 didFirstLayout(); 449 return true; 450 } else { 451 return false; 452 } 453 } 454 addJavascriptInterface(Object obj, String interfaceName)455 public void addJavascriptInterface(Object obj, String interfaceName) { 456 if (mJSInterfaceMap == null) { 457 mJSInterfaceMap = new HashMap<String, Object>(); 458 } 459 if (mJSInterfaceMap.containsKey(interfaceName)) { 460 mJSInterfaceMap.remove(interfaceName); 461 } 462 mJSInterfaceMap.put(interfaceName, obj); 463 } 464 465 /** 466 * Start loading a resource. 467 * @param loaderHandle The native ResourceLoader that is the target of the 468 * data. 469 * @param url The url to load. 470 * @param method The http method. 471 * @param headers The http headers. 472 * @param postData If the method is "POST" postData is sent as the request 473 * body. Is null when empty. 474 * @param cacheMode The cache mode to use when loading this resource. 475 * @param synchronous True if the load is synchronous. 476 * @return A newly created LoadListener object. 477 */ startLoadingResource(int loaderHandle, String url, String method, HashMap headers, byte[] postData, int cacheMode, boolean synchronous)478 private LoadListener startLoadingResource(int loaderHandle, 479 String url, 480 String method, 481 HashMap headers, 482 byte[] postData, 483 int cacheMode, 484 boolean synchronous) { 485 PerfChecker checker = new PerfChecker(); 486 487 if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) { 488 cacheMode = mSettings.getCacheMode(); 489 } 490 491 if (method.equals("POST")) { 492 // Don't use the cache on POSTs when issuing a normal POST 493 // request. 494 if (cacheMode == WebSettings.LOAD_NORMAL) { 495 cacheMode = WebSettings.LOAD_NO_CACHE; 496 } 497 if (mSettings.getSavePassword() && hasPasswordField()) { 498 try { 499 if (DebugFlags.BROWSER_FRAME) { 500 Assert.assertNotNull(mCallbackProxy.getBackForwardList() 501 .getCurrentItem()); 502 } 503 WebAddress uri = new WebAddress(mCallbackProxy 504 .getBackForwardList().getCurrentItem().getUrl()); 505 String schemePlusHost = uri.mScheme + uri.mHost; 506 String[] ret = getUsernamePassword(); 507 // Has the user entered a username/password pair and is 508 // there some POST data 509 if (ret != null && postData != null && 510 ret[0].length() > 0 && ret[1].length() > 0) { 511 // Check to see if the username & password appear in 512 // the post data (there could be another form on the 513 // page and that was posted instead. 514 String postString = new String(postData); 515 if (postString.contains(URLEncoder.encode(ret[0])) && 516 postString.contains(URLEncoder.encode(ret[1]))) { 517 String[] saved = mDatabase.getUsernamePassword( 518 schemePlusHost); 519 if (saved != null) { 520 // null username implies that user has chosen not to 521 // save password 522 if (saved[0] != null) { 523 // non-null username implies that user has 524 // chosen to save password, so update the 525 // recorded password 526 mDatabase.setUsernamePassword( 527 schemePlusHost, ret[0], ret[1]); 528 } 529 } else { 530 // CallbackProxy will handle creating the resume 531 // message 532 mCallbackProxy.onSavePassword(schemePlusHost, ret[0], 533 ret[1], null); 534 } 535 } 536 } 537 } catch (ParseException ex) { 538 // if it is bad uri, don't save its password 539 } 540 541 } 542 } 543 544 // is this resource the main-frame top-level page? 545 boolean isMainFramePage = mIsMainFrame; 546 547 if (DebugFlags.BROWSER_FRAME) { 548 Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method=" 549 + method + ", postData=" + postData + ", isMainFramePage=" 550 + isMainFramePage); 551 } 552 553 // Create a LoadListener 554 LoadListener loadListener = LoadListener.getLoadListener(mContext, this, url, 555 loaderHandle, synchronous, isMainFramePage); 556 557 mCallbackProxy.onLoadResource(url); 558 559 if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) { 560 // send an error message, so that loadListener can be deleted 561 // after this is returned. This is important as LoadListener's 562 // nativeError will remove the request from its DocLoader's request 563 // list. But the set up is not done until this method is returned. 564 loadListener.error( 565 android.net.http.EventHandler.ERROR, mContext.getString( 566 com.android.internal.R.string.httpErrorTooManyRequests)); 567 return loadListener; 568 } 569 570 FrameLoader loader = new FrameLoader(loadListener, mSettings, method); 571 loader.setHeaders(headers); 572 loader.setPostData(postData); 573 // Set the load mode to the mode used for the current page. 574 // If WebKit wants validation, go to network directly. 575 loader.setCacheMode(headers.containsKey("If-Modified-Since") 576 || headers.containsKey("If-None-Match") ? 577 WebSettings.LOAD_NO_CACHE : cacheMode); 578 // Set referrer to current URL? 579 if (!loader.executeLoad()) { 580 checker.responseAlert("startLoadingResource fail"); 581 } 582 checker.responseAlert("startLoadingResource succeed"); 583 584 return !synchronous ? loadListener : null; 585 } 586 587 /** 588 * Set the progress for the browser activity. Called by native code. 589 * Uses a delay so it does not happen too often. 590 * @param newProgress An int between zero and one hundred representing 591 * the current progress percentage of loading the page. 592 */ setProgress(int newProgress)593 private void setProgress(int newProgress) { 594 mCallbackProxy.onProgressChanged(newProgress); 595 if (newProgress == 100) { 596 sendMessageDelayed(obtainMessage(FRAME_COMPLETED), 100); 597 } 598 // FIXME: Need to figure out a better way to switch out of the history 599 // drawing mode. Maybe we can somehow compare the history picture with 600 // the current picture, and switch when it contains more content. 601 if (mFirstLayoutDone && newProgress > TRANSITION_SWITCH_THRESHOLD) { 602 mCallbackProxy.switchOutDrawHistory(); 603 } 604 } 605 606 /** 607 * Send the icon to the activity for display. 608 * @param icon A Bitmap representing a page's favicon. 609 */ didReceiveIcon(Bitmap icon)610 private void didReceiveIcon(Bitmap icon) { 611 mCallbackProxy.onReceivedIcon(icon); 612 } 613 614 // Called by JNI when an apple-touch-icon attribute was found. didReceiveTouchIconUrl(String url, boolean precomposed)615 private void didReceiveTouchIconUrl(String url, boolean precomposed) { 616 mCallbackProxy.onReceivedTouchIconUrl(url, precomposed); 617 } 618 619 /** 620 * Request a new window from the client. 621 * @return The BrowserFrame object stored in the new WebView. 622 */ createWindow(boolean dialog, boolean userGesture)623 private BrowserFrame createWindow(boolean dialog, boolean userGesture) { 624 WebView w = mCallbackProxy.createWindow(dialog, userGesture); 625 if (w != null) { 626 return w.getWebViewCore().getBrowserFrame(); 627 } 628 return null; 629 } 630 631 /** 632 * Try to focus this WebView. 633 */ requestFocus()634 private void requestFocus() { 635 mCallbackProxy.onRequestFocus(); 636 } 637 638 /** 639 * Close this frame and window. 640 */ closeWindow(WebViewCore w)641 private void closeWindow(WebViewCore w) { 642 mCallbackProxy.onCloseWindow(w.getWebView()); 643 } 644 645 // XXX: Must match PolicyAction in FrameLoaderTypes.h in webcore 646 static final int POLICY_USE = 0; 647 static final int POLICY_IGNORE = 2; 648 decidePolicyForFormResubmission(int policyFunction)649 private void decidePolicyForFormResubmission(int policyFunction) { 650 Message dontResend = obtainMessage(POLICY_FUNCTION, policyFunction, 651 POLICY_IGNORE); 652 Message resend = obtainMessage(POLICY_FUNCTION, policyFunction, 653 POLICY_USE); 654 mCallbackProxy.onFormResubmission(dontResend, resend); 655 } 656 657 /** 658 * Tell the activity to update its global history. 659 */ updateVisitedHistory(String url, boolean isReload)660 private void updateVisitedHistory(String url, boolean isReload) { 661 mCallbackProxy.doUpdateVisitedHistory(url, isReload); 662 } 663 664 /** 665 * Get the CallbackProxy for sending messages to the UI thread. 666 */ getCallbackProxy()667 /* package */ CallbackProxy getCallbackProxy() { 668 return mCallbackProxy; 669 } 670 671 /** 672 * Returns the User Agent used by this frame 673 */ getUserAgentString()674 String getUserAgentString() { 675 return mSettings.getUserAgentString(); 676 } 677 678 // these ids need to be in sync with enum RAW_RES_ID in WebFrame 679 private static final int NODOMAIN = 1; 680 private static final int LOADERROR = 2; 681 private static final int DRAWABLEDIR = 3; 682 getRawResFilename(int id)683 String getRawResFilename(int id) { 684 int resid; 685 switch (id) { 686 case NODOMAIN: 687 resid = com.android.internal.R.raw.nodomain; 688 break; 689 690 case LOADERROR: 691 resid = com.android.internal.R.raw.loaderror; 692 break; 693 694 case DRAWABLEDIR: 695 // use one known resource to find the drawable directory 696 resid = com.android.internal.R.drawable.btn_check_off; 697 break; 698 699 default: 700 Log.e(LOGTAG, "getRawResFilename got incompatible resource ID"); 701 return ""; 702 } 703 TypedValue value = new TypedValue(); 704 mContext.getResources().getValue(resid, value, true); 705 if (id == DRAWABLEDIR) { 706 String path = value.string.toString(); 707 int index = path.lastIndexOf('/'); 708 if (index < 0) { 709 Log.e(LOGTAG, "Can't find drawable directory."); 710 return ""; 711 } 712 return path.substring(0, index + 1); 713 } 714 return value.string.toString(); 715 } 716 density()717 private float density() { 718 return mContext.getResources().getDisplayMetrics().density; 719 } 720 721 //========================================================================== 722 // native functions 723 //========================================================================== 724 725 /** 726 * Create a new native frame for a given WebView 727 * @param w A WebView that the frame draws into. 728 * @param am AssetManager to use to get assets. 729 * @param list The native side will add and remove items from this list as 730 * the native list changes. 731 */ nativeCreateFrame(WebViewCore w, AssetManager am, WebBackForwardList list)732 private native void nativeCreateFrame(WebViewCore w, AssetManager am, 733 WebBackForwardList list); 734 735 /** 736 * Destroy the native frame. 737 */ nativeDestroyFrame()738 public native void nativeDestroyFrame(); 739 nativeCallPolicyFunction(int policyFunction, int decision)740 private native void nativeCallPolicyFunction(int policyFunction, 741 int decision); 742 743 /** 744 * Reload the current main frame. 745 */ reload(boolean allowStale)746 public native void reload(boolean allowStale); 747 748 /** 749 * Go back or forward the number of steps given. 750 * @param steps A negative or positive number indicating the direction 751 * and number of steps to move. 752 */ nativeGoBackOrForward(int steps)753 private native void nativeGoBackOrForward(int steps); 754 755 /** 756 * stringByEvaluatingJavaScriptFromString will execute the 757 * JS passed in in the context of this browser frame. 758 * @param script A javascript string to execute 759 * 760 * @return string result of execution or null 761 */ stringByEvaluatingJavaScriptFromString(String script)762 public native String stringByEvaluatingJavaScriptFromString(String script); 763 764 /** 765 * Add a javascript interface to the main frame. 766 */ nativeAddJavascriptInterface(int nativeFramePointer, Object obj, String interfaceName)767 private native void nativeAddJavascriptInterface(int nativeFramePointer, 768 Object obj, String interfaceName); 769 770 /** 771 * Enable or disable the native cache. 772 */ 773 /* FIXME: The native cache is always on for now until we have a better 774 * solution for our 2 caches. */ setCacheDisabled(boolean disabled)775 private native void setCacheDisabled(boolean disabled); 776 cacheDisabled()777 public native boolean cacheDisabled(); 778 clearCache()779 public native void clearCache(); 780 781 /** 782 * Returns false if the url is bad. 783 */ nativeLoadUrl(String url)784 private native void nativeLoadUrl(String url); 785 nativePostUrl(String url, byte[] postData)786 private native void nativePostUrl(String url, byte[] postData); 787 nativeLoadData(String baseUrl, String data, String mimeType, String encoding, String failUrl)788 private native void nativeLoadData(String baseUrl, String data, 789 String mimeType, String encoding, String failUrl); 790 791 /** 792 * Stop loading the current page. 793 */ stopLoading()794 public void stopLoading() { 795 if (mIsMainFrame) { 796 resetLoadingStates(); 797 } 798 nativeStopLoading(); 799 } 800 nativeStopLoading()801 private native void nativeStopLoading(); 802 803 /** 804 * Return true if the document has images. 805 */ documentHasImages()806 public native boolean documentHasImages(); 807 808 /** 809 * @return TRUE if there is a password field in the current frame 810 */ hasPasswordField()811 private native boolean hasPasswordField(); 812 813 /** 814 * Get username and password in the current frame. If found, String[0] is 815 * username and String[1] is password. Otherwise return NULL. 816 * @return String[] 817 */ getUsernamePassword()818 private native String[] getUsernamePassword(); 819 820 /** 821 * Set username and password to the proper fields in the current frame 822 * @param username 823 * @param password 824 */ setUsernamePassword(String username, String password)825 private native void setUsernamePassword(String username, String password); 826 827 /** 828 * Get form's "text" type data associated with the current frame. 829 * @return HashMap If succeed, returns a list of name/value pair. Otherwise 830 * returns null. 831 */ getFormTextData()832 private native HashMap getFormTextData(); 833 } 834