Back to index

lightning-sunbird  0.9+nobinonly
Public Member Functions | Protected Member Functions | Protected Attributes
nsDocumentOpenInfo Class Reference

The nsDocumentOpenInfo contains the state required when a single document is being opened in order to discover the content type... More...

Inheritance diagram for nsDocumentOpenInfo:
Inheritance graph
[legend]
Collaboration diagram for nsDocumentOpenInfo:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 nsDocumentOpenInfo ()
 nsDocumentOpenInfo (nsIInterfaceRequestor *aWindowContext, PRBool aIsContentPreferred, nsURILoader *aURILoader)
NS_DECL_ISUPPORTS nsresult Open (nsIChannel *channel)
nsresult DispatchContent (nsIRequest *request, nsISupports *aCtxt)
nsresult ConvertData (nsIRequest *request, nsIURIContentListener *aListener, const nsACString &aSrcContentType, const nsACString &aOutContentType)
PRBool TryContentListener (nsIURIContentListener *aListener, nsIChannel *aChannel)
 Function to attempt to use aListener to handle the load.
void onDataAvailable (in nsIRequest aRequest, in nsISupports aContext, in nsIInputStream aInputStream, in unsigned long aOffset, in unsigned long aCount)
 Called when the next chunk of data (corresponding to the request) may be read without blocking the calling thread.
void onStartRequest (in nsIRequest aRequest, in nsISupports aContext)
 Called to signify the beginning of an asynchronous request.
void onStopRequest (in nsIRequest aRequest, in nsISupports aContext, in nsresult aStatusCode)
 Called to signify the end of an asynchronous request.

Protected Member Functions

 ~nsDocumentOpenInfo ()

Protected Attributes

nsCOMPtr< nsIURIContentListenerm_contentListener
 The first content listener to try dispatching data to.
nsCOMPtr< nsIStreamListenerm_targetStreamListener
 The stream listener to forward nsIStreamListener notifications to.
nsCOMPtr< nsIInterfaceRequestorm_originalContext
 A pointer to the entity that originated the load.
PRBool mIsContentPreferred
 Boolean to pass to CanHandleContent (also determines whether we use CanHandleContent or IsPreferred).
nsCString mContentType
 The type of the data we will be trying to dispatch.
nsRefPtr< nsURILoadermURILoader
 Reference to the URILoader service so we can access its list of nsIURIContentListeners.

Detailed Description

The nsDocumentOpenInfo contains the state required when a single document is being opened in order to discover the content type...

Each instance remains alive until its target URL has been loaded (or aborted).

Definition at line 97 of file nsURILoader.cpp.


Constructor & Destructor Documentation

Definition at line 188 of file nsURILoader.cpp.

{
  NS_NOTREACHED("This should never be called\n");
}

Here is the caller graph for this function:

nsDocumentOpenInfo::nsDocumentOpenInfo ( nsIInterfaceRequestor aWindowContext,
PRBool  aIsContentPreferred,
nsURILoader aURILoader 
)

Definition at line 193 of file nsURILoader.cpp.

  : m_originalContext(aWindowContext),
    mIsContentPreferred(aIsContentPreferred),
    mURILoader(aURILoader)
{
}

Definition at line 202 of file nsURILoader.cpp.

{
}

Member Function Documentation

nsresult nsDocumentOpenInfo::ConvertData ( nsIRequest request,
nsIURIContentListener aListener,
const nsACString &  aSrcContentType,
const nsACString &  aOutContentType 
)

Definition at line 650 of file nsURILoader.cpp.

{
  LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this,
       PromiseFlatCString(aSrcContentType).get(),
       PromiseFlatCString(aOutContentType).get()));

  NS_PRECONDITION(aSrcContentType != aOutContentType,
                  "ConvertData called when the two types are the same!");
  nsresult rv = NS_OK;

  nsCOMPtr<nsIStreamConverterService> StreamConvService = 
    do_GetService(kStreamConverterServiceCID, &rv);
  if (NS_FAILED(rv)) return rv;

  LOG(("  Got converter service"));
  
  // When applying stream decoders, it is necessary to "insert" an 
  // intermediate nsDocumentOpenInfo instance to handle the targeting of
  // the "final" stream or streams.
  //
  // For certain content types (ie. multi-part/x-mixed-replace) the input
  // stream is split up into multiple destination streams.  This
  // intermediate instance is used to target these "decoded" streams...
  //
  nsCOMPtr<nsDocumentOpenInfo> nextLink =
    new nsDocumentOpenInfo(m_originalContext, mIsContentPreferred, mURILoader);
  if (!nextLink) return NS_ERROR_OUT_OF_MEMORY;

  LOG(("  Downstream DocumentOpenInfo would be: 0x%p", nextLink.get()));
  
  // Make sure nextLink starts with the contentListener that said it wanted the
  // results of this decode.
  nextLink->m_contentListener = aListener;
  // Also make sure it has to look for a stream listener to pump data into.
  nextLink->m_targetStreamListener = nsnull;

  // Make sure that nextLink treats the data as aOutContentType when
  // dispatching; that way even if the stream converters don't change the type
  // on the channel we will still do the right thing.  If aOutContentType is
  // */*, that's OK -- that will just indicate to nextLink that it should get
  // the type off the channel.
  nextLink->mContentType = aOutContentType;

  // The following call sets m_targetStreamListener to the input end of the
  // stream converter and sets the output end of the stream converter to
  // nextLink.  As we pump data into m_targetStreamListener the stream
  // converter will convert it and pass the converted data to nextLink.
  return StreamConvService->AsyncConvertData(PromiseFlatCString(aSrcContentType).get(), 
                                             PromiseFlatCString(aOutContentType).get(), 
                                             nextLink, 
                                             request,
                                             getter_AddRefs(m_targetStreamListener));
}

Here is the call graph for this function:

Here is the caller graph for this function:

nsresult nsDocumentOpenInfo::DispatchContent ( nsIRequest request,
nsISupports *  aCtxt 
)

Definition at line 399 of file nsURILoader.cpp.

{
  LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this, mContentType.get()));

  NS_PRECONDITION(!m_targetStreamListener,
                  "Why do we already have a target stream listener?");
  
  nsresult rv;
  nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
  if (!aChannel) {
    LOG_ERROR(("  Request is not a channel.  Bailing."));
    return NS_ERROR_FAILURE;
  }

  NS_NAMED_LITERAL_CSTRING(anyType, "*/*");
  if (mContentType.IsEmpty() || mContentType == anyType) {
    rv = aChannel->GetContentType(mContentType);
    if (NS_FAILED(rv)) return rv;
    LOG(("  Got type from channel: '%s'", mContentType.get()));
  }

  PRBool isGuessFromExt =
    mContentType.LowerCaseEqualsASCII(APPLICATION_GUESS_FROM_EXT);
  if (isGuessFromExt) {
    // Reset to application/octet-stream for now; no one other than the
    // external helper app service should see APPLICATION_GUESS_FROM_EXT.
    mContentType = APPLICATION_OCTET_STREAM;
    aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
  }

  // Check whether the data should be forced to be handled externally.  This
  // could happen because the Content-Disposition header is set so, or, in the
  // future, because the user has specified external handling for the MIME
  // type.
  PRBool forceExternalHandling = PR_FALSE;
  nsCAutoString disposition;
  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
  nsCOMPtr<nsIURI> uri;
  if (httpChannel)
  {
    rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"),
                                        disposition);
    httpChannel->GetURI(getter_AddRefs(uri));
  }
  else
  {
    nsCOMPtr<nsIMultiPartChannel> multipartChannel(do_QueryInterface(request));
    if (multipartChannel)
    {
      rv = multipartChannel->GetContentDisposition(disposition);
    }
  }

  LOG(("  Disposition header: '%s'", disposition.get()));

  if (NS_SUCCEEDED(rv) && !disposition.IsEmpty())
  {
    nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
    if (NS_SUCCEEDED(rv))
    {
      nsCAutoString fallbackCharset;
      if (uri)
        uri->GetOriginCharset(fallbackCharset);
      nsAutoString dispToken;
      // Get the disposition type
      rv = mimehdrpar->GetParameter(disposition, "", fallbackCharset,
                                    PR_TRUE, nsnull, dispToken);
      // RFC 2183, section 2.8 says that an unknown disposition
      // value should be treated as "attachment"
      // XXXbz this code is duplicated in GetFilenameAndExtensionFromChannel in
      // nsExternalHelperAppService.  Factor it out!
      if (NS_FAILED(rv) || 
          (// Some broken sites just send
           // Content-Disposition: ; filename="file"
           // screen those out here.
           !dispToken.IsEmpty() &&
           !dispToken.LowerCaseEqualsLiteral("inline") &&
          // Broken sites just send
          // Content-Disposition: filename="file"
          // without a disposition token... screen those out.
           !dispToken.EqualsIgnoreCase("filename", 8)) &&
          // Also in use is Content-Disposition: name="file"
           !dispToken.EqualsIgnoreCase("name", 4))
        // We have a content-disposition of "attachment" or unknown
        forceExternalHandling = PR_TRUE;
    }
  }

  LOG(("  forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
    
  // We're going to try to find a contentListener that can handle our data
  nsCOMPtr<nsIURIContentListener> contentListener;
  // The type or data the contentListener wants.
  nsXPIDLCString desiredContentType;

  if (!forceExternalHandling)
  {
    //
    // First step: See whether m_contentListener wants to handle this
    // content type.
    //
    if (m_contentListener && TryContentListener(m_contentListener, aChannel)) {
      LOG(("  Success!  Our default listener likes this type"));
      // All done here
      return NS_OK;
    }

    //
    // Second step: See whether some other registered listener wants
    // to handle this content type.
    //
    PRInt32 count = mURILoader->m_listeners.Count();
    nsCOMPtr<nsIURIContentListener> listener;
    for (PRInt32 i = 0; i < count; i++) {
      listener = do_QueryReferent(mURILoader->m_listeners[i]);
      if (listener) {
        if (TryContentListener(listener, aChannel)) {
          LOG(("  Found listener registered on the URILoader"));
          return NS_OK;
        }
      } else {
        // remove from the listener list, reset i and update count
        mURILoader->m_listeners.RemoveObjectAt(i--);
        --count;
      }
    }

    //
    // Third step: Try to find a content listener that has not yet had
    // the chance to register, as it is contained in a not-yet-loaded
    // module, but which has registered a contract ID.
    //
    nsCOMPtr<nsICategoryManager> catman =
      do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
    if (catman) {
      nsXPIDLCString contractidString;
      rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
                                    mContentType.get(),
                                    getter_Copies(contractidString));
      if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
        LOG(("  Listener contractid for '%s' is '%s'",
             mContentType.get(), contractidString.get()));

        listener = do_CreateInstance(contractidString);
        LOG(("  Listener from category manager: 0x%p", listener.get()));
        
        if (listener && TryContentListener(listener, aChannel)) {
          LOG(("  Listener from category manager likes this type"));
          return NS_OK;
        }
      }
    }

    //
    // Fourth step: try to find an nsIContentHandler for our type.
    //
    nsCAutoString handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX);
    handlerContractID += mContentType;

    nsCOMPtr<nsIContentHandler> contentHandler =
      do_CreateInstance(handlerContractID.get());
    if (contentHandler) {
      LOG(("  Content handler found"));
      rv = contentHandler->HandleContent(mContentType.get(),
                                         m_originalContext, request);
      // XXXbz returning an error code to represent handling the
      // content is just bizarre!
      if (rv != NS_ERROR_WONT_HANDLE_CONTENT) {
        if (NS_FAILED(rv)) {
          // The content handler has unexpectedly failed.  Cancel the request
          // just in case the handler didn't...
          LOG(("  Content handler failed.  Aborting load"));
          request->Cancel(rv);
        }
#ifdef PR_LOGGING
        else {
          LOG(("  Content handler taking over load"));
        }
#endif

        return rv;
      }
    }
  }

  NS_ASSERTION(!m_targetStreamListener,
               "If we found a listener, why are we not using it?");
  
  //
  // Fifth step:  If no listener prefers this type, see if any stream
  //              converters exist to transform this content type into
  //              some other.
  //

  // We always want to do this, since even content being forced to
  // be handled externally may need decoding (eg via the unknown
  // content decoder).
  // Don't do this if the server sent us a MIME type of "*/*" because they saw
  // it in our Accept header and got confused.
  // XXXbz have to be careful here; may end up in some sort of bizarre infinite
  // decoding loop.
  if (mContentType != anyType) {
    rv = ConvertData(request, m_contentListener, mContentType, anyType);
    if (NS_FAILED(rv)) {
      m_targetStreamListener = nsnull;
    } else if (m_targetStreamListener) {
      // We found a converter for this MIME type.  We'll just pump data into it
      // and let the downstream nsDocumentOpenInfo handle things.
      LOG(("  Converter taking over now"));
      return NS_OK;
    }
  }
      
  // Sixth step:
  //
  // All attempts to dispatch this content have failed.  Just pass it off to
  // the helper app service.
  //
  nsCOMPtr<nsIExternalHelperAppService> helperAppService =
    do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
  if (helperAppService) {
    LOG(("  Passing load off to helper app service"));

    // Set these flags to indicate that the channel has been targeted and that
    // we are not using the original consumer.
    nsLoadFlags loadFlags = 0;
    request->GetLoadFlags(&loadFlags);
    request->SetLoadFlags(loadFlags | nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
                                    | nsIChannel::LOAD_TARGETED);

    if (isGuessFromExt) {
      mContentType = APPLICATION_GUESS_FROM_EXT;
      aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
    }

    rv = helperAppService->DoContent(mContentType,
                                     request,
                                     m_originalContext,
                                     getter_AddRefs(m_targetStreamListener));
    if (NS_FAILED(rv)) {
      request->SetLoadFlags(loadFlags);
      m_targetStreamListener = nsnull;
    }
  }
      
  NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
               "There is no way we should be successful at this point without a m_targetStreamListener");
  return rv;
}

Here is the call graph for this function:

void nsIStreamListener::onDataAvailable ( in nsIRequest  aRequest,
in nsISupports  aContext,
in nsIInputStream  aInputStream,
in unsigned long  aOffset,
in unsigned long  aCount 
) [inherited]

Called when the next chunk of data (corresponding to the request) may be read without blocking the calling thread.

The onDataAvailable impl must read exactly |aCount| bytes of data before returning.

Parameters:
aRequestrequest corresponding to the source of the data
aContextuser defined context
aInputStreaminput stream containing the data chunk
aOffsetNumber of bytes that were sent in previous onDataAvailable calls for this request. In other words, the sum of all previous count parameters. If that number is greater than or equal to 2^32, this parameter will be PR_UINT32_MAX (2^32 - 1).
aCountnumber of bytes available in the stream

NOTE: The aInputStream parameter must implement readSegments.

An exception thrown from onDataAvailable has the side-effect of causing the request to be canceled.

void nsIRequestObserver::onStartRequest ( in nsIRequest  aRequest,
in nsISupports  aContext 
) [inherited]

Called to signify the beginning of an asynchronous request.

Parameters:
aRequestrequest being observed
aContextuser defined context

An exception thrown from onStartRequest has the side-effect of causing the request to be canceled.

Here is the caller graph for this function:

void nsIRequestObserver::onStopRequest ( in nsIRequest  aRequest,
in nsISupports  aContext,
in nsresult  aStatusCode 
) [inherited]

Called to signify the end of an asynchronous request.

This call is always preceded by a call to onStartRequest.

Parameters:
aRequestrequest being observed
aContextuser defined context
aStatusCodereason for stopping (NS_OK if completed successfully)

An exception thrown from onStopRequest is generally ignored.

Here is the caller graph for this function:

Definition at line 206 of file nsURILoader.cpp.

{
  LOG(("[0x%p] nsDocumentOpenInfo::Open", this));
    
  // this method is not complete!!! Eventually, we should first go
  // to the content listener and ask them for a protocol handler...
  // if they don't give us one, we need to go to the registry and get
  // the preferred protocol handler. 

  // But for now, I'm going to let necko do the work for us....

  nsresult rv;

  // ask our window context if it has a uri content listener...
  m_contentListener = do_GetInterface(m_originalContext, &rv);
  if (NS_FAILED(rv)) return rv;

  // now just open the channel!
  rv = aChannel->AsyncOpen(this, nsnull);

  // no content from this load - that's OK.
  if (rv == NS_ERROR_DOM_RETVAL_UNDEFINED ||
      rv == NS_ERROR_NO_CONTENT) {
    LOG(("  rv is NS_ERROR_DOM_RETVAL_UNDEFINED or NS_ERROR_NO_CONTENT -- doing nothing"));
    rv = NS_OK;
  }

  return rv;
}

Here is the call graph for this function:

Function to attempt to use aListener to handle the load.

If PR_TRUE is returned, nothing else needs to be done; if PR_FALSE is returned, then a different way of handling the load should be tried.

Definition at line 708 of file nsURILoader.cpp.

{
  LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mIsContentPreferred = %s",
       this, mIsContentPreferred ? "true" : "false"));

  NS_PRECONDITION(aListener, "Must have a non-null listener");
  NS_PRECONDITION(aChannel, "Must have a channel");
  
  PRBool listenerWantsContent = PR_FALSE;
  nsXPIDLCString typeToUse;
  
  if (mIsContentPreferred) {
    aListener->IsPreferred(mContentType.get(),
                           getter_Copies(typeToUse),
                           &listenerWantsContent);
  } else {
    aListener->CanHandleContent(mContentType.get(), PR_FALSE,
                                getter_Copies(typeToUse),
                                &listenerWantsContent);
  }
  if (!listenerWantsContent) {
    LOG(("  Listener is not interested"));
    return PR_FALSE;
  }

  if (!typeToUse.IsEmpty() && typeToUse != mContentType) {
    // Need to do a conversion here.

    nsresult rv = ConvertData(aChannel, aListener, mContentType, typeToUse);

    if (NS_FAILED(rv)) {
      // No conversion path -- we don't want this listener, if we got one
      m_targetStreamListener = nsnull;
    }

    LOG(("  Found conversion: %s", m_targetStreamListener ? "yes" : "no"));
    
    // m_targetStreamListener is now the input end of the converter, and we can
    // just pump the data in there, if it exists.  If it does not, we need to
    // try other nsIURIContentListeners.
    return m_targetStreamListener != nsnull;
  }

  // At this point, aListener wants data of type mContentType.  Let 'em have
  // it.  But first, if we are retargeting, set an appropriate flag on the
  // channel
  nsLoadFlags loadFlags = 0;
  aChannel->GetLoadFlags(&loadFlags);

  // Set this flag to indicate that the channel has been targeted at a final
  // consumer.  This load flag is tested in nsDocLoader::OnProgress.
  nsLoadFlags newLoadFlags = nsIChannel::LOAD_TARGETED;

  nsCOMPtr<nsIURIContentListener> originalListener =
    do_GetInterface(m_originalContext);
  if (originalListener != aListener) {
    newLoadFlags |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
  }
  aChannel->SetLoadFlags(loadFlags | newLoadFlags);
  
  PRBool abort = PR_FALSE;
  nsresult rv = aListener->DoContent(mContentType.get(),
                                     mIsContentPreferred,
                                     aChannel,
                                     getter_AddRefs(m_targetStreamListener),
                                     &abort);
    
  if (NS_FAILED(rv)) {
    LOG_ERROR(("  DoContent failed"));
    
    // Unset the RETARGETED_DOCUMENT_URI flag if we set it...
    aChannel->SetLoadFlags(loadFlags);
    m_targetStreamListener = nsnull;
    return PR_FALSE;
  }

  if (abort) {
    // Nothing else to do here -- aListener is handling it all.  Make
    // sure m_targetStreamListener is null so we don't do anything
    // after this point.
    LOG(("  Listener has aborted the load"));
    m_targetStreamListener = nsnull;
  }

  NS_ASSERTION(abort || m_targetStreamListener, "DoContent returned no listener?");

  // aListener is handling the load from this point on.  
  return PR_TRUE;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

The first content listener to try dispatching data to.

Typically the listener associated with the entity that originated the load.

Definition at line 147 of file nsURILoader.cpp.

A pointer to the entity that originated the load.

We depend on getting things like nsIURIContentListeners, nsIDOMWindows, etc off of it.

Definition at line 159 of file nsURILoader.cpp.

The stream listener to forward nsIStreamListener notifications to.

This is set once the load is dispatched.

Definition at line 153 of file nsURILoader.cpp.

The type of the data we will be trying to dispatch.

Definition at line 170 of file nsURILoader.cpp.

Boolean to pass to CanHandleContent (also determines whether we use CanHandleContent or IsPreferred).

Definition at line 165 of file nsURILoader.cpp.

Reference to the URILoader service so we can access its list of nsIURIContentListeners.

Definition at line 176 of file nsURILoader.cpp.


The documentation for this class was generated from the following file: