diff options
Diffstat (limited to 'core/java/android/webkit/BrowserFrame.java')
| -rw-r--r-- | core/java/android/webkit/BrowserFrame.java | 1351 |
1 files changed, 0 insertions, 1351 deletions
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java deleted file mode 100644 index 6955d149fbfd..000000000000 --- a/core/java/android/webkit/BrowserFrame.java +++ /dev/null @@ -1,1351 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.webkit; - -import android.app.ActivityManager; -import android.content.ComponentCallbacks; -import android.content.Context; -import android.content.res.AssetManager; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.content.res.Resources.NotFoundException; -import android.graphics.Bitmap; -import android.net.ParseException; -import android.net.Uri; -import android.net.WebAddress; -import android.net.http.ErrorStrings; -import android.net.http.SslCertificate; -import android.net.http.SslError; -import android.os.Handler; -import android.os.Message; -import android.util.Log; -import android.util.TypedValue; -import android.view.Surface; -import android.view.ViewRootImpl; -import android.view.WindowManager; - -import junit.framework.Assert; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.ref.WeakReference; -import java.net.URLEncoder; -import java.security.PrivateKey; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import com.android.org.conscrypt.OpenSSLKey; -import com.android.org.conscrypt.OpenSSLKeyHolder; - -class BrowserFrame extends Handler { - - private static final String LOGTAG = "webkit"; - - /** - * Cap the number of LoadListeners that will be instantiated, so - * we don't blow the GREF count. Attempting to queue more than - * this many requests will prompt an error() callback on the - * request's LoadListener - */ - private final static int MAX_OUTSTANDING_REQUESTS = 300; - - private final CallbackProxy mCallbackProxy; - private final WebSettingsClassic mSettings; - private final Context mContext; - private final WebViewDatabaseClassic mDatabase; - private final WebViewCore mWebViewCore; - /* package */ boolean mLoadInitFromJava; - private int mLoadType; - private boolean mFirstLayoutDone = true; - private boolean mCommitted = true; - // Flag for blocking messages. This is used during destroy() so - // that if the UI thread posts any messages after the message - // queue has been cleared,they are ignored. - private boolean mBlockMessages = false; - private int mOrientation = -1; - - // Is this frame the main frame? - private boolean mIsMainFrame; - - // Javascript interface object - private class JSObject { - Object object; - boolean requireAnnotation; - - public JSObject(Object object, boolean requireAnnotation) { - this.object = object; - this.requireAnnotation = requireAnnotation; - } - } - - // Attached Javascript interfaces - private Map<String, JSObject> mJavaScriptObjects; - private Set<Object> mRemovedJavaScriptObjects; - - // Key store handler when Chromium HTTP stack is used. - private KeyStoreHandler mKeyStoreHandler = null; - - // message ids - // a message posted when a frame loading is completed - static final int FRAME_COMPLETED = 1001; - // orientation change message - static final int ORIENTATION_CHANGED = 1002; - // a message posted when the user decides the policy - static final int POLICY_FUNCTION = 1003; - - // Note: need to keep these in sync with FrameLoaderTypes.h in native - static final int FRAME_LOADTYPE_STANDARD = 0; - static final int FRAME_LOADTYPE_BACK = 1; - static final int FRAME_LOADTYPE_FORWARD = 2; - static final int FRAME_LOADTYPE_INDEXEDBACKFORWARD = 3; - static final int FRAME_LOADTYPE_RELOAD = 4; - static final int FRAME_LOADTYPE_RELOADALLOWINGSTALEDATA = 5; - static final int FRAME_LOADTYPE_SAME = 6; - static final int FRAME_LOADTYPE_REDIRECT = 7; - static final int FRAME_LOADTYPE_REPLACE = 8; - - // A progress threshold to switch from history Picture to live Picture - private static final int TRANSITION_SWITCH_THRESHOLD = 75; - - // This is a field accessed by native code as well as package classes. - /*package*/ int mNativeFrame; - - // Static instance of a JWebCoreJavaBridge to handle timer and cookie - // requests from WebCore. - static JWebCoreJavaBridge sJavaBridge; - - private static class ConfigCallback implements ComponentCallbacks { - private final ArrayList<WeakReference<Handler>> mHandlers = - new ArrayList<WeakReference<Handler>>(); - private final WindowManager mWindowManager; - - ConfigCallback(WindowManager wm) { - mWindowManager = wm; - } - - public synchronized void addHandler(Handler h) { - // No need to ever remove a Handler. If the BrowserFrame is - // destroyed, it will be collected and the WeakReference set to - // null. If it happens to still be around during a configuration - // change, the message will be ignored. - mHandlers.add(new WeakReference<Handler>(h)); - } - - public void onConfigurationChanged(Configuration newConfig) { - if (mHandlers.size() == 0) { - return; - } - int orientation = - mWindowManager.getDefaultDisplay().getOrientation(); - switch (orientation) { - case Surface.ROTATION_90: - orientation = 90; - break; - case Surface.ROTATION_180: - orientation = 180; - break; - case Surface.ROTATION_270: - orientation = -90; - break; - case Surface.ROTATION_0: - orientation = 0; - break; - default: - break; - } - synchronized (this) { - // Create a list of handlers to remove. Go ahead and make it - // the same size to avoid resizing. - ArrayList<WeakReference> handlersToRemove = - new ArrayList<WeakReference>(mHandlers.size()); - for (WeakReference<Handler> wh : mHandlers) { - Handler h = wh.get(); - if (h != null) { - h.sendMessage(h.obtainMessage(ORIENTATION_CHANGED, - orientation, 0)); - } else { - handlersToRemove.add(wh); - } - } - // Now remove all the null references. - for (WeakReference weak : handlersToRemove) { - mHandlers.remove(weak); - } - } - } - - public void onLowMemory() {} - } - static ConfigCallback sConfigCallback; - - /** - * Create a new BrowserFrame to be used in an application. - * @param context An application context to use when retrieving assets. - * @param w A WebViewCore used as the view for this frame. - * @param proxy A CallbackProxy for posting messages to the UI thread and - * querying a client for information. - * @param settings A WebSettings object that holds all settings. - * XXX: Called by WebCore thread. - */ - public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy, - WebSettingsClassic settings, Map<String, Object> javascriptInterfaces) { - - Context appContext = context.getApplicationContext(); - - // Create a global JWebCoreJavaBridge to handle timers and - // cookies in the WebCore thread. - if (sJavaBridge == null) { - sJavaBridge = new JWebCoreJavaBridge(); - // set WebCore native cache size - ActivityManager am = (ActivityManager) context - .getSystemService(Context.ACTIVITY_SERVICE); - if (am.getMemoryClass() > 16) { - sJavaBridge.setCacheSize(8 * 1024 * 1024); - } else { - sJavaBridge.setCacheSize(4 * 1024 * 1024); - } - // create CookieSyncManager with current Context - CookieSyncManager.createInstance(appContext); - // create PluginManager with current Context - PluginManager.getInstance(appContext); - } - - if (sConfigCallback == null) { - sConfigCallback = new ConfigCallback( - (WindowManager) appContext.getSystemService( - Context.WINDOW_SERVICE)); - ViewRootImpl.addConfigCallback(sConfigCallback); - } - sConfigCallback.addHandler(this); - - mJavaScriptObjects = new HashMap<String, JSObject>(); - addJavaScriptObjects(javascriptInterfaces); - mRemovedJavaScriptObjects = new HashSet<Object>(); - - mSettings = settings; - mContext = context; - mCallbackProxy = proxy; - mDatabase = WebViewDatabaseClassic.getInstance(appContext); - mWebViewCore = w; - - AssetManager am = context.getAssets(); - nativeCreateFrame(w, am, proxy.getBackForwardList()); - - if (DebugFlags.BROWSER_FRAME) { - Log.v(LOGTAG, "BrowserFrame constructor: this=" + this); - } - } - - /** - * Load a url from the network or the filesystem into the main frame. - * Following the same behaviour as Safari, javascript: URLs are not passed - * to the main frame, instead they are evaluated immediately. - * @param url The url to load. - * @param extraHeaders The extra headers sent with this url. This should not - * include the common headers like "user-agent". If it does, it - * will be replaced by the intrinsic value of the WebView. - */ - public void loadUrl(String url, Map<String, String> extraHeaders) { - mLoadInitFromJava = true; - if (URLUtil.isJavaScriptUrl(url)) { - // strip off the scheme and evaluate the string - stringByEvaluatingJavaScriptFromString( - url.substring("javascript:".length())); - } else { - nativeLoadUrl(url, extraHeaders); - } - mLoadInitFromJava = false; - } - - /** - * Load a url with "POST" method from the network into the main frame. - * @param url The url to load. - * @param data The data for POST request. - */ - public void postUrl(String url, byte[] data) { - mLoadInitFromJava = true; - nativePostUrl(url, data); - mLoadInitFromJava = false; - } - - /** - * Load the content as if it was loaded by the provided base URL. The - * historyUrl is used as the history entry for the load data. - * - * @param baseUrl Base URL used to resolve relative paths in the content - * @param data Content to render in the browser - * @param mimeType Mimetype of the data being passed in - * @param encoding Character set encoding of the provided data. - * @param historyUrl URL to use as the history entry. - */ - public void loadData(String baseUrl, String data, String mimeType, - String encoding, String historyUrl) { - mLoadInitFromJava = true; - if (historyUrl == null || historyUrl.length() == 0) { - historyUrl = "about:blank"; - } - if (data == null) { - data = ""; - } - - // Setup defaults for missing values. These defaults where taken from - // WebKit's WebFrame.mm - if (baseUrl == null || baseUrl.length() == 0) { - baseUrl = "about:blank"; - } - if (mimeType == null || mimeType.length() == 0) { - mimeType = "text/html"; - } - nativeLoadData(baseUrl, data, mimeType, encoding, historyUrl); - mLoadInitFromJava = false; - } - - /** - * Saves the contents of the frame as a web archive. - * - * @param basename The filename where the archive should be placed. - * @param autoname If false, takes filename to be a file. If true, filename - * is assumed to be a directory in which a filename will be - * chosen according to the url of the current page. - */ - /* package */ String saveWebArchive(String basename, boolean autoname) { - return nativeSaveWebArchive(basename, autoname); - } - - /** - * Go back or forward the number of steps given. - * @param steps A negative or positive number indicating the direction - * and number of steps to move. - */ - public void goBackOrForward(int steps) { - mLoadInitFromJava = true; - nativeGoBackOrForward(steps); - mLoadInitFromJava = false; - } - - /** - * native callback - * Report an error to an activity. - * @param errorCode The HTTP error code. - * @param description Optional human-readable description. If no description - * is given, we'll use a standard localized error message. - * @param failingUrl The URL that was being loaded when the error occurred. - * TODO: Report all errors including resource errors but include some kind - * of domain identifier. Change errorCode to an enum for a cleaner - * interface. - */ - private void reportError(int errorCode, String description, String failingUrl) { - // As this is called for the main resource and loading will be stopped - // after, reset the state variables. - resetLoadingStates(); - if (description == null || description.isEmpty()) { - description = ErrorStrings.getString(errorCode, mContext); - } - mCallbackProxy.onReceivedError(errorCode, description, failingUrl); - } - - private void resetLoadingStates() { - mCommitted = true; - mFirstLayoutDone = true; - } - - /* package */boolean committed() { - return mCommitted; - } - - /* package */boolean firstLayoutDone() { - return mFirstLayoutDone; - } - - /* package */int loadType() { - return mLoadType; - } - - /* package */void didFirstLayout() { - if (!mFirstLayoutDone) { - mFirstLayoutDone = true; - // ensure {@link WebViewCore#webkitDraw} is called as we were - // blocking the update in {@link #loadStarted} - mWebViewCore.contentDraw(); - } - } - - /** - * native callback - * Indicates the beginning of a new load. - * This method will be called once for the main frame. - */ - private void loadStarted(String url, Bitmap favicon, int loadType, - boolean isMainFrame) { - mIsMainFrame = isMainFrame; - - if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) { - mLoadType = loadType; - - if (isMainFrame) { - // Call onPageStarted for main frames. - mCallbackProxy.onPageStarted(url, favicon); - // as didFirstLayout() is only called for the main frame, reset - // mFirstLayoutDone only for the main frames - mFirstLayoutDone = false; - mCommitted = false; - // remove pending draw to block update until mFirstLayoutDone is - // set to true in didFirstLayout() - mWebViewCore.clearContent(); - mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW); - } - } - } - - @SuppressWarnings("unused") - private void saveFormData(HashMap<String, String> data) { - if (mSettings.getSaveFormData()) { - final WebHistoryItem h = mCallbackProxy.getBackForwardList() - .getCurrentItem(); - if (h != null) { - String url = WebTextView.urlForAutoCompleteData(h.getUrl()); - if (url != null) { - mDatabase.setFormData(url, data); - } - } - } - } - - @SuppressWarnings("unused") - private boolean shouldSaveFormData() { - if (mSettings.getSaveFormData()) { - final WebHistoryItem h = mCallbackProxy.getBackForwardList() - .getCurrentItem(); - return h != null && h.getUrl() != null; - } - return false; - } - - /** - * native callback - * Indicates the WebKit has committed to the new load - */ - private void transitionToCommitted(int loadType, boolean isMainFrame) { - // loadType is not used yet - if (isMainFrame) { - mCommitted = true; - mWebViewCore.getWebViewClassic().mViewManager.postResetStateAll(); - } - } - - /** - * native callback - * <p> - * Indicates the end of a new load. - * This method will be called once for the main frame. - */ - private void loadFinished(String url, int loadType, boolean isMainFrame) { - // mIsMainFrame and isMainFrame are better be equal!!! - - if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) { - if (isMainFrame) { - resetLoadingStates(); - mCallbackProxy.switchOutDrawHistory(); - mCallbackProxy.onPageFinished(url); - } - } - } - - /** - * Destroy all native components of the BrowserFrame. - */ - public void destroy() { - nativeDestroyFrame(); - mBlockMessages = true; - removeCallbacksAndMessages(null); - } - - /** - * Handle messages posted to us. - * @param msg The message to handle. - */ - @Override - public void handleMessage(Message msg) { - if (mBlockMessages) { - return; - } - switch (msg.what) { - case FRAME_COMPLETED: { - if (mSettings.getSavePassword() && hasPasswordField()) { - WebHistoryItem item = mCallbackProxy.getBackForwardList() - .getCurrentItem(); - if (item != null) { - WebAddress uri = new WebAddress(item.getUrl()); - String schemePlusHost = uri.getScheme() + uri.getHost(); - String[] up = - WebViewDatabaseClassic.getInstance(mContext) - .getUsernamePassword(schemePlusHost); - if (up != null && up[0] != null) { - setUsernamePassword(up[0], up[1]); - } - } - } - break; - } - - case POLICY_FUNCTION: { - nativeCallPolicyFunction(msg.arg1, msg.arg2); - break; - } - - case ORIENTATION_CHANGED: { - if (mOrientation != msg.arg1) { - mOrientation = msg.arg1; - nativeOrientationChanged(msg.arg1); - } - break; - } - - default: - break; - } - } - - /** - * Punch-through for WebCore to set the document - * title. Inform the Activity of the new title. - * @param title The new title of the document. - */ - private void setTitle(String title) { - // FIXME: The activity must call getTitle (a native method) to get the - // title. We should try and cache the title if we can also keep it in - // sync with the document. - mCallbackProxy.onReceivedTitle(title); - } - - /** - * Retrieves the render tree of this frame and puts it as the object for - * the message and sends the message. - * @param callback the message to use to send the render tree - */ - public void externalRepresentation(Message callback) { - callback.obj = externalRepresentation();; - callback.sendToTarget(); - } - - /** - * Return the render tree as a string - */ - private native String externalRepresentation(); - - /** - * Retrieves the visual text of the frames, puts it as the object for - * the message and sends the message. - * @param callback the message to use to send the visual text - */ - public void documentAsText(Message callback) { - StringBuilder text = new StringBuilder(); - if (callback.arg1 != 0) { - // Dump top frame as text. - text.append(documentAsText()); - } - if (callback.arg2 != 0) { - // Dump child frames as text. - text.append(childFramesAsText()); - } - callback.obj = text.toString(); - callback.sendToTarget(); - } - - /** - * Return the text drawn on the screen as a string - */ - private native String documentAsText(); - - /** - * Return the text drawn on the child frames as a string - */ - private native String childFramesAsText(); - - /* - * This method is called by WebCore to inform the frame that - * the Javascript window object has been cleared. - * We should re-attach any attached js interfaces. - */ - private void windowObjectCleared(int nativeFramePointer) { - Iterator<String> iter = mJavaScriptObjects.keySet().iterator(); - while (iter.hasNext()) { - String interfaceName = iter.next(); - JSObject jsobject = mJavaScriptObjects.get(interfaceName); - if (jsobject != null && jsobject.object != null) { - nativeAddJavascriptInterface(nativeFramePointer, - jsobject.object, interfaceName, jsobject.requireAnnotation); - } - } - mRemovedJavaScriptObjects.clear(); - } - - /* - * Add javascript objects to the internal list of objects. The default behavior - * is to allow access to inherited methods (no annotation needed). This is only - * used when js objects are passed through a constructor (via a hidden constructor). - * - * @TODO change the default behavior to be compatible with the public addjavascriptinterface - */ - private void addJavaScriptObjects(Map<String, Object> javascriptInterfaces) { - - // TODO in a separate CL provide logic to enable annotations for API level JB_MR1 and above. - if (javascriptInterfaces == null) return; - Iterator<String> iter = javascriptInterfaces.keySet().iterator(); - while (iter.hasNext()) { - String interfaceName = iter.next(); - Object object = javascriptInterfaces.get(interfaceName); - if (object != null) { - mJavaScriptObjects.put(interfaceName, new JSObject(object, false)); - } - } - } - - /** - * This method is called by WebCore to check whether application - * wants to hijack url loading - */ - public boolean handleUrl(String url) { - if (mLoadInitFromJava == true) { - return false; - } - if (mCallbackProxy.shouldOverrideUrlLoading(url)) { - // if the url is hijacked, reset the state of the BrowserFrame - didFirstLayout(); - return true; - } else { - return false; - } - } - - public void addJavascriptInterface(Object obj, String interfaceName, - boolean requireAnnotation) { - assert obj != null; - removeJavascriptInterface(interfaceName); - mJavaScriptObjects.put(interfaceName, new JSObject(obj, requireAnnotation)); - } - - public void removeJavascriptInterface(String interfaceName) { - // We keep a reference to the removed object because the native side holds only a weak - // reference and we need to allow the object to continue to be used until the page has been - // navigated. - if (mJavaScriptObjects.containsKey(interfaceName)) { - mRemovedJavaScriptObjects.add(mJavaScriptObjects.remove(interfaceName)); - } - } - - /** - * Called by JNI. Given a URI, find the associated file and return its size - * @param uri A String representing the URI of the desired file. - * @return int The size of the given file. - */ - private int getFileSize(String uri) { - int size = 0; - try { - InputStream stream = mContext.getContentResolver() - .openInputStream(Uri.parse(uri)); - size = stream.available(); - stream.close(); - } catch (Exception e) {} - return size; - } - - /** - * Called by JNI. Given a URI, a buffer, and an offset into the buffer, - * copy the resource into buffer. - * @param uri A String representing the URI of the desired file. - * @param buffer The byte array to copy the data into. - * @param offset The offet into buffer to place the data. - * @param expectedSize The size that the buffer has allocated for this file. - * @return int The size of the given file, or zero if it fails. - */ - private int getFile(String uri, byte[] buffer, int offset, - int expectedSize) { - int size = 0; - try { - InputStream stream = mContext.getContentResolver() - .openInputStream(Uri.parse(uri)); - size = stream.available(); - if (size <= expectedSize && buffer != null - && buffer.length - offset >= size) { - stream.read(buffer, offset, size); - } else { - size = 0; - } - stream.close(); - } catch (java.io.FileNotFoundException e) { - Log.e(LOGTAG, "FileNotFoundException:" + e); - size = 0; - } catch (java.io.IOException e2) { - Log.e(LOGTAG, "IOException: " + e2); - size = 0; - } - return size; - } - - /** - * Get the InputStream for an Android resource - * There are three different kinds of android resources: - * - file:///android_res - * - file:///android_asset - * - content:// - * @param url The url to load. - * @return An InputStream to the android resource - */ - private InputStream inputStreamForAndroidResource(String url) { - final String ANDROID_ASSET = URLUtil.ASSET_BASE; - final String ANDROID_RESOURCE = URLUtil.RESOURCE_BASE; - final String ANDROID_CONTENT = URLUtil.CONTENT_BASE; - - if (url.startsWith(ANDROID_RESOURCE)) { - url = url.replaceFirst(ANDROID_RESOURCE, ""); - if (url == null || url.length() == 0) { - Log.e(LOGTAG, "url has length 0 " + url); - return null; - } - int slash = url.indexOf('/'); - int dot = url.indexOf('.', slash); - if (slash == -1 || dot == -1) { - Log.e(LOGTAG, "Incorrect res path: " + url); - return null; - } - String subClassName = url.substring(0, slash); - String fieldName = url.substring(slash + 1, dot); - String errorMsg = null; - try { - final Class<?> d = mContext.getApplicationContext() - .getClassLoader().loadClass( - mContext.getPackageName() + ".R$" - + subClassName); - final java.lang.reflect.Field field = d.getField(fieldName); - final int id = field.getInt(null); - TypedValue value = new TypedValue(); - mContext.getResources().getValue(id, value, true); - if (value.type == TypedValue.TYPE_STRING) { - return mContext.getAssets().openNonAsset( - value.assetCookie, value.string.toString(), - AssetManager.ACCESS_STREAMING); - } else { - // Old stack only supports TYPE_STRING for res files - Log.e(LOGTAG, "not of type string: " + url); - return null; - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception: " + url); - return null; - } - } else if (url.startsWith(ANDROID_ASSET)) { - String assetUrl = url.replaceFirst(ANDROID_ASSET, ""); - try { - AssetManager assets = mContext.getAssets(); - Uri uri = Uri.parse(assetUrl); - return assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING); - } catch (IOException e) { - return null; - } catch (Exception e) { - Log.w(LOGTAG, "Problem loading url: " + url, e); - return null; - } - } else if (mSettings.getAllowContentAccess() && - url.startsWith(ANDROID_CONTENT)) { - try { - // Strip off MIME type. If we don't do this, we can fail to - // load Gmail attachments, because the URL being loaded doesn't - // exactly match the URL we have permission to read. - int mimeIndex = url.lastIndexOf('?'); - if (mimeIndex != -1) { - url = url.substring(0, mimeIndex); - } - Uri uri = Uri.parse(url); - return mContext.getContentResolver().openInputStream(uri); - } catch (Exception e) { - Log.e(LOGTAG, "Exception: " + url); - return null; - } - } else { - return null; - } - } - - /** - * If this looks like a POST request (form submission) containing a username - * and password, give the user the option of saving them. Will either do - * nothing, or block until the UI interaction is complete. - * - * Called directly by WebKit. - * - * @param postData The data about to be sent as the body of a POST request. - * @param username The username entered by the user (sniffed from the DOM). - * @param password The password entered by the user (sniffed from the DOM). - */ - private void maybeSavePassword( - byte[] postData, String username, String password) { - if (postData == null - || username == null || username.isEmpty() - || password == null || password.isEmpty()) { - return; // No password to save. - } - - if (!mSettings.getSavePassword()) { - return; // User doesn't want to save passwords. - } - - try { - if (DebugFlags.BROWSER_FRAME) { - Assert.assertNotNull(mCallbackProxy.getBackForwardList() - .getCurrentItem()); - } - WebAddress uri = new WebAddress(mCallbackProxy - .getBackForwardList().getCurrentItem().getUrl()); - String schemePlusHost = uri.getScheme() + uri.getHost(); - // Check to see if the username & password appear in - // the post data (there could be another form on the - // page and that was posted instead. - String postString = new String(postData); - if (postString.contains(URLEncoder.encode(username)) && - postString.contains(URLEncoder.encode(password))) { - String[] saved = mDatabase.getUsernamePassword( - schemePlusHost); - if (saved != null) { - // null username implies that user has chosen not to - // save password - if (saved[0] != null) { - // non-null username implies that user has - // chosen to save password, so update the - // recorded password - mDatabase.setUsernamePassword(schemePlusHost, username, password); - } - } else { - // CallbackProxy will handle creating the resume - // message - mCallbackProxy.onSavePassword(schemePlusHost, username, - password, null); - } - } - } catch (ParseException ex) { - // if it is bad uri, don't save its password - } - } - - // Called by jni from the chrome network stack. - private WebResourceResponse shouldInterceptRequest(String url) { - InputStream androidResource = inputStreamForAndroidResource(url); - if (androidResource != null) { - return new WebResourceResponse(null, null, androidResource); - } - - // Note that we check this after looking for an android_asset or - // android_res URL, as we allow those even if file access is disabled. - if (!mSettings.getAllowFileAccess() && url.startsWith("file://")) { - return new WebResourceResponse(null, null, null); - } - - WebResourceResponse response = mCallbackProxy.shouldInterceptRequest(url); - if (response == null && "browser:incognito".equals(url)) { - try { - Resources res = mContext.getResources(); - InputStream ins = res.openRawResource( - com.android.internal.R.raw.incognito_mode_start_page); - response = new WebResourceResponse("text/html", "utf8", ins); - } catch (NotFoundException ex) { - // This shouldn't happen, but try and gracefully handle it jic - Log.w(LOGTAG, "Failed opening raw.incognito_mode_start_page", ex); - } - } - return response; - } - - /** - * Set the progress for the browser activity. Called by native code. - * Uses a delay so it does not happen too often. - * @param newProgress An int between zero and one hundred representing - * the current progress percentage of loading the page. - */ - private void setProgress(int newProgress) { - mCallbackProxy.onProgressChanged(newProgress); - if (newProgress == 100) { - sendMessageDelayed(obtainMessage(FRAME_COMPLETED), 100); - } - // FIXME: Need to figure out a better way to switch out of the history - // drawing mode. Maybe we can somehow compare the history picture with - // the current picture, and switch when it contains more content. - if (mFirstLayoutDone && newProgress > TRANSITION_SWITCH_THRESHOLD) { - mCallbackProxy.switchOutDrawHistory(); - } - } - - /** - * Send the icon to the activity for display. - * @param icon A Bitmap representing a page's favicon. - */ - private void didReceiveIcon(Bitmap icon) { - mCallbackProxy.onReceivedIcon(icon); - } - - // Called by JNI when an apple-touch-icon attribute was found. - private void didReceiveTouchIconUrl(String url, boolean precomposed) { - mCallbackProxy.onReceivedTouchIconUrl(url, precomposed); - } - - /** - * Request a new window from the client. - * @return The BrowserFrame object stored in the new WebView. - */ - private BrowserFrame createWindow(boolean dialog, boolean userGesture) { - return mCallbackProxy.createWindow(dialog, userGesture); - } - - /** - * Try to focus this WebView. - */ - private void requestFocus() { - mCallbackProxy.onRequestFocus(); - } - - /** - * Close this frame and window. - */ - private void closeWindow(WebViewCore w) { - mCallbackProxy.onCloseWindow(w.getWebViewClassic()); - } - - // XXX: Must match PolicyAction in FrameLoaderTypes.h in webcore - static final int POLICY_USE = 0; - static final int POLICY_IGNORE = 2; - - private void decidePolicyForFormResubmission(int policyFunction) { - Message dontResend = obtainMessage(POLICY_FUNCTION, policyFunction, - POLICY_IGNORE); - Message resend = obtainMessage(POLICY_FUNCTION, policyFunction, - POLICY_USE); - mCallbackProxy.onFormResubmission(dontResend, resend); - } - - /** - * Tell the activity to update its global history. - */ - private void updateVisitedHistory(String url, boolean isReload) { - mCallbackProxy.doUpdateVisitedHistory(url, isReload); - } - - /** - * Get the CallbackProxy for sending messages to the UI thread. - */ - /* package */ CallbackProxy getCallbackProxy() { - return mCallbackProxy; - } - - /** - * Returns the User Agent used by this frame - */ - String getUserAgentString() { - return mSettings.getUserAgentString(); - } - - // These ids need to be in sync with enum rawResId in PlatformBridge.h - private static final int NODOMAIN = 1; - private static final int LOADERROR = 2; - /* package */ static final int DRAWABLEDIR = 3; - private static final int FILE_UPLOAD_LABEL = 4; - private static final int RESET_LABEL = 5; - private static final int SUBMIT_LABEL = 6; - private static final int FILE_UPLOAD_NO_FILE_CHOSEN = 7; - - private String getRawResFilename(int id) { - return getRawResFilename(id, mContext); - } - /* package */ static String getRawResFilename(int id, Context context) { - int resid; - switch (id) { - case NODOMAIN: - resid = com.android.internal.R.raw.nodomain; - break; - - case LOADERROR: - resid = com.android.internal.R.raw.loaderror; - break; - - case DRAWABLEDIR: - // use one known resource to find the drawable directory - resid = com.android.internal.R.drawable.btn_check_off; - break; - - case FILE_UPLOAD_LABEL: - return context.getResources().getString( - com.android.internal.R.string.upload_file); - - case RESET_LABEL: - return context.getResources().getString( - com.android.internal.R.string.reset); - - case SUBMIT_LABEL: - return context.getResources().getString( - com.android.internal.R.string.submit); - - case FILE_UPLOAD_NO_FILE_CHOSEN: - return context.getResources().getString( - com.android.internal.R.string.no_file_chosen); - - default: - Log.e(LOGTAG, "getRawResFilename got incompatible resource ID"); - return ""; - } - TypedValue value = new TypedValue(); - context.getResources().getValue(resid, value, true); - if (id == DRAWABLEDIR) { - String path = value.string.toString(); - int index = path.lastIndexOf('/'); - if (index < 0) { - Log.e(LOGTAG, "Can't find drawable directory."); - return ""; - } - return path.substring(0, index + 1); - } - return value.string.toString(); - } - - private float density() { - return WebViewCore.getFixedDisplayDensity(mContext); - } - - /** - * Called by JNI when the native HTTP stack gets an authentication request. - * - * We delegate the request to CallbackProxy, and route its response to - * {@link #nativeAuthenticationProceed(int, String, String)} or - * {@link #nativeAuthenticationCancel(int)}. - * - * We don't care what thread the callback is invoked on. All threading is - * handled on the C++ side, because the WebKit thread may be blocked on a - * synchronous call and unable to pump our MessageQueue. - */ - private void didReceiveAuthenticationChallenge( - final int handle, String host, String realm, final boolean useCachedCredentials, - final boolean suppressDialog) { - - HttpAuthHandler handler = new HttpAuthHandler() { - - @Override - public boolean useHttpAuthUsernamePassword() { - return useCachedCredentials; - } - - @Override - public void proceed(String username, String password) { - nativeAuthenticationProceed(handle, username, password); - } - - @Override - public void cancel() { - nativeAuthenticationCancel(handle); - } - - @Override - public boolean suppressDialog() { - return suppressDialog; - } - }; - mCallbackProxy.onReceivedHttpAuthRequest(handler, host, realm); - } - - /** - * Called by JNI when the Chromium HTTP stack gets an invalid certificate chain. - * - * We delegate the request to CallbackProxy, and route its response to - * {@link #nativeSslCertErrorProceed(int)} or - * {@link #nativeSslCertErrorCancel(int, int)}. - */ - private void reportSslCertError(final int handle, final int certError, byte certDER[], - String url) { - final SslError sslError; - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509Certificate cert = (X509Certificate) cf.generateCertificate( - new ByteArrayInputStream(certDER)); - SslCertificate sslCert = new SslCertificate(cert); - sslError = SslError.SslErrorFromChromiumErrorCode(certError, sslCert, url); - } catch (Exception e) { - // Can't get the certificate, not much to do. - Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); - nativeSslCertErrorCancel(handle, certError); - return; - } - - if (SslCertLookupTable.getInstance().isAllowed(sslError)) { - nativeSslCertErrorProceed(handle); - mCallbackProxy.onProceededAfterSslError(sslError); - return; - } - - SslErrorHandler handler = new SslErrorHandler() { - @Override - public void proceed() { - SslCertLookupTable.getInstance().setIsAllowed(sslError); - post(new Runnable() { - public void run() { - nativeSslCertErrorProceed(handle); - } - }); - } - @Override - public void cancel() { - post(new Runnable() { - public void run() { - nativeSslCertErrorCancel(handle, certError); - } - }); - } - }; - mCallbackProxy.onReceivedSslError(handler, sslError); - } - - /** - * Called by JNI when the native HTTPS stack gets a client - * certificate request. - * - * We delegate the request to CallbackProxy, and route its response to - * {@link #nativeSslClientCert(int, X509Certificate)}. - */ - private void requestClientCert(int handle, String hostAndPort) { - SslClientCertLookupTable table = SslClientCertLookupTable.getInstance(); - if (table.IsAllowed(hostAndPort)) { - // previously allowed - PrivateKey pkey = table.PrivateKey(hostAndPort); - if (pkey instanceof OpenSSLKeyHolder) { - OpenSSLKey sslKey = ((OpenSSLKeyHolder) pkey).getOpenSSLKey(); - nativeSslClientCert(handle, - sslKey.getPkeyContext(), - table.CertificateChain(hostAndPort)); - } else { - nativeSslClientCert(handle, - pkey.getEncoded(), - table.CertificateChain(hostAndPort)); - } - } else if (table.IsDenied(hostAndPort)) { - // previously denied - nativeSslClientCert(handle, 0, null); - } else { - // previously ignored or new - mCallbackProxy.onReceivedClientCertRequest( - new ClientCertRequestHandler(this, handle, hostAndPort, table), hostAndPort); - } - } - - /** - * Called by JNI when the native HTTP stack needs to download a file. - * - * We delegate the request to CallbackProxy, which owns the current app's - * DownloadListener. - */ - private void downloadStart(String url, String userAgent, - String contentDisposition, String mimeType, String referer, long contentLength) { - // This will only work if the url ends with the filename - if (mimeType.isEmpty()) { - try { - String extension = url.substring(url.lastIndexOf('.') + 1); - mimeType = libcore.net.MimeUtils.guessMimeTypeFromExtension(extension); - // MimeUtils might return null, not sure if downloadmanager is happy with that - if (mimeType == null) - mimeType = ""; - } catch(IndexOutOfBoundsException exception) { - // mimeType string end with a '.', not much to do - } - } - mimeType = MimeTypeMap.getSingleton().remapGenericMimeType( - mimeType, url, contentDisposition); - - if (CertTool.getCertType(mimeType) != null) { - mKeyStoreHandler = new KeyStoreHandler(mimeType); - } else { - mCallbackProxy.onDownloadStart(url, userAgent, - contentDisposition, mimeType, referer, contentLength); - } - } - - /** - * Called by JNI for Chrome HTTP stack when the Java side needs to access the data. - */ - private void didReceiveData(byte data[], int size) { - if (mKeyStoreHandler != null) mKeyStoreHandler.didReceiveData(data, size); - } - - private void didFinishLoading() { - if (mKeyStoreHandler != null) { - mKeyStoreHandler.installCert(mContext); - mKeyStoreHandler = null; - } - } - - /** - * Called by JNI when we recieve a certificate for the page's main resource. - * Used by the Chromium HTTP stack only. - */ - private void setCertificate(byte cert_der[]) { - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509Certificate cert = (X509Certificate) cf.generateCertificate( - new ByteArrayInputStream(cert_der)); - mCallbackProxy.onReceivedCertificate(new SslCertificate(cert)); - } catch (Exception e) { - // Can't get the certificate, not much to do. - Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); - return; - } - } - - /** - * Called by JNI when processing the X-Auto-Login header. - */ - private void autoLogin(String realm, String account, String args) { - mCallbackProxy.onReceivedLoginRequest(realm, account, args); - } - - //========================================================================== - // native functions - //========================================================================== - - /** - * Create a new native frame for a given WebView - * @param w A WebView that the frame draws into. - * @param am AssetManager to use to get assets. - * @param list The native side will add and remove items from this list as - * the native list changes. - */ - private native void nativeCreateFrame(WebViewCore w, AssetManager am, - WebBackForwardList list); - - /** - * Destroy the native frame. - */ - public native void nativeDestroyFrame(); - - private native void nativeCallPolicyFunction(int policyFunction, - int decision); - - /** - * Reload the current main frame. - */ - public native void reload(boolean allowStale); - - /** - * Go back or forward the number of steps given. - * @param steps A negative or positive number indicating the direction - * and number of steps to move. - */ - private native void nativeGoBackOrForward(int steps); - - /** - * stringByEvaluatingJavaScriptFromString will execute the - * JS passed in in the context of this browser frame. - * @param script A javascript string to execute - * - * @return string result of execution or null - */ - public native String stringByEvaluatingJavaScriptFromString(String script); - - /** - * Add a javascript interface to the main frame. - */ - private native void nativeAddJavascriptInterface(int nativeFramePointer, - Object obj, String interfaceName, boolean requireAnnotation); - - public native void clearCache(); - - /** - * Returns false if the url is bad. - */ - private native void nativeLoadUrl(String url, Map<String, String> headers); - - private native void nativePostUrl(String url, byte[] postData); - - private native void nativeLoadData(String baseUrl, String data, - String mimeType, String encoding, String historyUrl); - - /** - * Stop loading the current page. - */ - public void stopLoading() { - if (mIsMainFrame) { - resetLoadingStates(); - } - nativeStopLoading(); - } - - private native void nativeStopLoading(); - - /** - * Return true if the document has images. - */ - public native boolean documentHasImages(); - - /** - * @return TRUE if there is a password field in the current frame - */ - private native boolean hasPasswordField(); - - /** - * Get username and password in the current frame. If found, String[0] is - * username and String[1] is password. Otherwise return NULL. - * @return String[] - */ - private native String[] getUsernamePassword(); - - /** - * Set username and password to the proper fields in the current frame - * @param username - * @param password - */ - private native void setUsernamePassword(String username, String password); - - private native String nativeSaveWebArchive(String basename, boolean autoname); - - private native void nativeOrientationChanged(int orientation); - - private native void nativeAuthenticationProceed(int handle, String username, String password); - private native void nativeAuthenticationCancel(int handle); - - private native void nativeSslCertErrorProceed(int handle); - private native void nativeSslCertErrorCancel(int handle, int certError); - - native void nativeSslClientCert(int handle, - long ctx, - byte[][] asn1DerEncodedCertificateChain); - - native void nativeSslClientCert(int handle, - byte[] pkey, - byte[][] asn1DerEncodedCertificateChain); - - /** - * Returns true when the contents of the frame is an RTL or vertical-rl - * page. This is used for determining whether a frame should be initially - * scrolled right-most as opposed to left-most. - * @return true when the frame should be initially scrolled right-most - * based on the text direction and writing mode. - */ - /* package */ boolean getShouldStartScrolledRight() { - return nativeGetShouldStartScrolledRight(mNativeFrame); - } - - private native boolean nativeGetShouldStartScrolledRight(int nativeBrowserFrame); -} |
