diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
| commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
| tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /core/java/android/webkit/FrameLoader.java | |
| parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/android/webkit/FrameLoader.java')
| -rw-r--r-- | core/java/android/webkit/FrameLoader.java | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java new file mode 100644 index 000000000000..5e323ebb67a5 --- /dev/null +++ b/core/java/android/webkit/FrameLoader.java @@ -0,0 +1,370 @@ +/* + * 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.net.http.EventHandler; +import android.net.http.RequestHandle; +import android.util.Config; +import android.util.Log; +import android.webkit.CacheManager.CacheResult; +import android.webkit.UrlInterceptRegistry; + +import java.util.HashMap; +import java.util.Map; + +class FrameLoader { + + private final LoadListener mListener; + private final String mMethod; + private final boolean mIsHighPriority; + private final WebSettings mSettings; + private Map<String, String> mHeaders; + private byte[] mPostData; + private Network mNetwork; + private int mCacheMode; + private String mReferrer; + private String mContentType; + + private static final int URI_PROTOCOL = 0x100; + + private static final String CONTENT_TYPE = "content-type"; + + // Contents of an about:blank page + private static final String mAboutBlank = + "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EB\">" + + "<html><head><title>about:blank</title></head><body></body></html>"; + + static final String HEADER_STR = "text/xml, text/html, " + + "application/xhtml+xml, image/png, text/plain, */*;q=0.8"; + + private static final String LOGTAG = "webkit"; + + FrameLoader(LoadListener listener, WebSettings settings, + String method, boolean highPriority) { + mListener = listener; + mHeaders = null; + mMethod = method; + mIsHighPriority = highPriority; + mCacheMode = WebSettings.LOAD_NORMAL; + mSettings = settings; + } + + public void setReferrer(String ref) { + // only set referrer for http or https + if (URLUtil.isNetworkUrl(ref)) mReferrer = ref; + } + + public void setPostData(byte[] postData) { + mPostData = postData; + } + + public void setContentTypeForPost(String postContentType) { + mContentType = postContentType; + } + + public void setCacheMode(int cacheMode) { + mCacheMode = cacheMode; + } + + public void setHeaders(HashMap headers) { + mHeaders = headers; + } + + public LoadListener getLoadListener() { + return mListener; + } + + /** + * Issues the load request. + * + * Return value does not indicate if the load was successful or not. It + * simply indicates that the load request is reasonable. + * + * @return true if the load is reasonable. + */ + public boolean executeLoad() { + String url = mListener.url(); + + // Attempt to decode the percent-encoded url. + try { + url = new String(URLUtil.decode(url.getBytes())); + } catch (IllegalArgumentException e) { + // Fail with a bad url error if the decode fails. + mListener.error(EventHandler.ERROR_BAD_URL, + mListener.getContext().getString( + com.android.internal.R.string.httpErrorBadUrl)); + return false; + } + + if (URLUtil.isNetworkUrl(url)){ + if (mSettings.getBlockNetworkLoads()) { + mListener.error(EventHandler.ERROR_BAD_URL, + mListener.getContext().getString( + com.android.internal.R.string.httpErrorBadUrl)); + return false; + } + mNetwork = Network.getInstance(mListener.getContext()); + return handleHTTPLoad(); + } else if (handleLocalFile(url, mListener, mSettings)) { + return true; + } + if (Config.LOGV) { + Log.v(LOGTAG, "FrameLoader.executeLoad: url protocol not supported:" + + mListener.url()); + } + mListener.error(EventHandler.ERROR_UNSUPPORTED_SCHEME, + mListener.getContext().getText( + com.android.internal.R.string.httpErrorUnsupportedScheme).toString()); + return false; + + } + + /* package */ + static boolean handleLocalFile(String url, LoadListener loadListener, + WebSettings settings) { + if (URLUtil.isAssetUrl(url)) { + FileLoader.requestUrl(url, loadListener, loadListener.getContext(), + true, settings.getAllowFileAccess()); + return true; + } else if (URLUtil.isFileUrl(url)) { + FileLoader.requestUrl(url, loadListener, loadListener.getContext(), + false, settings.getAllowFileAccess()); + return true; + } else if (URLUtil.isContentUrl(url)) { + // Send the raw url to the ContentLoader because it will do a + // permission check and the url has to match.. + ContentLoader.requestUrl(loadListener.url(), loadListener, + loadListener.getContext()); + return true; + } else if (URLUtil.isDataUrl(url)) { + DataLoader.requestUrl(url, loadListener); + return true; + } else if (URLUtil.isAboutUrl(url)) { + loadListener.data(mAboutBlank.getBytes(), mAboutBlank.length()); + loadListener.endData(); + return true; + } + return false; + } + + private boolean handleHTTPLoad() { + if (mHeaders == null) { + mHeaders = new HashMap<String, String>(); + } + populateStaticHeaders(); + populateHeaders(); + + // response was handled by UrlIntercept, don't issue HTTP request + if (handleUrlIntercept()) return true; + + // response was handled by Cache, don't issue HTTP request + if (handleCache()) { + // push the request data down to the LoadListener + // as response from the cache could be a redirect + // and we may need to initiate a network request if the cache + // can't satisfy redirect URL + mListener.setRequestData(mMethod, mHeaders, mPostData, + mIsHighPriority); + return true; + } + + if (Config.LOGV) { + Log.v(LOGTAG, "FrameLoader: http " + mMethod + " load for: " + + mListener.url()); + } + + boolean ret = false; + int error = EventHandler.ERROR_UNSUPPORTED_SCHEME; + + try { + ret = mNetwork.requestURL(mMethod, mHeaders, + mPostData, mListener, mIsHighPriority); + } catch (android.net.ParseException ex) { + error = EventHandler.ERROR_BAD_URL; + } catch (java.lang.RuntimeException ex) { + /* probably an empty header set by javascript. We want + the same result as bad URL */ + error = EventHandler.ERROR_BAD_URL; + } + if (!ret) { + mListener.error(error, mListener.getContext().getText( + EventHandler.errorStringResources[Math.abs(error)]).toString()); + return false; + } + return true; + } + + /* + * This function is used by handleUrlInterecpt and handleCache to + * setup a load from the byte stream in a CacheResult. + */ + private void startCacheLoad(CacheResult result) { + if (Config.LOGV) { + Log.v(LOGTAG, "FrameLoader: loading from cache: " + + mListener.url()); + } + // Tell the Listener respond with the cache file + CacheLoader cacheLoader = + new CacheLoader(mListener, result); + mListener.setCacheLoader(cacheLoader); + cacheLoader.load(); + } + + /* + * This function is used by handleHTTPLoad to allow URL + * interception. This can be used to provide alternative load + * methods such as locally stored versions or for debugging. + * + * Returns true if the response was handled by UrlIntercept. + */ + private boolean handleUrlIntercept() { + // Check if the URL can be served from UrlIntercept. If + // successful, return the data just like a cache hit. + CacheResult result = UrlInterceptRegistry.getSurrogate( + mListener.url(), mHeaders); + if(result != null) { + // Intercepted. The data is stored in result.stream. Setup + // a load from the CacheResult. + startCacheLoad(result); + return true; + } + // Not intercepted. Carry on as normal. + return false; + } + + /* + * This function is used by the handleHTTPLoad to setup the cache headers + * correctly. + * Returns true if the response was handled from the cache + */ + private boolean handleCache() { + switch (mCacheMode) { + // This mode is normally used for a reload, it instructs the http + // loader to not use the cached content. + case WebSettings.LOAD_NO_CACHE: + break; + + + // This mode is used when the content should only be loaded from + // the cache. If it is not there, then fail the load. This is used + // to load POST content in a history navigation. + case WebSettings.LOAD_CACHE_ONLY: { + CacheResult result = CacheManager.getCacheFile(mListener.url(), + null); + if (result != null) { + startCacheLoad(result); + } else { + // This happens if WebCore was first told that the POST + // response was in the cache, then when we try to use it + // it has gone. + // Generate a file not found error + int err = EventHandler.FILE_NOT_FOUND_ERROR; + mListener.error(err, mListener.getContext().getText( + EventHandler.errorStringResources[Math.abs(err)]) + .toString()); + } + return true; + } + + // This mode is for when the user is doing a history navigation + // in the browser and should returned cached content regardless + // of it's state. If it is not in the cache, then go to the + // network. + case WebSettings.LOAD_CACHE_ELSE_NETWORK: { + if (Config.LOGV) { + Log.v(LOGTAG, "FrameLoader: checking cache: " + + mListener.url()); + } + // Get the cache file name for the current URL, passing null for + // the validation headers causes no validation to occur + CacheResult result = CacheManager.getCacheFile(mListener.url(), + null); + if (result != null) { + startCacheLoad(result); + return true; + } + break; + } + + // This is the default case, which is to check to see if the + // content in the cache can be used. If it can be used, then + // use it. If it needs revalidation then the relevant headers + // are added to the request. + default: + case WebSettings.LOAD_NORMAL: + return mListener.checkCache(mHeaders); + }// end of switch + + return false; + } + + /** + * Add the static headers that don't change with each request. + */ + private void populateStaticHeaders() { + // Accept header should already be there as they are built by WebCore, + // but in the case they are missing, add some. + String accept = mHeaders.get("Accept"); + if (accept == null || accept.length() == 0) { + mHeaders.put("Accept", HEADER_STR); + } + mHeaders.put("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); + + String acceptLanguage = mSettings.getAcceptLanguage(); + if (acceptLanguage.length() > 0) { + mHeaders.put("Accept-Language", acceptLanguage); + } + + mHeaders.put("User-Agent", mSettings.getUserAgentString()); + } + + /** + * Add the content related headers. These headers contain user private data + * and is not used when we are proxying an untrusted request. + */ + private void populateHeaders() { + + if (mReferrer != null) mHeaders.put("Referer", mReferrer); + if (mContentType != null) mHeaders.put(CONTENT_TYPE, mContentType); + + // if we have an active proxy and have proxy credentials, do pre-emptive + // authentication to avoid an extra round-trip: + if (mNetwork.isValidProxySet()) { + String username; + String password; + /* The proxy credentials can be set in the Network thread */ + synchronized (mNetwork) { + username = mNetwork.getProxyUsername(); + password = mNetwork.getProxyPassword(); + } + if (username != null && password != null) { + // we collect credentials ONLY if the proxy scheme is BASIC!!! + String proxyHeader = RequestHandle.authorizationHeader(true); + mHeaders.put(proxyHeader, + "Basic " + RequestHandle.computeBasicAuthResponse( + username, password)); + } + } + + // Set cookie header + String cookie = CookieManager.getInstance().getCookie( + mListener.getWebAddress()); + if (cookie != null && cookie.length() > 0) { + mHeaders.put("cookie", cookie); + } + } +} |
