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