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.ComponentCallbacks; 21 import android.content.Context; 22 import android.content.res.AssetManager; 23 import android.content.res.Configuration; 24 import android.content.res.Resources; 25 import android.content.res.Resources.NotFoundException; 26 import android.graphics.Bitmap; 27 import android.net.ParseException; 28 import android.net.Uri; 29 import android.net.WebAddress; 30 import android.net.http.ErrorStrings; 31 import android.net.http.SslCertificate; 32 import android.net.http.SslError; 33 import android.os.Handler; 34 import android.os.Message; 35 import android.util.Log; 36 import android.util.TypedValue; 37 import android.view.Surface; 38 import android.view.ViewRootImpl; 39 import android.view.WindowManager; 40 41 import junit.framework.Assert; 42 43 import java.io.IOException; 44 import java.io.InputStream; 45 import java.lang.ref.WeakReference; 46 import java.net.URLEncoder; 47 import java.nio.charset.Charsets; 48 import java.security.PrivateKey; 49 import java.security.cert.CertificateEncodingException; 50 import java.security.cert.X509Certificate; 51 import java.util.ArrayList; 52 import java.util.HashMap; 53 import java.util.HashSet; 54 import java.util.Iterator; 55 import java.util.Map; 56 import java.util.Set; 57 58 import org.apache.harmony.security.provider.cert.X509CertImpl; 59 60 class BrowserFrame extends Handler { 61 62 private static final String LOGTAG = "webkit"; 63 64 /** 65 * Cap the number of LoadListeners that will be instantiated, so 66 * we don't blow the GREF count. Attempting to queue more than 67 * this many requests will prompt an error() callback on the 68 * request's LoadListener 69 */ 70 private final static int MAX_OUTSTANDING_REQUESTS = 300; 71 72 private final CallbackProxy mCallbackProxy; 73 private final WebSettings mSettings; 74 private final Context mContext; 75 private final WebViewDatabase mDatabase; 76 private final WebViewCore mWebViewCore; 77 /* package */ boolean mLoadInitFromJava; 78 private int mLoadType; 79 private boolean mFirstLayoutDone = true; 80 private boolean mCommitted = true; 81 // Flag for blocking messages. This is used during destroy() so 82 // that if the UI thread posts any messages after the message 83 // queue has been cleared,they are ignored. 84 private boolean mBlockMessages = false; 85 private int mOrientation = -1; 86 87 // Is this frame the main frame? 88 private boolean mIsMainFrame; 89 90 // Attached Javascript interfaces 91 private Map<String, Object> mJavaScriptObjects; 92 private Set<Object> mRemovedJavaScriptObjects; 93 94 // Key store handler when Chromium HTTP stack is used. 95 private KeyStoreHandler mKeyStoreHandler = null; 96 97 // Implementation of the searchbox API. 98 private final SearchBoxImpl mSearchBox; 99 100 // message ids 101 // a message posted when a frame loading is completed 102 static final int FRAME_COMPLETED = 1001; 103 // orientation change message 104 static final int ORIENTATION_CHANGED = 1002; 105 // a message posted when the user decides the policy 106 static final int POLICY_FUNCTION = 1003; 107 108 // Note: need to keep these in sync with FrameLoaderTypes.h in native 109 static final int FRAME_LOADTYPE_STANDARD = 0; 110 static final int FRAME_LOADTYPE_BACK = 1; 111 static final int FRAME_LOADTYPE_FORWARD = 2; 112 static final int FRAME_LOADTYPE_INDEXEDBACKFORWARD = 3; 113 static final int FRAME_LOADTYPE_RELOAD = 4; 114 static final int FRAME_LOADTYPE_RELOADALLOWINGSTALEDATA = 5; 115 static final int FRAME_LOADTYPE_SAME = 6; 116 static final int FRAME_LOADTYPE_REDIRECT = 7; 117 static final int FRAME_LOADTYPE_REPLACE = 8; 118 119 // A progress threshold to switch from history Picture to live Picture 120 private static final int TRANSITION_SWITCH_THRESHOLD = 75; 121 122 // This is a field accessed by native code as well as package classes. 123 /*package*/ int mNativeFrame; 124 125 // Static instance of a JWebCoreJavaBridge to handle timer and cookie 126 // requests from WebCore. 127 static JWebCoreJavaBridge sJavaBridge; 128 129 private static class ConfigCallback implements ComponentCallbacks { 130 private final ArrayList<WeakReference<Handler>> mHandlers = 131 new ArrayList<WeakReference<Handler>>(); 132 private final WindowManager mWindowManager; 133 ConfigCallback(WindowManager wm)134 ConfigCallback(WindowManager wm) { 135 mWindowManager = wm; 136 } 137 addHandler(Handler h)138 public synchronized void addHandler(Handler h) { 139 // No need to ever remove a Handler. If the BrowserFrame is 140 // destroyed, it will be collected and the WeakReference set to 141 // null. If it happens to still be around during a configuration 142 // change, the message will be ignored. 143 mHandlers.add(new WeakReference<Handler>(h)); 144 } 145 onConfigurationChanged(Configuration newConfig)146 public void onConfigurationChanged(Configuration newConfig) { 147 if (mHandlers.size() == 0) { 148 return; 149 } 150 int orientation = 151 mWindowManager.getDefaultDisplay().getOrientation(); 152 switch (orientation) { 153 case Surface.ROTATION_90: 154 orientation = 90; 155 break; 156 case Surface.ROTATION_180: 157 orientation = 180; 158 break; 159 case Surface.ROTATION_270: 160 orientation = -90; 161 break; 162 case Surface.ROTATION_0: 163 orientation = 0; 164 break; 165 default: 166 break; 167 } 168 synchronized (this) { 169 // Create a list of handlers to remove. Go ahead and make it 170 // the same size to avoid resizing. 171 ArrayList<WeakReference> handlersToRemove = 172 new ArrayList<WeakReference>(mHandlers.size()); 173 for (WeakReference<Handler> wh : mHandlers) { 174 Handler h = wh.get(); 175 if (h != null) { 176 h.sendMessage(h.obtainMessage(ORIENTATION_CHANGED, 177 orientation, 0)); 178 } else { 179 handlersToRemove.add(wh); 180 } 181 } 182 // Now remove all the null references. 183 for (WeakReference weak : handlersToRemove) { 184 mHandlers.remove(weak); 185 } 186 } 187 } 188 onLowMemory()189 public void onLowMemory() {} 190 } 191 static ConfigCallback sConfigCallback; 192 193 /** 194 * Create a new BrowserFrame to be used in an application. 195 * @param context An application context to use when retrieving assets. 196 * @param w A WebViewCore used as the view for this frame. 197 * @param proxy A CallbackProxy for posting messages to the UI thread and 198 * querying a client for information. 199 * @param settings A WebSettings object that holds all settings. 200 * XXX: Called by WebCore thread. 201 */ BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy, WebSettings settings, Map<String, Object> javascriptInterfaces)202 public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy, 203 WebSettings settings, Map<String, Object> javascriptInterfaces) { 204 205 Context appContext = context.getApplicationContext(); 206 207 // Create a global JWebCoreJavaBridge to handle timers and 208 // cookies in the WebCore thread. 209 if (sJavaBridge == null) { 210 sJavaBridge = new JWebCoreJavaBridge(); 211 // set WebCore native cache size 212 ActivityManager am = (ActivityManager) context 213 .getSystemService(Context.ACTIVITY_SERVICE); 214 if (am.getMemoryClass() > 16) { 215 sJavaBridge.setCacheSize(8 * 1024 * 1024); 216 } else { 217 sJavaBridge.setCacheSize(4 * 1024 * 1024); 218 } 219 // initialize CacheManager 220 CacheManager.init(appContext); 221 // create CookieSyncManager with current Context 222 CookieSyncManager.createInstance(appContext); 223 // create PluginManager with current Context 224 PluginManager.getInstance(appContext); 225 } 226 227 if (sConfigCallback == null) { 228 sConfigCallback = new ConfigCallback( 229 (WindowManager) appContext.getSystemService( 230 Context.WINDOW_SERVICE)); 231 ViewRootImpl.addConfigCallback(sConfigCallback); 232 } 233 sConfigCallback.addHandler(this); 234 235 mJavaScriptObjects = javascriptInterfaces; 236 if (mJavaScriptObjects == null) { 237 mJavaScriptObjects = new HashMap<String, Object>(); 238 } 239 mRemovedJavaScriptObjects = new HashSet<Object>(); 240 241 mSettings = settings; 242 mContext = context; 243 mCallbackProxy = proxy; 244 mDatabase = WebViewDatabase.getInstance(appContext); 245 mWebViewCore = w; 246 247 mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy); 248 mJavaScriptObjects.put(SearchBoxImpl.JS_INTERFACE_NAME, mSearchBox); 249 250 AssetManager am = context.getAssets(); 251 nativeCreateFrame(w, am, proxy.getBackForwardList()); 252 253 if (DebugFlags.BROWSER_FRAME) { 254 Log.v(LOGTAG, "BrowserFrame constructor: this=" + this); 255 } 256 } 257 258 /** 259 * Load a url from the network or the filesystem into the main frame. 260 * Following the same behaviour as Safari, javascript: URLs are not passed 261 * to the main frame, instead they are evaluated immediately. 262 * @param url The url to load. 263 * @param extraHeaders The extra headers sent with this url. This should not 264 * include the common headers like "user-agent". If it does, it 265 * will be replaced by the intrinsic value of the WebView. 266 */ loadUrl(String url, Map<String, String> extraHeaders)267 public void loadUrl(String url, Map<String, String> extraHeaders) { 268 mLoadInitFromJava = true; 269 if (URLUtil.isJavaScriptUrl(url)) { 270 // strip off the scheme and evaluate the string 271 stringByEvaluatingJavaScriptFromString( 272 url.substring("javascript:".length())); 273 } else { 274 nativeLoadUrl(url, extraHeaders); 275 } 276 mLoadInitFromJava = false; 277 } 278 279 /** 280 * Load a url with "POST" method from the network into the main frame. 281 * @param url The url to load. 282 * @param data The data for POST request. 283 */ postUrl(String url, byte[] data)284 public void postUrl(String url, byte[] data) { 285 mLoadInitFromJava = true; 286 nativePostUrl(url, data); 287 mLoadInitFromJava = false; 288 } 289 290 /** 291 * Load the content as if it was loaded by the provided base URL. The 292 * historyUrl is used as the history entry for the load data. 293 * 294 * @param baseUrl Base URL used to resolve relative paths in the content 295 * @param data Content to render in the browser 296 * @param mimeType Mimetype of the data being passed in 297 * @param encoding Character set encoding of the provided data. 298 * @param historyUrl URL to use as the history entry. 299 */ loadData(String baseUrl, String data, String mimeType, String encoding, String historyUrl)300 public void loadData(String baseUrl, String data, String mimeType, 301 String encoding, String historyUrl) { 302 mLoadInitFromJava = true; 303 if (historyUrl == null || historyUrl.length() == 0) { 304 historyUrl = "about:blank"; 305 } 306 if (data == null) { 307 data = ""; 308 } 309 310 // Setup defaults for missing values. These defaults where taken from 311 // WebKit's WebFrame.mm 312 if (baseUrl == null || baseUrl.length() == 0) { 313 baseUrl = "about:blank"; 314 } 315 if (mimeType == null || mimeType.length() == 0) { 316 mimeType = "text/html"; 317 } 318 nativeLoadData(baseUrl, data, mimeType, encoding, historyUrl); 319 mLoadInitFromJava = false; 320 } 321 322 /** 323 * Saves the contents of the frame as a web archive. 324 * 325 * @param basename The filename where the archive should be placed. 326 * @param autoname If false, takes filename to be a file. If true, filename 327 * is assumed to be a directory in which a filename will be 328 * chosen according to the url of the current page. 329 */ saveWebArchive(String basename, boolean autoname)330 /* package */ String saveWebArchive(String basename, boolean autoname) { 331 return nativeSaveWebArchive(basename, autoname); 332 } 333 334 /** 335 * Go back or forward the number of steps given. 336 * @param steps A negative or positive number indicating the direction 337 * and number of steps to move. 338 */ goBackOrForward(int steps)339 public void goBackOrForward(int steps) { 340 mLoadInitFromJava = true; 341 nativeGoBackOrForward(steps); 342 mLoadInitFromJava = false; 343 } 344 345 /** 346 * native callback 347 * Report an error to an activity. 348 * @param errorCode The HTTP error code. 349 * @param description Optional human-readable description. If no description 350 * is given, we'll use a standard localized error message. 351 * @param failingUrl The URL that was being loaded when the error occurred. 352 * TODO: Report all errors including resource errors but include some kind 353 * of domain identifier. Change errorCode to an enum for a cleaner 354 * interface. 355 */ reportError(int errorCode, String description, String failingUrl)356 private void reportError(int errorCode, String description, String failingUrl) { 357 // As this is called for the main resource and loading will be stopped 358 // after, reset the state variables. 359 resetLoadingStates(); 360 if (description == null || description.isEmpty()) { 361 description = ErrorStrings.getString(errorCode, mContext); 362 } 363 mCallbackProxy.onReceivedError(errorCode, description, failingUrl); 364 } 365 resetLoadingStates()366 private void resetLoadingStates() { 367 mCommitted = true; 368 mFirstLayoutDone = true; 369 } 370 committed()371 /* package */boolean committed() { 372 return mCommitted; 373 } 374 firstLayoutDone()375 /* package */boolean firstLayoutDone() { 376 return mFirstLayoutDone; 377 } 378 loadType()379 /* package */int loadType() { 380 return mLoadType; 381 } 382 didFirstLayout()383 /* package */void didFirstLayout() { 384 if (!mFirstLayoutDone) { 385 mFirstLayoutDone = true; 386 // ensure {@link WebViewCore#webkitDraw} is called as we were 387 // blocking the update in {@link #loadStarted} 388 mWebViewCore.contentDraw(); 389 } 390 } 391 392 /** 393 * native callback 394 * Indicates the beginning of a new load. 395 * This method will be called once for the main frame. 396 */ loadStarted(String url, Bitmap favicon, int loadType, boolean isMainFrame)397 private void loadStarted(String url, Bitmap favicon, int loadType, 398 boolean isMainFrame) { 399 mIsMainFrame = isMainFrame; 400 401 if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) { 402 mLoadType = loadType; 403 404 if (isMainFrame) { 405 // Call onPageStarted for main frames. 406 mCallbackProxy.onPageStarted(url, favicon); 407 // as didFirstLayout() is only called for the main frame, reset 408 // mFirstLayoutDone only for the main frames 409 mFirstLayoutDone = false; 410 mCommitted = false; 411 // remove pending draw to block update until mFirstLayoutDone is 412 // set to true in didFirstLayout() 413 mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW); 414 } 415 } 416 } 417 418 @SuppressWarnings("unused") saveFormData(HashMap<String, String> data)419 private void saveFormData(HashMap<String, String> data) { 420 if (mSettings.getSaveFormData()) { 421 final WebHistoryItem h = mCallbackProxy.getBackForwardList() 422 .getCurrentItem(); 423 if (h != null) { 424 String url = WebTextView.urlForAutoCompleteData(h.getUrl()); 425 if (url != null) { 426 mDatabase.setFormData(url, data); 427 } 428 } 429 } 430 } 431 432 @SuppressWarnings("unused") shouldSaveFormData()433 private boolean shouldSaveFormData() { 434 if (mSettings.getSaveFormData()) { 435 final WebHistoryItem h = mCallbackProxy.getBackForwardList() 436 .getCurrentItem(); 437 return h != null && h.getUrl() != null; 438 } 439 return false; 440 } 441 442 /** 443 * native callback 444 * Indicates the WebKit has committed to the new load 445 */ transitionToCommitted(int loadType, boolean isMainFrame)446 private void transitionToCommitted(int loadType, boolean isMainFrame) { 447 // loadType is not used yet 448 if (isMainFrame) { 449 mCommitted = true; 450 mWebViewCore.getWebView().mViewManager.postResetStateAll(); 451 } 452 } 453 454 /** 455 * native callback 456 * <p> 457 * Indicates the end of a new load. 458 * This method will be called once for the main frame. 459 */ loadFinished(String url, int loadType, boolean isMainFrame)460 private void loadFinished(String url, int loadType, boolean isMainFrame) { 461 // mIsMainFrame and isMainFrame are better be equal!!! 462 463 if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) { 464 if (isMainFrame) { 465 resetLoadingStates(); 466 mCallbackProxy.switchOutDrawHistory(); 467 mCallbackProxy.onPageFinished(url); 468 } 469 } 470 } 471 472 /** 473 * We have received an SSL certificate for the main top-level page. 474 * Used by the Android HTTP stack only. 475 */ certificate(SslCertificate certificate)476 void certificate(SslCertificate certificate) { 477 if (mIsMainFrame) { 478 // we want to make this call even if the certificate is null 479 // (ie, the site is not secure) 480 mCallbackProxy.onReceivedCertificate(certificate); 481 } 482 } 483 484 /** 485 * Destroy all native components of the BrowserFrame. 486 */ destroy()487 public void destroy() { 488 nativeDestroyFrame(); 489 mBlockMessages = true; 490 removeCallbacksAndMessages(null); 491 } 492 493 /** 494 * Handle messages posted to us. 495 * @param msg The message to handle. 496 */ 497 @Override handleMessage(Message msg)498 public void handleMessage(Message msg) { 499 if (mBlockMessages) { 500 return; 501 } 502 switch (msg.what) { 503 case FRAME_COMPLETED: { 504 if (mSettings.getSavePassword() && hasPasswordField()) { 505 WebHistoryItem item = mCallbackProxy.getBackForwardList() 506 .getCurrentItem(); 507 if (item != null) { 508 WebAddress uri = new WebAddress(item.getUrl()); 509 String schemePlusHost = uri.getScheme() + uri.getHost(); 510 String[] up = 511 mDatabase.getUsernamePassword(schemePlusHost); 512 if (up != null && up[0] != null) { 513 setUsernamePassword(up[0], up[1]); 514 } 515 } 516 } 517 if (!JniUtil.useChromiumHttpStack()) { 518 WebViewWorker.getHandler().sendEmptyMessage( 519 WebViewWorker.MSG_TRIM_CACHE); 520 } 521 break; 522 } 523 524 case POLICY_FUNCTION: { 525 nativeCallPolicyFunction(msg.arg1, msg.arg2); 526 break; 527 } 528 529 case ORIENTATION_CHANGED: { 530 if (mOrientation != msg.arg1) { 531 mOrientation = msg.arg1; 532 nativeOrientationChanged(msg.arg1); 533 } 534 break; 535 } 536 537 default: 538 break; 539 } 540 } 541 542 /** 543 * Punch-through for WebCore to set the document 544 * title. Inform the Activity of the new title. 545 * @param title The new title of the document. 546 */ setTitle(String title)547 private void setTitle(String title) { 548 // FIXME: The activity must call getTitle (a native method) to get the 549 // title. We should try and cache the title if we can also keep it in 550 // sync with the document. 551 mCallbackProxy.onReceivedTitle(title); 552 } 553 554 /** 555 * Retrieves the render tree of this frame and puts it as the object for 556 * the message and sends the message. 557 * @param callback the message to use to send the render tree 558 */ externalRepresentation(Message callback)559 public void externalRepresentation(Message callback) { 560 callback.obj = externalRepresentation();; 561 callback.sendToTarget(); 562 } 563 564 /** 565 * Return the render tree as a string 566 */ externalRepresentation()567 private native String externalRepresentation(); 568 569 /** 570 * Retrieves the visual text of the frames, puts it as the object for 571 * the message and sends the message. 572 * @param callback the message to use to send the visual text 573 */ documentAsText(Message callback)574 public void documentAsText(Message callback) { 575 StringBuilder text = new StringBuilder(); 576 if (callback.arg1 != 0) { 577 // Dump top frame as text. 578 text.append(documentAsText()); 579 } 580 if (callback.arg2 != 0) { 581 // Dump child frames as text. 582 text.append(childFramesAsText()); 583 } 584 callback.obj = text.toString(); 585 callback.sendToTarget(); 586 } 587 588 /** 589 * Return the text drawn on the screen as a string 590 */ documentAsText()591 private native String documentAsText(); 592 593 /** 594 * Return the text drawn on the child frames as a string 595 */ childFramesAsText()596 private native String childFramesAsText(); 597 598 /* 599 * This method is called by WebCore to inform the frame that 600 * the Javascript window object has been cleared. 601 * We should re-attach any attached js interfaces. 602 */ windowObjectCleared(int nativeFramePointer)603 private void windowObjectCleared(int nativeFramePointer) { 604 Iterator<String> iter = mJavaScriptObjects.keySet().iterator(); 605 while (iter.hasNext()) { 606 String interfaceName = iter.next(); 607 Object object = mJavaScriptObjects.get(interfaceName); 608 if (object != null) { 609 nativeAddJavascriptInterface(nativeFramePointer, 610 mJavaScriptObjects.get(interfaceName), interfaceName); 611 } 612 } 613 mRemovedJavaScriptObjects.clear(); 614 615 stringByEvaluatingJavaScriptFromString(SearchBoxImpl.JS_BRIDGE); 616 } 617 618 /** 619 * This method is called by WebCore to check whether application 620 * wants to hijack url loading 621 */ handleUrl(String url)622 public boolean handleUrl(String url) { 623 if (mLoadInitFromJava == true) { 624 return false; 625 } 626 if (mCallbackProxy.shouldOverrideUrlLoading(url)) { 627 // if the url is hijacked, reset the state of the BrowserFrame 628 didFirstLayout(); 629 return true; 630 } else { 631 return false; 632 } 633 } 634 addJavascriptInterface(Object obj, String interfaceName)635 public void addJavascriptInterface(Object obj, String interfaceName) { 636 assert obj != null; 637 removeJavascriptInterface(interfaceName); 638 639 mJavaScriptObjects.put(interfaceName, obj); 640 } 641 removeJavascriptInterface(String interfaceName)642 public void removeJavascriptInterface(String interfaceName) { 643 // We keep a reference to the removed object because the native side holds only a weak 644 // reference and we need to allow the object to continue to be used until the page has been 645 // navigated. 646 if (mJavaScriptObjects.containsKey(interfaceName)) { 647 mRemovedJavaScriptObjects.add(mJavaScriptObjects.remove(interfaceName)); 648 } 649 } 650 651 /** 652 * Called by JNI. Given a URI, find the associated file and return its size 653 * @param uri A String representing the URI of the desired file. 654 * @return int The size of the given file. 655 */ getFileSize(String uri)656 private int getFileSize(String uri) { 657 int size = 0; 658 try { 659 InputStream stream = mContext.getContentResolver() 660 .openInputStream(Uri.parse(uri)); 661 size = stream.available(); 662 stream.close(); 663 } catch (Exception e) {} 664 return size; 665 } 666 667 /** 668 * Called by JNI. Given a URI, a buffer, and an offset into the buffer, 669 * copy the resource into buffer. 670 * @param uri A String representing the URI of the desired file. 671 * @param buffer The byte array to copy the data into. 672 * @param offset The offet into buffer to place the data. 673 * @param expectedSize The size that the buffer has allocated for this file. 674 * @return int The size of the given file, or zero if it fails. 675 */ getFile(String uri, byte[] buffer, int offset, int expectedSize)676 private int getFile(String uri, byte[] buffer, int offset, 677 int expectedSize) { 678 int size = 0; 679 try { 680 InputStream stream = mContext.getContentResolver() 681 .openInputStream(Uri.parse(uri)); 682 size = stream.available(); 683 if (size <= expectedSize && buffer != null 684 && buffer.length - offset >= size) { 685 stream.read(buffer, offset, size); 686 } else { 687 size = 0; 688 } 689 stream.close(); 690 } catch (java.io.FileNotFoundException e) { 691 Log.e(LOGTAG, "FileNotFoundException:" + e); 692 size = 0; 693 } catch (java.io.IOException e2) { 694 Log.e(LOGTAG, "IOException: " + e2); 695 size = 0; 696 } 697 return size; 698 } 699 700 /** 701 * Get the InputStream for an Android resource 702 * There are three different kinds of android resources: 703 * - file:///android_res 704 * - file:///android_asset 705 * - content:// 706 * @param url The url to load. 707 * @return An InputStream to the android resource 708 */ inputStreamForAndroidResource(String url)709 private InputStream inputStreamForAndroidResource(String url) { 710 // This list needs to be kept in sync with the list in 711 // external/webkit/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp 712 final String ANDROID_ASSET = "file:///android_asset/"; 713 final String ANDROID_RESOURCE = "file:///android_res/"; 714 final String ANDROID_CONTENT = "content:"; 715 716 // file:///android_res 717 if (url.startsWith(ANDROID_RESOURCE)) { 718 url = url.replaceFirst(ANDROID_RESOURCE, ""); 719 if (url == null || url.length() == 0) { 720 Log.e(LOGTAG, "url has length 0 " + url); 721 return null; 722 } 723 int slash = url.indexOf('/'); 724 int dot = url.indexOf('.', slash); 725 if (slash == -1 || dot == -1) { 726 Log.e(LOGTAG, "Incorrect res path: " + url); 727 return null; 728 } 729 String subClassName = url.substring(0, slash); 730 String fieldName = url.substring(slash + 1, dot); 731 String errorMsg = null; 732 try { 733 final Class<?> d = mContext.getApplicationContext() 734 .getClassLoader().loadClass( 735 mContext.getPackageName() + ".R$" 736 + subClassName); 737 final java.lang.reflect.Field field = d.getField(fieldName); 738 final int id = field.getInt(null); 739 TypedValue value = new TypedValue(); 740 mContext.getResources().getValue(id, value, true); 741 if (value.type == TypedValue.TYPE_STRING) { 742 return mContext.getAssets().openNonAsset( 743 value.assetCookie, value.string.toString(), 744 AssetManager.ACCESS_STREAMING); 745 } else { 746 // Old stack only supports TYPE_STRING for res files 747 Log.e(LOGTAG, "not of type string: " + url); 748 return null; 749 } 750 } catch (Exception e) { 751 Log.e(LOGTAG, "Exception: " + url); 752 return null; 753 } 754 755 // file:///android_asset 756 } else if (url.startsWith(ANDROID_ASSET)) { 757 url = url.replaceFirst(ANDROID_ASSET, ""); 758 try { 759 AssetManager assets = mContext.getAssets(); 760 return assets.open(url, AssetManager.ACCESS_STREAMING); 761 } catch (IOException e) { 762 return null; 763 } 764 765 // content:// 766 } else if (mSettings.getAllowContentAccess() && 767 url.startsWith(ANDROID_CONTENT)) { 768 try { 769 // Strip off mimetype, for compatibility with ContentLoader.java 770 // If we don't do this, we can fail to load Gmail attachments, 771 // because the URL being loaded doesn't exactly match the URL we 772 // have permission to read. 773 int mimeIndex = url.lastIndexOf('?'); 774 if (mimeIndex != -1) { 775 url = url.substring(0, mimeIndex); 776 } 777 Uri uri = Uri.parse(url); 778 return mContext.getContentResolver().openInputStream(uri); 779 } catch (Exception e) { 780 Log.e(LOGTAG, "Exception: " + url); 781 return null; 782 } 783 } else { 784 return null; 785 } 786 } 787 788 /** 789 * Start loading a resource. 790 * @param loaderHandle The native ResourceLoader that is the target of the 791 * data. 792 * @param url The url to load. 793 * @param method The http method. 794 * @param headers The http headers. 795 * @param postData If the method is "POST" postData is sent as the request 796 * body. Is null when empty. 797 * @param postDataIdentifier If the post data contained form this is the form identifier, otherwise it is 0. 798 * @param cacheMode The cache mode to use when loading this resource. See WebSettings.setCacheMode 799 * @param mainResource True if the this resource is the main request, not a supporting resource 800 * @param userGesture 801 * @param synchronous True if the load is synchronous. 802 * @return A newly created LoadListener object. 803 */ startLoadingResource(int loaderHandle, String url, String method, HashMap headers, byte[] postData, long postDataIdentifier, int cacheMode, boolean mainResource, boolean userGesture, boolean synchronous, String username, String password)804 private LoadListener startLoadingResource(int loaderHandle, 805 String url, 806 String method, 807 HashMap headers, 808 byte[] postData, 809 long postDataIdentifier, 810 int cacheMode, 811 boolean mainResource, 812 boolean userGesture, 813 boolean synchronous, 814 String username, 815 String password) { 816 PerfChecker checker = new PerfChecker(); 817 818 if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) { 819 cacheMode = mSettings.getCacheMode(); 820 } 821 822 if (method.equals("POST")) { 823 // Don't use the cache on POSTs when issuing a normal POST 824 // request. 825 if (cacheMode == WebSettings.LOAD_NORMAL) { 826 cacheMode = WebSettings.LOAD_NO_CACHE; 827 } 828 String[] ret = getUsernamePassword(); 829 if (ret != null) { 830 String domUsername = ret[0]; 831 String domPassword = ret[1]; 832 maybeSavePassword(postData, domUsername, domPassword); 833 } 834 } 835 836 // is this resource the main-frame top-level page? 837 boolean isMainFramePage = mIsMainFrame; 838 839 if (DebugFlags.BROWSER_FRAME) { 840 Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method=" 841 + method + ", postData=" + postData + ", isMainFramePage=" 842 + isMainFramePage + ", mainResource=" + mainResource 843 + ", userGesture=" + userGesture); 844 } 845 846 // Create a LoadListener 847 LoadListener loadListener = LoadListener.getLoadListener(mContext, 848 this, url, loaderHandle, synchronous, isMainFramePage, 849 mainResource, userGesture, postDataIdentifier, username, password); 850 851 if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) { 852 // send an error message, so that loadListener can be deleted 853 // after this is returned. This is important as LoadListener's 854 // nativeError will remove the request from its DocLoader's request 855 // list. But the set up is not done until this method is returned. 856 loadListener.error( 857 android.net.http.EventHandler.ERROR, mContext.getString( 858 com.android.internal.R.string.httpErrorTooManyRequests)); 859 return loadListener; 860 } 861 862 // Note that we are intentionally skipping 863 // inputStreamForAndroidResource. This is so that FrameLoader will use 864 // the various StreamLoader classes to handle assets. 865 FrameLoader loader = new FrameLoader(loadListener, mSettings, method, 866 mCallbackProxy.shouldInterceptRequest(url)); 867 loader.setHeaders(headers); 868 loader.setPostData(postData); 869 // Set the load mode to the mode used for the current page. 870 // If WebKit wants validation, go to network directly. 871 loader.setCacheMode(headers.containsKey("If-Modified-Since") 872 || headers.containsKey("If-None-Match") ? 873 WebSettings.LOAD_NO_CACHE : cacheMode); 874 // Set referrer to current URL? 875 if (!loader.executeLoad()) { 876 checker.responseAlert("startLoadingResource fail"); 877 } 878 checker.responseAlert("startLoadingResource succeed"); 879 880 return !synchronous ? loadListener : null; 881 } 882 883 /** 884 * If this looks like a POST request (form submission) containing a username 885 * and password, give the user the option of saving them. Will either do 886 * nothing, or block until the UI interaction is complete. 887 * 888 * Called by startLoadingResource when using the Apache HTTP stack. 889 * Called directly by WebKit when using the Chrome HTTP stack. 890 * 891 * @param postData The data about to be sent as the body of a POST request. 892 * @param username The username entered by the user (sniffed from the DOM). 893 * @param password The password entered by the user (sniffed from the DOM). 894 */ maybeSavePassword( byte[] postData, String username, String password)895 private void maybeSavePassword( 896 byte[] postData, String username, String password) { 897 if (postData == null 898 || username == null || username.isEmpty() 899 || password == null || password.isEmpty()) { 900 return; // No password to save. 901 } 902 903 if (!mSettings.getSavePassword()) { 904 return; // User doesn't want to save passwords. 905 } 906 907 try { 908 if (DebugFlags.BROWSER_FRAME) { 909 Assert.assertNotNull(mCallbackProxy.getBackForwardList() 910 .getCurrentItem()); 911 } 912 WebAddress uri = new WebAddress(mCallbackProxy 913 .getBackForwardList().getCurrentItem().getUrl()); 914 String schemePlusHost = uri.getScheme() + uri.getHost(); 915 // Check to see if the username & password appear in 916 // the post data (there could be another form on the 917 // page and that was posted instead. 918 String postString = new String(postData); 919 if (postString.contains(URLEncoder.encode(username)) && 920 postString.contains(URLEncoder.encode(password))) { 921 String[] saved = mDatabase.getUsernamePassword( 922 schemePlusHost); 923 if (saved != null) { 924 // null username implies that user has chosen not to 925 // save password 926 if (saved[0] != null) { 927 // non-null username implies that user has 928 // chosen to save password, so update the 929 // recorded password 930 mDatabase.setUsernamePassword( 931 schemePlusHost, username, password); 932 } 933 } else { 934 // CallbackProxy will handle creating the resume 935 // message 936 mCallbackProxy.onSavePassword(schemePlusHost, username, 937 password, null); 938 } 939 } 940 } catch (ParseException ex) { 941 // if it is bad uri, don't save its password 942 } 943 } 944 945 // Called by jni from the chrome network stack. shouldInterceptRequest(String url)946 private WebResourceResponse shouldInterceptRequest(String url) { 947 InputStream androidResource = inputStreamForAndroidResource(url); 948 if (androidResource != null) { 949 return new WebResourceResponse(null, null, androidResource); 950 } 951 WebResourceResponse response = mCallbackProxy.shouldInterceptRequest(url); 952 if (response == null && "browser:incognito".equals(url)) { 953 try { 954 Resources res = mContext.getResources(); 955 InputStream ins = res.openRawResource( 956 com.android.internal.R.raw.incognito_mode_start_page); 957 response = new WebResourceResponse("text/html", "utf8", ins); 958 } catch (NotFoundException ex) { 959 // This shouldn't happen, but try and gracefully handle it jic 960 Log.w(LOGTAG, "Failed opening raw.incognito_mode_start_page", ex); 961 } 962 } 963 return response; 964 } 965 966 /** 967 * Set the progress for the browser activity. Called by native code. 968 * Uses a delay so it does not happen too often. 969 * @param newProgress An int between zero and one hundred representing 970 * the current progress percentage of loading the page. 971 */ setProgress(int newProgress)972 private void setProgress(int newProgress) { 973 mCallbackProxy.onProgressChanged(newProgress); 974 if (newProgress == 100) { 975 sendMessageDelayed(obtainMessage(FRAME_COMPLETED), 100); 976 } 977 // FIXME: Need to figure out a better way to switch out of the history 978 // drawing mode. Maybe we can somehow compare the history picture with 979 // the current picture, and switch when it contains more content. 980 if (mFirstLayoutDone && newProgress > TRANSITION_SWITCH_THRESHOLD) { 981 mCallbackProxy.switchOutDrawHistory(); 982 } 983 } 984 985 /** 986 * Send the icon to the activity for display. 987 * @param icon A Bitmap representing a page's favicon. 988 */ didReceiveIcon(Bitmap icon)989 private void didReceiveIcon(Bitmap icon) { 990 mCallbackProxy.onReceivedIcon(icon); 991 } 992 993 // Called by JNI when an apple-touch-icon attribute was found. didReceiveTouchIconUrl(String url, boolean precomposed)994 private void didReceiveTouchIconUrl(String url, boolean precomposed) { 995 mCallbackProxy.onReceivedTouchIconUrl(url, precomposed); 996 } 997 998 /** 999 * Request a new window from the client. 1000 * @return The BrowserFrame object stored in the new WebView. 1001 */ createWindow(boolean dialog, boolean userGesture)1002 private BrowserFrame createWindow(boolean dialog, boolean userGesture) { 1003 return mCallbackProxy.createWindow(dialog, userGesture); 1004 } 1005 1006 /** 1007 * Try to focus this WebView. 1008 */ requestFocus()1009 private void requestFocus() { 1010 mCallbackProxy.onRequestFocus(); 1011 } 1012 1013 /** 1014 * Close this frame and window. 1015 */ closeWindow(WebViewCore w)1016 private void closeWindow(WebViewCore w) { 1017 mCallbackProxy.onCloseWindow(w.getWebView()); 1018 } 1019 1020 // XXX: Must match PolicyAction in FrameLoaderTypes.h in webcore 1021 static final int POLICY_USE = 0; 1022 static final int POLICY_IGNORE = 2; 1023 decidePolicyForFormResubmission(int policyFunction)1024 private void decidePolicyForFormResubmission(int policyFunction) { 1025 Message dontResend = obtainMessage(POLICY_FUNCTION, policyFunction, 1026 POLICY_IGNORE); 1027 Message resend = obtainMessage(POLICY_FUNCTION, policyFunction, 1028 POLICY_USE); 1029 mCallbackProxy.onFormResubmission(dontResend, resend); 1030 } 1031 1032 /** 1033 * Tell the activity to update its global history. 1034 */ updateVisitedHistory(String url, boolean isReload)1035 private void updateVisitedHistory(String url, boolean isReload) { 1036 mCallbackProxy.doUpdateVisitedHistory(url, isReload); 1037 } 1038 1039 /** 1040 * Get the CallbackProxy for sending messages to the UI thread. 1041 */ getCallbackProxy()1042 /* package */ CallbackProxy getCallbackProxy() { 1043 return mCallbackProxy; 1044 } 1045 1046 /** 1047 * Returns the User Agent used by this frame 1048 */ getUserAgentString()1049 String getUserAgentString() { 1050 return mSettings.getUserAgentString(); 1051 } 1052 1053 // These ids need to be in sync with enum rawResId in PlatformBridge.h 1054 private static final int NODOMAIN = 1; 1055 private static final int LOADERROR = 2; 1056 /* package */ static final int DRAWABLEDIR = 3; 1057 private static final int FILE_UPLOAD_LABEL = 4; 1058 private static final int RESET_LABEL = 5; 1059 private static final int SUBMIT_LABEL = 6; 1060 private static final int FILE_UPLOAD_NO_FILE_CHOSEN = 7; 1061 getRawResFilename(int id)1062 private String getRawResFilename(int id) { 1063 return getRawResFilename(id, mContext); 1064 } getRawResFilename(int id, Context context)1065 /* package */ static String getRawResFilename(int id, Context context) { 1066 int resid; 1067 switch (id) { 1068 case NODOMAIN: 1069 resid = com.android.internal.R.raw.nodomain; 1070 break; 1071 1072 case LOADERROR: 1073 resid = com.android.internal.R.raw.loaderror; 1074 break; 1075 1076 case DRAWABLEDIR: 1077 // use one known resource to find the drawable directory 1078 resid = com.android.internal.R.drawable.btn_check_off; 1079 break; 1080 1081 case FILE_UPLOAD_LABEL: 1082 return context.getResources().getString( 1083 com.android.internal.R.string.upload_file); 1084 1085 case RESET_LABEL: 1086 return context.getResources().getString( 1087 com.android.internal.R.string.reset); 1088 1089 case SUBMIT_LABEL: 1090 return context.getResources().getString( 1091 com.android.internal.R.string.submit); 1092 1093 case FILE_UPLOAD_NO_FILE_CHOSEN: 1094 return context.getResources().getString( 1095 com.android.internal.R.string.no_file_chosen); 1096 1097 default: 1098 Log.e(LOGTAG, "getRawResFilename got incompatible resource ID"); 1099 return ""; 1100 } 1101 TypedValue value = new TypedValue(); 1102 context.getResources().getValue(resid, value, true); 1103 if (id == DRAWABLEDIR) { 1104 String path = value.string.toString(); 1105 int index = path.lastIndexOf('/'); 1106 if (index < 0) { 1107 Log.e(LOGTAG, "Can't find drawable directory."); 1108 return ""; 1109 } 1110 return path.substring(0, index + 1); 1111 } 1112 return value.string.toString(); 1113 } 1114 density()1115 private float density() { 1116 return mContext.getResources().getDisplayMetrics().density; 1117 } 1118 1119 /** 1120 * Called by JNI when the native HTTP stack gets an authentication request. 1121 * 1122 * We delegate the request to CallbackProxy, and route its response to 1123 * {@link #nativeAuthenticationProceed(int, String, String)} or 1124 * {@link #nativeAuthenticationCancel(int)}. 1125 * 1126 * We don't care what thread the callback is invoked on. All threading is 1127 * handled on the C++ side, because the WebKit thread may be blocked on a 1128 * synchronous call and unable to pump our MessageQueue. 1129 */ didReceiveAuthenticationChallenge( final int handle, String host, String realm, final boolean useCachedCredentials, final boolean suppressDialog)1130 private void didReceiveAuthenticationChallenge( 1131 final int handle, String host, String realm, final boolean useCachedCredentials, 1132 final boolean suppressDialog) { 1133 1134 HttpAuthHandler handler = new HttpAuthHandler() { 1135 1136 @Override 1137 public boolean useHttpAuthUsernamePassword() { 1138 return useCachedCredentials; 1139 } 1140 1141 @Override 1142 public void proceed(String username, String password) { 1143 nativeAuthenticationProceed(handle, username, password); 1144 } 1145 1146 @Override 1147 public void cancel() { 1148 nativeAuthenticationCancel(handle); 1149 } 1150 1151 @Override 1152 public boolean suppressDialog() { 1153 return suppressDialog; 1154 } 1155 }; 1156 mCallbackProxy.onReceivedHttpAuthRequest(handler, host, realm); 1157 } 1158 1159 /** 1160 * Called by JNI when the Chromium HTTP stack gets an invalid certificate chain. 1161 * 1162 * We delegate the request to CallbackProxy, and route its response to 1163 * {@link #nativeSslCertErrorProceed(int)} or 1164 * {@link #nativeSslCertErrorCancel(int, int)}. 1165 */ reportSslCertError(final int handle, final int certError, byte certDER[], String url)1166 private void reportSslCertError(final int handle, final int certError, byte certDER[], 1167 String url) { 1168 final SslError sslError; 1169 try { 1170 X509Certificate cert = new X509CertImpl(certDER); 1171 SslCertificate sslCert = new SslCertificate(cert); 1172 sslError = SslError.SslErrorFromChromiumErrorCode(certError, sslCert, url); 1173 } catch (IOException e) { 1174 // Can't get the certificate, not much to do. 1175 Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); 1176 nativeSslCertErrorCancel(handle, certError); 1177 return; 1178 } 1179 1180 if (SslCertLookupTable.getInstance().isAllowed(sslError)) { 1181 nativeSslCertErrorProceed(handle); 1182 mCallbackProxy.onProceededAfterSslError(sslError); 1183 return; 1184 } 1185 1186 SslErrorHandler handler = new SslErrorHandler() { 1187 @Override 1188 public void proceed() { 1189 SslCertLookupTable.getInstance().setIsAllowed(sslError); 1190 nativeSslCertErrorProceed(handle); 1191 } 1192 @Override 1193 public void cancel() { 1194 nativeSslCertErrorCancel(handle, certError); 1195 } 1196 }; 1197 mCallbackProxy.onReceivedSslError(handler, sslError); 1198 } 1199 1200 /** 1201 * Called by JNI when the native HTTPS stack gets a client 1202 * certificate request. 1203 * 1204 * We delegate the request to CallbackProxy, and route its response to 1205 * {@link #nativeSslClientCert(int, X509Certificate)}. 1206 */ requestClientCert(int handle, String hostAndPort)1207 private void requestClientCert(int handle, String hostAndPort) { 1208 SslClientCertLookupTable table = SslClientCertLookupTable.getInstance(); 1209 if (table.IsAllowed(hostAndPort)) { 1210 // previously allowed 1211 nativeSslClientCert(handle, 1212 table.PrivateKey(hostAndPort), 1213 table.CertificateChain(hostAndPort)); 1214 } else if (table.IsDenied(hostAndPort)) { 1215 // previously denied 1216 nativeSslClientCert(handle, null, null); 1217 } else { 1218 // previously ignored or new 1219 mCallbackProxy.onReceivedClientCertRequest( 1220 new ClientCertRequestHandler(this, handle, hostAndPort, table), hostAndPort); 1221 } 1222 } 1223 1224 /** 1225 * Called by JNI when the native HTTP stack needs to download a file. 1226 * 1227 * We delegate the request to CallbackProxy, which owns the current app's 1228 * DownloadListener. 1229 */ downloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength)1230 private void downloadStart(String url, String userAgent, 1231 String contentDisposition, String mimeType, long contentLength) { 1232 // This will only work if the url ends with the filename 1233 if (mimeType.isEmpty()) { 1234 try { 1235 String extension = url.substring(url.lastIndexOf('.') + 1); 1236 mimeType = libcore.net.MimeUtils.guessMimeTypeFromExtension(extension); 1237 // MimeUtils might return null, not sure if downloadmanager is happy with that 1238 if (mimeType == null) 1239 mimeType = ""; 1240 } catch(IndexOutOfBoundsException exception) { 1241 // mimeType string end with a '.', not much to do 1242 } 1243 } 1244 mimeType = MimeTypeMap.getSingleton().remapGenericMimeType( 1245 mimeType, url, contentDisposition); 1246 1247 if (CertTool.getCertType(mimeType) != null) { 1248 mKeyStoreHandler = new KeyStoreHandler(mimeType); 1249 } else { 1250 mCallbackProxy.onDownloadStart(url, userAgent, 1251 contentDisposition, mimeType, contentLength); 1252 } 1253 } 1254 1255 /** 1256 * Called by JNI for Chrome HTTP stack when the Java side needs to access the data. 1257 */ didReceiveData(byte data[], int size)1258 private void didReceiveData(byte data[], int size) { 1259 if (mKeyStoreHandler != null) mKeyStoreHandler.didReceiveData(data, size); 1260 } 1261 didFinishLoading()1262 private void didFinishLoading() { 1263 if (mKeyStoreHandler != null) { 1264 mKeyStoreHandler.installCert(mContext); 1265 mKeyStoreHandler = null; 1266 } 1267 } 1268 1269 /** 1270 * Called by JNI when we recieve a certificate for the page's main resource. 1271 * Used by the Chromium HTTP stack only. 1272 */ setCertificate(byte cert_der[])1273 private void setCertificate(byte cert_der[]) { 1274 try { 1275 X509Certificate cert = new X509CertImpl(cert_der); 1276 mCallbackProxy.onReceivedCertificate(new SslCertificate(cert)); 1277 } catch (IOException e) { 1278 // Can't get the certificate, not much to do. 1279 Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); 1280 return; 1281 } 1282 } 1283 getSearchBox()1284 /*package*/ SearchBox getSearchBox() { 1285 return mSearchBox; 1286 } 1287 1288 /** 1289 * Called by JNI when processing the X-Auto-Login header. 1290 */ autoLogin(String realm, String account, String args)1291 private void autoLogin(String realm, String account, String args) { 1292 mCallbackProxy.onReceivedLoginRequest(realm, account, args); 1293 } 1294 1295 //========================================================================== 1296 // native functions 1297 //========================================================================== 1298 1299 /** 1300 * Create a new native frame for a given WebView 1301 * @param w A WebView that the frame draws into. 1302 * @param am AssetManager to use to get assets. 1303 * @param list The native side will add and remove items from this list as 1304 * the native list changes. 1305 */ nativeCreateFrame(WebViewCore w, AssetManager am, WebBackForwardList list)1306 private native void nativeCreateFrame(WebViewCore w, AssetManager am, 1307 WebBackForwardList list); 1308 1309 /** 1310 * Destroy the native frame. 1311 */ nativeDestroyFrame()1312 public native void nativeDestroyFrame(); 1313 nativeCallPolicyFunction(int policyFunction, int decision)1314 private native void nativeCallPolicyFunction(int policyFunction, 1315 int decision); 1316 1317 /** 1318 * Reload the current main frame. 1319 */ reload(boolean allowStale)1320 public native void reload(boolean allowStale); 1321 1322 /** 1323 * Go back or forward the number of steps given. 1324 * @param steps A negative or positive number indicating the direction 1325 * and number of steps to move. 1326 */ nativeGoBackOrForward(int steps)1327 private native void nativeGoBackOrForward(int steps); 1328 1329 /** 1330 * stringByEvaluatingJavaScriptFromString will execute the 1331 * JS passed in in the context of this browser frame. 1332 * @param script A javascript string to execute 1333 * 1334 * @return string result of execution or null 1335 */ stringByEvaluatingJavaScriptFromString(String script)1336 public native String stringByEvaluatingJavaScriptFromString(String script); 1337 1338 /** 1339 * Add a javascript interface to the main frame. 1340 */ nativeAddJavascriptInterface(int nativeFramePointer, Object obj, String interfaceName)1341 private native void nativeAddJavascriptInterface(int nativeFramePointer, 1342 Object obj, String interfaceName); 1343 1344 /** 1345 * Enable or disable the native cache. 1346 */ 1347 /* FIXME: The native cache is always on for now until we have a better 1348 * solution for our 2 caches. */ setCacheDisabled(boolean disabled)1349 private native void setCacheDisabled(boolean disabled); 1350 cacheDisabled()1351 public native boolean cacheDisabled(); 1352 clearCache()1353 public native void clearCache(); 1354 1355 /** 1356 * Returns false if the url is bad. 1357 */ nativeLoadUrl(String url, Map<String, String> headers)1358 private native void nativeLoadUrl(String url, Map<String, String> headers); 1359 nativePostUrl(String url, byte[] postData)1360 private native void nativePostUrl(String url, byte[] postData); 1361 nativeLoadData(String baseUrl, String data, String mimeType, String encoding, String historyUrl)1362 private native void nativeLoadData(String baseUrl, String data, 1363 String mimeType, String encoding, String historyUrl); 1364 1365 /** 1366 * Stop loading the current page. 1367 */ stopLoading()1368 public void stopLoading() { 1369 if (mIsMainFrame) { 1370 resetLoadingStates(); 1371 } 1372 nativeStopLoading(); 1373 } 1374 nativeStopLoading()1375 private native void nativeStopLoading(); 1376 1377 /** 1378 * Return true if the document has images. 1379 */ documentHasImages()1380 public native boolean documentHasImages(); 1381 1382 /** 1383 * @return TRUE if there is a password field in the current frame 1384 */ hasPasswordField()1385 private native boolean hasPasswordField(); 1386 1387 /** 1388 * Get username and password in the current frame. If found, String[0] is 1389 * username and String[1] is password. Otherwise return NULL. 1390 * @return String[] 1391 */ getUsernamePassword()1392 private native String[] getUsernamePassword(); 1393 1394 /** 1395 * Set username and password to the proper fields in the current frame 1396 * @param username 1397 * @param password 1398 */ setUsernamePassword(String username, String password)1399 private native void setUsernamePassword(String username, String password); 1400 nativeSaveWebArchive(String basename, boolean autoname)1401 private native String nativeSaveWebArchive(String basename, boolean autoname); 1402 nativeOrientationChanged(int orientation)1403 private native void nativeOrientationChanged(int orientation); 1404 nativeAuthenticationProceed(int handle, String username, String password)1405 private native void nativeAuthenticationProceed(int handle, String username, String password); nativeAuthenticationCancel(int handle)1406 private native void nativeAuthenticationCancel(int handle); 1407 nativeSslCertErrorProceed(int handle)1408 private native void nativeSslCertErrorProceed(int handle); nativeSslCertErrorCancel(int handle, int certError)1409 private native void nativeSslCertErrorCancel(int handle, int certError); 1410 nativeSslClientCert(int handle, byte[] pkcs8EncodedPrivateKey, byte[][] asn1DerEncodedCertificateChain)1411 native void nativeSslClientCert(int handle, 1412 byte[] pkcs8EncodedPrivateKey, 1413 byte[][] asn1DerEncodedCertificateChain); 1414 } 1415