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