/**
  * Create a WebCore response object so that it can be used by nativeReceivedResponse or
  * nativeRedirectedToUrl
  *
  * @return native response pointer
  */
 private int createNativeResponse() {
   // The reason we change HTTP_NOT_MODIFIED to HTTP_OK is because we know
   // that WebCore never sends the if-modified-since header. Our
   // CacheManager does it for us. If the server responds with a 304, then
   // we treat it like it was a 200 code and proceed with loading the file
   // from the cache.
   int statusCode = mStatusCode == HTTP_NOT_MODIFIED ? HTTP_OK : mStatusCode;
   // pass content-type content-length and content-encoding
   final int nativeResponse =
       nativeCreateResponse(
           mUrl,
           statusCode,
           mStatusText,
           mMimeType,
           mContentLength,
           mEncoding,
           mCacheResult == null ? 0 : mCacheResult.expires / 1000);
   if (mHeaders != null) {
     mHeaders.getHeaders(
         new Headers.HeaderCallback() {
           public void header(String name, String value) {
             nativeSetResponseHeader(nativeResponse, name, value);
           }
         });
   }
   return nativeResponse;
 }
 @Override
 public void endData() {
   Xlog.d(XLOGTAG, "endData::mStatusCode is: " + mStatusCode + this);
   if (mStatusCode == 200) {
     if (mPosterBytes.size() > 0) {
       Bitmap poster =
           BitmapFactory.decodeByteArray(mPosterBytes.toByteArray(), 0, mPosterBytes.size());
       mProxy.doSetPoster(poster);
     }
     cleanup();
   } else if (mStatusCode >= 300 && mStatusCode < 400) {
     // We have a redirect.
     try {
       mUrl = new URL(mHeaders.getLocation());
     } catch (MalformedURLException e) {
       mUrl = null;
     }
     if (mUrl != null) {
       mHandler.post(
           new Runnable() {
             @Override
             public void run() {
               if (mRequestHandle != null) {
                 mRequestHandle.setupRedirect(
                     mUrl.toString(), mStatusCode, new HashMap<String, String>());
               }
             }
           });
     }
   }
 }
  /*
   * This function is called from native WebCore code to
   * notify this LoadListener that the content it is currently
   * downloading should be saved to a file and not sent to
   * WebCore.
   */
  void downloadFile() {
    // Setting the Cache Result to null ensures that this
    // content is not added to the cache
    mCacheResult = null;

    // Inform the client that they should download a file
    mBrowserFrame
        .getCallbackProxy()
        .onDownloadStart(
            url(),
            mBrowserFrame.getUserAgentString(),
            mHeaders.getContentDisposition(),
            mMimeType,
            mContentLength);

    // Cancel the download. We need to stop the http load.
    // The native loader object will get cleared by the call to
    // cancel() but will also be cleared on the WebCore side
    // when this function returns.
    cancel();
  }
  /*
   * Perform the actual redirection. This involves setting up the new URL,
   * informing WebCore and then telling the Network to start loading again.
   */
  private void doRedirect() {
    // as cancel() can cancel the load before doRedirect() is
    // called through handleMessage, needs to check to see if we
    // are canceled before proceed
    if (mCancelled) {
      return;
    }

    String redirectTo = mHeaders.getLocation();
    if (redirectTo != null) {
      int nativeResponse = createNativeResponse();
      redirectTo = nativeRedirectedToUrl(mUrl, redirectTo, nativeResponse);
      // nativeRedirectedToUrl() may call cancel(), e.g. when redirect
      // from a https site to a http site, check mCancelled again
      if (mCancelled) {
        return;
      }
      if (redirectTo == null) {
        Log.d(LOGTAG, "Redirection failed for " + mHeaders.getLocation());
        cancel();
        return;
      } else if (!URLUtil.isNetworkUrl(redirectTo)) {
        final String text =
            mContext.getString(com.android.internal.R.string.open_permission_deny)
                + "\n"
                + redirectTo;
        nativeAddData(text.getBytes(), text.length());
        nativeFinished();
        clearNativeLoader();
        return;
      }

      if (mOriginalUrl == null) {
        mOriginalUrl = mUrl;
      }

      // Cache the redirect response
      if (mCacheResult != null) {
        if (getErrorID() == OK) {
          CacheManager.saveCacheFile(mUrl, mCacheResult);
        }
        mCacheResult = null;
      }

      setUrl(redirectTo);

      // Redirect may be in the cache
      if (mRequestHeaders == null) {
        mRequestHeaders = new HashMap<String, String>();
      }
      if (!checkCache(mRequestHeaders)) {
        // mRequestHandle can be null when the request was satisfied
        // by the cache, and the cache returned a redirect
        if (mRequestHandle != null) {
          mRequestHandle.setupRedirect(redirectTo, mStatusCode, mRequestHeaders);
        } else {
          String method = mMethod;

          if (method == null) {
            return;
          }

          Network network = Network.getInstance(getContext());
          network.requestURL(method, mRequestHeaders, mPostData, this, mIsHighPriority);
        }
      }
    } else {
      cancel();
    }

    if (Config.LOGV) {
      Log.v(LOGTAG, "LoadListener.onRedirect(): redirect to: " + redirectTo);
    }
  }
  /**
   * Parse the headers sent from the server.
   *
   * @param headers gives up the HeaderGroup IMPORTANT: as this is called from network thread, can't
   *     call native directly
   */
  public void headers(Headers headers) {
    if (Config.LOGV) Log.v(LOGTAG, "LoadListener.headers");
    if (mCancelled) return;
    mHeaders = headers;
    mMimeType = "";
    mEncoding = "";

    ArrayList<String> cookies = headers.getSetCookie();
    for (int i = 0; i < cookies.size(); ++i) {
      CookieManager.getInstance().setCookie(mUri, cookies.get(i));
    }

    long contentLength = headers.getContentLength();
    if (contentLength != Headers.NO_CONTENT_LENGTH) {
      mContentLength = contentLength;
    } else {
      mContentLength = 0;
    }

    String contentType = headers.getContentType();
    if (contentType != null) {
      parseContentTypeHeader(contentType);

      // If we have one of "generic" MIME types, try to deduce
      // the right MIME type from the file extension (if any):
      if (mMimeType.equalsIgnoreCase("text/plain")
          || mMimeType.equalsIgnoreCase("application/octet-stream")) {

        String newMimeType = guessMimeTypeFromExtension();
        if (newMimeType != null) {
          mMimeType = newMimeType;
        }
      } else if (mMimeType.equalsIgnoreCase("text/vnd.wap.wml")) {
        // As we don't support wml, render it as plain text
        mMimeType = "text/plain";
      } else {
        // XXX: Until the servers send us either correct xhtml or
        // text/html, treat application/xhtml+xml as text/html.
        // It seems that xhtml+xml and vnd.wap.xhtml+xml mime
        // subtypes are used interchangeably. So treat them the same.
        if (mMimeType.equalsIgnoreCase("application/xhtml+xml")
            || mMimeType.equals("application/vnd.wap.xhtml+xml")) {
          mMimeType = "text/html";
        }
      }
    } else {
      /* Often when servers respond with 304 Not Modified or a
      Redirect, then they don't specify a MIMEType. When this
      occurs, the function below is called.  In the case of
      304 Not Modified, the cached headers are used rather
      than the headers that are returned from the server. */
      guessMimeType();
    }

    // is it an authentication request?
    boolean mustAuthenticate = (mStatusCode == HTTP_AUTH || mStatusCode == HTTP_PROXY_AUTH);
    // is it a proxy authentication request?
    boolean isProxyAuthRequest = (mStatusCode == HTTP_PROXY_AUTH);
    // is this authentication request due to a failed attempt to
    // authenticate ealier?
    mAuthFailed = false;

    // if we tried to authenticate ourselves last time
    if (mAuthHeader != null) {
      // we failed, if we must to authenticate again now and
      // we have a proxy-ness match
      mAuthFailed = (mustAuthenticate && isProxyAuthRequest == mAuthHeader.isProxy());

      // if we did NOT fail and last authentication request was a
      // proxy-authentication request
      if (!mAuthFailed && mAuthHeader.isProxy()) {
        Network network = Network.getInstance(mContext);
        // if we have a valid proxy set
        if (network.isValidProxySet()) {
          /* The proxy credentials can be read in the WebCore thread
           */
          synchronized (network) {
            // save authentication credentials for pre-emptive proxy
            // authentication
            network.setProxyUsername(mAuthHeader.getUsername());
            network.setProxyPassword(mAuthHeader.getPassword());
          }
        }
      }
    }
    // it is only here that we can reset the last mAuthHeader object
    // (if existed) and start a new one!!!
    mAuthHeader = null;
    if (mustAuthenticate) {
      if (mStatusCode == HTTP_AUTH) {
        mAuthHeader = parseAuthHeader(headers.getWwwAuthenticate());
      } else {
        mAuthHeader = parseAuthHeader(headers.getProxyAuthenticate());
        // if successfully parsed the header
        if (mAuthHeader != null) {
          // mark the auth-header object as a proxy
          mAuthHeader.setProxy();
        }
      }
    }

    // Only create a cache file if the server has responded positively.
    if ((mStatusCode == HTTP_OK
            || mStatusCode == HTTP_FOUND
            || mStatusCode == HTTP_MOVED_PERMANENTLY
            || mStatusCode == HTTP_TEMPORARY_REDIRECT)
        && mNativeLoader != 0) {
      // Content arriving from a StreamLoader (eg File, Cache or Data)
      // will not be cached as they have the header:
      // cache-control: no-store
      mCacheResult = CacheManager.createCacheFile(mUrl, mStatusCode, headers, mMimeType, false);
      if (mCacheResult != null) {
        mCacheResult.encoding = mEncoding;
      }
    }
    sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS));
  }