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