Home | History | Annotate | Download | only in webkit
      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 
     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 
    144         ConfigCallback(WindowManager wm) {
    145             mWindowManager = wm;
    146         }
    147 
    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 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    371     private void resetLoadingStates() {
    372         mCommitted = true;
    373         mFirstLayoutDone = true;
    374     }
    375 
    376     /* package */boolean committed() {
    377         return mCommitted;
    378     }
    379 
    380     /* package */boolean firstLayoutDone() {
    381         return mFirstLayoutDone;
    382     }
    383 
    384     /* package */int loadType() {
    385         return mLoadType;
    386     }
    387 
    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      */
    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")
    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")
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    581     private native String documentAsText();
    582 
    583     /**
    584      * Return the text drawn on the child frames as a string
    585      */
    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      */
    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      */
    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      */
    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 
    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 
    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      */
    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      */
    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      */
    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      */
    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.
    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      */
    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      */
    901     private void didReceiveIcon(Bitmap icon) {
    902         mCallbackProxy.onReceivedIcon(icon);
    903     }
    904 
    905     // Called by JNI when an apple-touch-icon attribute was found.
    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      */
    914     private BrowserFrame createWindow(boolean dialog, boolean userGesture) {
    915         return mCallbackProxy.createWindow(dialog, userGesture);
    916     }
    917 
    918     /**
    919      * Try to focus this WebView.
    920      */
    921     private void requestFocus() {
    922         mCallbackProxy.onRequestFocus();
    923     }
    924 
    925     /**
    926      * Close this frame and window.
    927      */
    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 
    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      */
    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      */
    954     /* package */ CallbackProxy getCallbackProxy() {
    955         return mCallbackProxy;
    956     }
    957 
    958     /**
    959      * Returns the User Agent used by this frame
    960      */
    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 
    974     private String getRawResFilename(int id) {
    975         return getRawResFilename(id, mContext);
    976     }
    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 
   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      */
   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      */
   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      */
   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      */
   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      */
   1189     private void didReceiveData(byte data[], int size) {
   1190         if (mKeyStoreHandler != null) mKeyStoreHandler.didReceiveData(data, size);
   1191     }
   1192 
   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      */
   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      */
   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      */
   1233     private native void nativeCreateFrame(WebViewCore w, AssetManager am,
   1234             WebBackForwardList list);
   1235 
   1236     /**
   1237      * Destroy the native frame.
   1238      */
   1239     public native void nativeDestroyFrame();
   1240 
   1241     private native void nativeCallPolicyFunction(int policyFunction,
   1242             int decision);
   1243 
   1244     /**
   1245      * Reload the current main frame.
   1246      */
   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      */
   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      */
   1263     public native String stringByEvaluatingJavaScriptFromString(String script);
   1264 
   1265     /**
   1266      * Add a javascript interface to the main frame.
   1267      */
   1268     private native void nativeAddJavascriptInterface(int nativeFramePointer,
   1269             Object obj, String interfaceName, boolean requireAnnotation);
   1270 
   1271     public native void clearCache();
   1272 
   1273     /**
   1274      * Returns false if the url is bad.
   1275      */
   1276     private native void nativeLoadUrl(String url, Map<String, String> headers);
   1277 
   1278     private native void nativePostUrl(String url, byte[] postData);
   1279 
   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      */
   1286     public void stopLoading() {
   1287         if (mIsMainFrame) {
   1288             resetLoadingStates();
   1289         }
   1290         nativeStopLoading();
   1291     }
   1292 
   1293     private native void nativeStopLoading();
   1294 
   1295     /**
   1296      * Return true if the document has images.
   1297      */
   1298     public native boolean documentHasImages();
   1299 
   1300     /**
   1301      * @return TRUE if there is a password field in the current frame
   1302      */
   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      */
   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      */
   1317     private native void setUsernamePassword(String username, String password);
   1318 
   1319     private native String nativeSaveWebArchive(String basename, boolean autoname);
   1320 
   1321     private native void nativeOrientationChanged(int orientation);
   1322 
   1323     private native void nativeAuthenticationProceed(int handle, String username, String password);
   1324     private native void nativeAuthenticationCancel(int handle);
   1325 
   1326     private native void nativeSslCertErrorProceed(int handle);
   1327     private native void nativeSslCertErrorCancel(int handle, int certError);
   1328 
   1329     native void nativeSslClientCert(int handle,
   1330                                     int ctx,
   1331                                     byte[][] asn1DerEncodedCertificateChain);
   1332 
   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      */
   1344     /* package */ boolean getShouldStartScrolledRight() {
   1345         return nativeGetShouldStartScrolledRight(mNativeFrame);
   1346     }
   1347 
   1348     private native boolean nativeGetShouldStartScrolledRight(int nativeBrowserFrame);
   1349 }
   1350