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