/*
  * 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 (WebView.LOGV_ENABLED) {
     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();
 }
  /**
   * 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);
    }
  }
  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;
  }
  /*
   * 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.

    PluginData data = UrlInterceptRegistry.getPluginData(mListener.url(), mHeaders);

    if (data != null) {
      PluginContentLoader loader = new PluginContentLoader(mListener, data);
      loader.load();
      return true;
    }
    // Not intercepted. Carry on as normal.
    return false;
  }
  /**
   * 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;
  }
 /* 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;
 }
  /**
   * 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;
  }