/** * Issues the load request. * * <p>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 (WebView.LOGV_ENABLED) { 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; }
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 (WebView.LOGV_ENABLED) { 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; }
/** * Guesses MIME type if one was not specified. Defaults to 'text/html'. In addition, tries to * guess the MIME type based on the extension. */ private void guessMimeType() { // Data urls must have a valid mime type or a blank string for the mime // type (implying text/plain). if (URLUtil.isDataUrl(mUrl) && mMimeType.length() != 0) { cancel(); final String text = mContext.getString(com.android.internal.R.string.httpErrorBadUrl); error(EventHandler.ERROR_BAD_URL, text); } else { // Note: This is ok because this is used only for the main content // of frames. If no content-type was specified, it is fine to // default to text/html. mMimeType = "text/html"; String newMimeType = guessMimeTypeFromExtension(); if (newMimeType != null) { mMimeType = newMimeType; } } }
/** * Start loading a resource. * * @param loaderHandle The native ResourceLoader that is the target of the data. * @param url The url to load. * @param method The http method. * @param headers The http headers. * @param postData If the method is "POST" postData is sent as the request body. Is null when * empty. * @param cacheMode The cache mode to use when loading this resource. * @param isHighPriority True if this resource needs to be put at the front of the network queue. * @param synchronous True if the load is synchronous. * @return A newly created LoadListener object. */ private LoadListener startLoadingResource( int loaderHandle, String url, String method, HashMap headers, byte[] postData, int cacheMode, boolean isHighPriority, boolean synchronous) { PerfChecker checker = new PerfChecker(); if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) { cacheMode = mSettings.getCacheMode(); } if (method.equals("POST")) { // Don't use the cache on POSTs when issuing a normal POST // request. if (cacheMode == WebSettings.LOAD_NORMAL) { cacheMode = WebSettings.LOAD_NO_CACHE; } if (mSettings.getSavePassword() && hasPasswordField()) { try { if (WebView.DEBUG) { Assert.assertNotNull(mCallbackProxy.getBackForwardList().getCurrentItem()); } WebAddress uri = new WebAddress(mCallbackProxy.getBackForwardList().getCurrentItem().getUrl()); String schemePlusHost = uri.mScheme + uri.mHost; String[] ret = getUsernamePassword(); // Has the user entered a username/password pair and is // there some POST data if (ret != null && postData != null && ret[0].length() > 0 && ret[1].length() > 0) { // 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(ret[0])) && postString.contains(URLEncoder.encode(ret[1]))) { 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, ret[0], ret[1]); } } else { // CallbackProxy will handle creating the resume // message mCallbackProxy.onSavePassword(schemePlusHost, ret[0], ret[1], null); } } } } catch (ParseException ex) { // if it is bad uri, don't save its password } } } // is this resource the main-frame top-level page? boolean isMainFramePage = mIsMainFrame; if (WebView.LOGV_ENABLED) { Log.v( LOGTAG, "startLoadingResource: url=" + url + ", method=" + method + ", postData=" + postData + ", isHighPriority=" + isHighPriority + ", isMainFramePage=" + isMainFramePage); } // Create a LoadListener LoadListener loadListener = LoadListener.getLoadListener( mContext, this, url, loaderHandle, synchronous, isMainFramePage); mCallbackProxy.onLoadResource(url); if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) { loadListener.error( android.net.http.EventHandler.ERROR, mContext.getString(com.android.internal.R.string.httpErrorTooManyRequests)); loadListener.notifyError(); loadListener.tearDown(); return null; } // during synchronous load, the WebViewCore thread is blocked, so we // need to endCacheTransaction first so that http thread won't be // blocked in setupFile() when createCacheFile. if (synchronous) { CacheManager.endCacheTransaction(); } FrameLoader loader = new FrameLoader(loadListener, mSettings, method, isHighPriority); loader.setHeaders(headers); loader.setPostData(postData); // Set the load mode to the mode used for the current page. // If WebKit wants validation, go to network directly. loader.setCacheMode( headers.containsKey("If-Modified-Since") || headers.containsKey("If-None-Match") ? WebSettings.LOAD_NO_CACHE : cacheMode); // Set referrer to current URL? if (!loader.executeLoad()) { checker.responseAlert("startLoadingResource fail"); } checker.responseAlert("startLoadingResource succeed"); if (synchronous) { CacheManager.startCacheTransaction(); } return !synchronous ? loadListener : null; }
/* * 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 (WebView.LOGV_ENABLED) { 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; }
/** * Start loading a resource. * * @param loaderHandle The native ResourceLoader that is the target of the data. * @param url The url to load. * @param method The http method. * @param headers The http headers. * @param postData If the method is "POST" postData is sent as the request body. Is null when * empty. * @param postDataIdentifier If the post data contained form this is the form identifier, * otherwise it is 0. * @param cacheMode The cache mode to use when loading this resource. See WebSettings.setCacheMode * @param mainResource True if the this resource is the main request, not a supporting resource * @param userGesture * @param synchronous True if the load is synchronous. * @return A newly created LoadListener object. */ private LoadListener startLoadingResource( int loaderHandle, String url, String method, HashMap headers, byte[] postData, long postDataIdentifier, int cacheMode, boolean mainResource, boolean userGesture, boolean synchronous, String username, String password) { if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) { cacheMode = mSettings.getCacheMode(); } if (method.equals("POST")) { // Don't use the cache on POSTs when issuing a normal POST // request. if (cacheMode == WebSettings.LOAD_NORMAL) { cacheMode = WebSettings.LOAD_NO_CACHE; } String[] ret = getUsernamePassword(); if (ret != null) { String domUsername = ret[0]; String domPassword = ret[1]; maybeSavePassword(postData, domUsername, domPassword); } } // is this resource the main-frame top-level page? boolean isMainFramePage = mIsMainFrame; if (DebugFlags.BROWSER_FRAME) { Log.v( LOGTAG, "startLoadingResource: url=" + url + ", method=" + method + ", postData=" + postData + ", isMainFramePage=" + isMainFramePage + ", mainResource=" + mainResource + ", userGesture=" + userGesture); } // Create a LoadListener LoadListener loadListener = LoadListener.getLoadListener( mContext, this, url, loaderHandle, synchronous, isMainFramePage, mainResource, userGesture, postDataIdentifier, username, password); if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) { // send an error message, so that loadListener can be deleted // after this is returned. This is important as LoadListener's // nativeError will remove the request from its DocLoader's request // list. But the set up is not done until this method is returned. loadListener.error( android.net.http.EventHandler.ERROR, mContext.getString(com.android.internal.R.string.httpErrorTooManyRequests)); return loadListener; } // Note that we are intentionally skipping // inputStreamForAndroidResource. This is so that FrameLoader will use // the various StreamLoader classes to handle assets. FrameLoader loader = new FrameLoader( loadListener, mSettings, method, mCallbackProxy.shouldInterceptRequest(url)); loader.setHeaders(headers); loader.setPostData(postData); // Set the load mode to the mode used for the current page. // If WebKit wants validation, go to network directly. loader.setCacheMode( headers.containsKey("If-Modified-Since") || headers.containsKey("If-None-Match") ? WebSettings.LOAD_NO_CACHE : cacheMode); loader.executeLoad(); // Set referrer to current URL? return !synchronous ? loadListener : null; }