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