Back to index

lightning-sunbird  0.9+nobinonly
nsDirectoryViewer.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Chris Waterson           <waterson@netscape.com>
00024  *   Robert John Churchill    <rjc@netscape.com>
00025  *   Pierre Phaneuf <pp@ludusdesign.com>
00026  *   Bradley Baetz <bbaetz@student.usyd.edu.au>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 /*
00043 
00044   A directory viewer object. Parses "application/http-index-format"
00045   per Lou Montulli's original spec:
00046 
00047   http://www.mozilla.org/projects/netlib/dirindexformat.html
00048 
00049   One added change is for a description entry, for when the
00050   target does not match the filename (ie gopher)
00051 
00052 */
00053 
00054 #include "nsDirectoryViewer.h"
00055 #include "nsIDirIndex.h"
00056 #include "jsapi.h"
00057 #include "nsCOMPtr.h"
00058 #include "nsCRT.h"
00059 #include "nsEscape.h"
00060 #include "nsIDocumentLoader.h"
00061 #include "nsIEnumerator.h"
00062 #include "nsIRDFService.h"
00063 #include "nsIScriptContext.h"
00064 #include "nsIScriptGlobalObject.h"
00065 #include "nsIServiceManager.h"
00066 #include "nsISupportsArray.h"
00067 #include "nsIXPConnect.h"
00068 #include "nsEnumeratorUtils.h"
00069 #include "nsRDFCID.h"
00070 #include "nsString.h"
00071 #include "nsXPIDLString.h"
00072 #include "nsReadableUtils.h"
00073 #include "rdf.h"
00074 #include "nsITextToSubURI.h"
00075 #include "nsIInterfaceRequestor.h"
00076 #include "nsIInterfaceRequestorUtils.h"
00077 #include "nsIFTPChannel.h"
00078 #include "nsIWindowWatcher.h"
00079 #include "nsIPrompt.h"
00080 #include "nsIAuthPrompt.h"
00081 #include "nsIProgressEventSink.h"
00082 #include "nsIDOMWindow.h"
00083 #include "nsIDOMWindowInternal.h"
00084 #include "nsIDOMWindowCollection.h"
00085 #include "nsIDOMDocument.h"
00086 #include "nsIDOMElement.h"
00087 #include "nsIDOMText.h"
00088 #include "nsIPrefService.h"
00089 #include "nsIPrefBranch.h"
00090 #include "nsIStreamConverterService.h"
00091 #include "nsICategoryManager.h"
00092 #include "nsXPCOMCID.h"
00093 
00094 static const int FORMAT_HTML = 2;
00095 static const int FORMAT_XUL = 3;
00096 
00097 //----------------------------------------------------------------------
00098 //
00099 // Common CIDs
00100 //
00101 
00102 static NS_DEFINE_CID(kRDFServiceCID,             NS_RDFSERVICE_CID);
00103 
00104 // Various protocols we have to special case
00105 static const char               kFTPProtocol[] = "ftp://";
00106 static const char               kGopherProtocol[] = "gopher://";
00107 
00108 //----------------------------------------------------------------------
00109 //
00110 // nsHTTPIndex
00111 //
00112 
00113 NS_IMPL_THREADSAFE_ISUPPORTS7(nsHTTPIndex,
00114                               nsIHTTPIndex,
00115                               nsIRDFDataSource,
00116                               nsIStreamListener,
00117                               nsIDirIndexListener,
00118                               nsIRequestObserver,
00119                               nsIInterfaceRequestor,
00120                               nsIFTPEventSink)
00121 
00122 NS_IMETHODIMP
00123 nsHTTPIndex::GetInterface(const nsIID &anIID, void **aResult ) 
00124 {
00125     if (anIID.Equals(NS_GET_IID(nsIFTPEventSink))) {
00126         // If we don't have a container to store the logged data
00127         // then don't report ourselves back to the caller
00128 
00129         if (!mRequestor)
00130           return NS_ERROR_NO_INTERFACE;
00131         *aResult = NS_STATIC_CAST(nsIFTPEventSink*, this);
00132         NS_ADDREF(this);
00133         return NS_OK;
00134     }
00135 
00136     if (anIID.Equals(NS_GET_IID(nsIPrompt))) {
00137         
00138         if (!mRequestor) 
00139             return NS_ERROR_NO_INTERFACE;
00140 
00141         nsCOMPtr<nsIDOMWindow> aDOMWindow = do_GetInterface(mRequestor);
00142         if (!aDOMWindow) 
00143             return NS_ERROR_NO_INTERFACE;
00144 
00145         nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
00146         
00147         return wwatch->GetNewPrompter(aDOMWindow, (nsIPrompt**)aResult);
00148     }  
00149 
00150     if (anIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
00151         
00152         if (!mRequestor) 
00153             return NS_ERROR_NO_INTERFACE;
00154 
00155         nsCOMPtr<nsIDOMWindow> aDOMWindow = do_GetInterface(mRequestor);
00156         if (!aDOMWindow) 
00157             return NS_ERROR_NO_INTERFACE;
00158 
00159         nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
00160         
00161         return wwatch->GetNewAuthPrompter(aDOMWindow, (nsIAuthPrompt**)aResult);
00162     }  
00163 
00164     if (anIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
00165 
00166         if (!mRequestor) 
00167             return NS_ERROR_NO_INTERFACE;
00168 
00169         nsCOMPtr<nsIProgressEventSink> sink = do_GetInterface(mRequestor);
00170         if (!sink) 
00171             return NS_ERROR_NO_INTERFACE;
00172         
00173         *aResult = sink;
00174         NS_ADDREF((nsISupports*)*aResult);
00175         return NS_OK;
00176     }
00177 
00178     return NS_ERROR_NO_INTERFACE;
00179 }
00180 
00181 NS_IMETHODIMP 
00182 nsHTTPIndex::OnFTPControlLog(PRBool server, const char *msg)
00183 {
00184     NS_ENSURE_TRUE(mRequestor, NS_OK);
00185 
00186     nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(do_GetInterface(mRequestor));
00187     NS_ENSURE_TRUE(scriptGlobal, NS_OK);
00188 
00189     nsIScriptContext *context = scriptGlobal->GetContext();
00190     NS_ENSURE_TRUE(context, NS_OK);
00191 
00192     JSContext* jscontext = NS_REINTERPRET_CAST(JSContext*,
00193                                                context->GetNativeContext());
00194     NS_ENSURE_TRUE(jscontext, NS_OK);
00195 
00196     JSObject* global = JS_GetGlobalObject(jscontext);
00197     NS_ENSURE_TRUE(global, NS_OK);
00198 
00199     jsval params[2];
00200 
00201     nsString unicodeMsg;
00202     unicodeMsg.AssignWithConversion(msg);
00203     JSString* jsMsgStr = JS_NewUCStringCopyZ(jscontext, (jschar*) unicodeMsg.get());
00204 
00205     params[0] = BOOLEAN_TO_JSVAL(server);
00206     params[1] = STRING_TO_JSVAL(jsMsgStr);
00207     
00208     jsval val;
00209     JS_CallFunctionName(jscontext, 
00210                         global, 
00211                         "OnFTPControlLog",
00212                         2, 
00213                         params, 
00214                         &val);
00215     return NS_OK;
00216 }
00217 
00218 NS_IMETHODIMP
00219 nsHTTPIndex::SetEncoding(const char *encoding)
00220 {
00221     mEncoding = encoding;
00222     return(NS_OK);
00223 }
00224 
00225 NS_IMETHODIMP
00226 nsHTTPIndex::GetEncoding(char **encoding)
00227 {
00228   NS_PRECONDITION(encoding, "null ptr");
00229   if (! encoding)
00230     return(NS_ERROR_NULL_POINTER);
00231   
00232   *encoding = ToNewCString(mEncoding);
00233   if (!*encoding)
00234     return(NS_ERROR_OUT_OF_MEMORY);
00235   
00236   return(NS_OK);
00237 }
00238 
00239 NS_IMETHODIMP
00240 nsHTTPIndex::OnStartRequest(nsIRequest *request, nsISupports* aContext)
00241 {
00242   nsresult rv;
00243 
00244   mParser = do_CreateInstance(NS_DIRINDEXPARSER_CONTRACTID, &rv);
00245   if (NS_FAILED(rv)) return rv;
00246   
00247   rv = mParser->SetEncoding(mEncoding.get());
00248   if (NS_FAILED(rv)) return rv;
00249 
00250   rv = mParser->SetListener(this);
00251   if (NS_FAILED(rv)) return rv;
00252 
00253   rv = mParser->OnStartRequest(request,aContext);
00254   if (NS_FAILED(rv)) return rv;
00255 
00256   // This should only run once...
00257   // Unless we don't have a container to start with
00258   // (ie called from bookmarks as an rdf datasource)
00259   if (mBindToGlobalObject && mRequestor) {
00260     mBindToGlobalObject = PR_FALSE;
00261 
00262     // Now get the content viewer container's script object.
00263     nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(do_GetInterface(mRequestor));
00264     NS_ENSURE_TRUE(scriptGlobal, NS_ERROR_FAILURE);
00265 
00266     nsIScriptContext *context = scriptGlobal->GetContext();
00267     NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
00268 
00269     JSContext* jscontext = NS_REINTERPRET_CAST(JSContext*,
00270                                                context->GetNativeContext());
00271     JSObject* global = JS_GetGlobalObject(jscontext);
00272 
00273     // Using XPConnect, wrap the HTTP index object...
00274     static NS_DEFINE_CID(kXPConnectCID, NS_XPCONNECT_CID);
00275     nsCOMPtr<nsIXPConnect> xpc(do_GetService(kXPConnectCID, &rv));
00276     if (NS_FAILED(rv)) return rv;
00277 
00278     nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
00279     rv = xpc->WrapNative(jscontext,
00280                          global,
00281                          NS_STATIC_CAST(nsIHTTPIndex*, this),
00282                          NS_GET_IID(nsIHTTPIndex),
00283                          getter_AddRefs(wrapper));
00284 
00285     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to xpconnect-wrap http-index");
00286     if (NS_FAILED(rv)) return rv;
00287 
00288     JSObject* jsobj;
00289     rv = wrapper->GetJSObject(&jsobj);
00290     NS_ASSERTION(NS_SUCCEEDED(rv),
00291                  "unable to get jsobj from xpconnect wrapper");
00292     if (NS_FAILED(rv)) return rv;
00293 
00294     jsval jslistener = OBJECT_TO_JSVAL(jsobj);
00295 
00296     // ...and stuff it into the global context
00297     PRBool ok;
00298     ok = JS_SetProperty(jscontext, global, "HTTPIndex", &jslistener);
00299 
00300     NS_ASSERTION(ok, "unable to set Listener property");
00301     if (! ok)
00302       return NS_ERROR_FAILURE;
00303   }
00304   if (!aContext) {
00305     nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
00306     NS_ASSERTION(channel, "request should be a channel");
00307 
00308     // lets hijack the notifications:
00309     channel->SetNotificationCallbacks(this);
00310 
00311     // now create the top most resource
00312     nsCOMPtr<nsIURI> uri;
00313     channel->GetURI(getter_AddRefs(uri));
00314       
00315     nsCAutoString entryuriC;
00316     uri->GetSpec(entryuriC);
00317 
00318     nsCOMPtr<nsIRDFResource> entry;
00319     rv = mDirRDF->GetResource(entryuriC, getter_AddRefs(entry));
00320     
00321     NS_ConvertUTF8toUCS2 uriUnicode(entryuriC);
00322 
00323     nsCOMPtr<nsIRDFLiteral> URLVal;
00324     rv = mDirRDF->GetLiteral(uriUnicode.get(), getter_AddRefs(URLVal));
00325 
00326     Assert(entry, kNC_URL, URLVal, PR_TRUE);
00327     mDirectory = do_QueryInterface(entry);
00328   }
00329   else
00330   {
00331     // Get the directory from the context
00332     mDirectory = do_QueryInterface(aContext);
00333   }
00334 
00335   if (!mDirectory) {
00336       request->Cancel(NS_BINDING_ABORTED);
00337       return NS_BINDING_ABORTED;
00338   }
00339 
00340   // Mark the directory as "loading"
00341   rv = Assert(mDirectory, kNC_Loading,
00342                            kTrueLiteral, PR_TRUE);
00343   if (NS_FAILED(rv)) return rv;
00344 
00345   return NS_OK;
00346 }
00347 
00348 
00349 NS_IMETHODIMP
00350 nsHTTPIndex::OnStopRequest(nsIRequest *request,
00351                            nsISupports* aContext,
00352                            nsresult aStatus)
00353 {
00354   // If mDirectory isn't set, then we should just bail. Either an
00355   // error occurred and OnStartRequest() never got called, or
00356   // something exploded in OnStartRequest().
00357   if (! mDirectory)
00358     return NS_BINDING_ABORTED;
00359 
00360   mParser->OnStopRequest(request,aContext,aStatus);
00361 
00362   nsresult rv;
00363 
00364   nsXPIDLCString commentStr;
00365   mParser->GetComment(getter_Copies(commentStr));
00366 
00367   nsCOMPtr<nsIRDFLiteral> comment;
00368   rv = mDirRDF->GetLiteral(NS_ConvertASCIItoUCS2(commentStr).get(), getter_AddRefs(comment));
00369   if (NS_FAILED(rv)) return rv;
00370 
00371   rv = Assert(mDirectory, kNC_Comment, comment, PR_TRUE);
00372   if (NS_FAILED(rv)) return rv;
00373 
00374   // hack: Remove the 'loading' annotation (ignore errors)
00375   AddElement(mDirectory, kNC_Loading, kTrueLiteral);
00376 
00377   return NS_OK;
00378 }
00379 
00380 
00381 NS_IMETHODIMP
00382 nsHTTPIndex::OnDataAvailable(nsIRequest *request,
00383                              nsISupports* aContext,
00384                              nsIInputStream* aStream,
00385                              PRUint32 aSourceOffset,
00386                              PRUint32 aCount)
00387 {
00388   // If mDirectory isn't set, then we should just bail. Either an
00389   // error occurred and OnStartRequest() never got called, or
00390   // something exploded in OnStartRequest().
00391   if (! mDirectory)
00392     return NS_BINDING_ABORTED;
00393 
00394   return mParser->OnDataAvailable(request, mDirectory, aStream, aSourceOffset, aCount);
00395 }
00396 
00397 
00398 nsresult
00399 nsHTTPIndex::OnIndexAvailable(nsIRequest* aRequest, nsISupports *aContext,
00400                               nsIDirIndex* aIndex)
00401 {
00402   nsCOMPtr<nsIRDFResource>  parentRes = do_QueryInterface(aContext);
00403   if (!parentRes) {
00404     NS_ERROR("Could not obtain parent resource");
00405     return(NS_ERROR_UNEXPECTED);
00406   }
00407   
00408   const char* baseStr;
00409   parentRes->GetValueConst(&baseStr);
00410   if (! baseStr) {
00411     NS_ERROR("Could not reconstruct base uri\n");
00412     return NS_ERROR_UNEXPECTED;
00413   }
00414 
00415   // we found the filename; construct a resource for its entry
00416   nsCAutoString entryuriC(baseStr);
00417 
00418   // gopher resources don't point to an entry in the same directory
00419   // like ftp uris. So the entryuriC is just a unique string, while
00420   // the URL attribute is the destination of this element
00421   // The naming scheme for the attributes is taken from the bookmarks
00422   nsXPIDLCString filename;
00423   nsresult rv = aIndex->GetLocation(getter_Copies(filename));
00424   if (NS_FAILED(rv)) return rv;
00425   entryuriC.Append(filename);
00426 
00427   // if its a directory, make sure it ends with a trailing slash.
00428   // This doesn't matter for gopher, (where directories don't have
00429   // to end in a trailing /), because the filename is used for the URL
00430   // attribute.
00431   PRUint32 type;
00432   rv = aIndex->GetType(&type);
00433   if (NS_FAILED(rv))
00434     return rv;
00435 
00436   PRBool isDirType = (type == nsIDirIndex::TYPE_DIRECTORY);
00437 
00438   if (isDirType) {
00439       entryuriC.Append('/');
00440   }
00441 
00442   nsCOMPtr<nsIRDFResource> entry;
00443   rv = mDirRDF->GetResource(entryuriC, getter_AddRefs(entry));
00444 
00445   // At this point, we'll (hopefully) have found the filename and
00446   // constructed a resource for it, stored in entry. So now take a
00447   // second pass through the values and add as statements to the RDF
00448   // datasource.
00449 
00450   if (entry && NS_SUCCEEDED(rv)) {
00451     nsCOMPtr<nsIRDFLiteral> lit;
00452     nsString str;
00453 
00454     // For gopher, the target is the filename. We still have to do all
00455     // the above string manipulation though, because we need the entryuric
00456     // as the key for the RDF data source
00457     if (!strncmp(entryuriC.get(), kGopherProtocol, sizeof(kGopherProtocol)-1))
00458       str.AssignWithConversion(filename);
00459     else {
00460       str.AssignWithConversion(entryuriC.get());
00461     }
00462 
00463     rv = mDirRDF->GetLiteral(str.get(), getter_AddRefs(lit));
00464 
00465     if (NS_SUCCEEDED(rv)) {
00466       rv = Assert(entry, kNC_URL, lit, PR_TRUE);
00467       if (NS_FAILED(rv)) return rv;
00468       
00469       nsXPIDLString xpstr;
00470 
00471       // description
00472       rv = aIndex->GetDescription(getter_Copies(xpstr));
00473       if (NS_FAILED(rv)) return rv;
00474 
00475       rv = mDirRDF->GetLiteral(xpstr.get(), getter_AddRefs(lit));
00476       if (NS_FAILED(rv)) return rv;
00477       rv = Assert(entry, kNC_Description, lit, PR_TRUE);
00478       if (NS_FAILED(rv)) return rv;
00479       
00480       // contentlength
00481       PRInt64 size;
00482       rv = aIndex->GetSize(&size);
00483       if (NS_FAILED(rv)) return rv;
00484       PRInt64 minus1 = LL_MAXUINT;
00485       if (LL_NE(size, minus1)) {
00486         PRInt32 intSize;
00487         LL_L2I(intSize, size);
00488         // XXX RDF should support 64 bit integers (bug 240160)
00489         nsCOMPtr<nsIRDFInt> val;
00490         rv = mDirRDF->GetIntLiteral(intSize, getter_AddRefs(val));
00491         if (NS_FAILED(rv)) return rv;
00492         rv = Assert(entry, kNC_ContentLength, val, PR_TRUE);
00493         if (NS_FAILED(rv)) return rv;
00494       }
00495 
00496       // lastmodified
00497       PRTime tm;
00498       rv = aIndex->GetLastModified(&tm);
00499       if (NS_FAILED(rv)) return rv;
00500       if (tm != -1) {
00501         nsCOMPtr<nsIRDFDate> val;
00502         rv = mDirRDF->GetDateLiteral(tm, getter_AddRefs(val));
00503         if (NS_FAILED(rv)) return rv;
00504         rv = Assert(entry, kNC_LastModified, val, PR_TRUE);
00505       }
00506 
00507       // filetype
00508       PRUint32 type;
00509       rv = aIndex->GetType(&type);
00510       switch (type) {
00511       case nsIDirIndex::TYPE_UNKNOWN:
00512         rv = mDirRDF->GetLiteral(NS_LITERAL_STRING("UNKNOWN").get(), getter_AddRefs(lit));
00513         break;
00514       case nsIDirIndex::TYPE_DIRECTORY:
00515         rv = mDirRDF->GetLiteral(NS_LITERAL_STRING("DIRECTORY").get(), getter_AddRefs(lit));
00516         break;
00517       case nsIDirIndex::TYPE_FILE:
00518         rv = mDirRDF->GetLiteral(NS_LITERAL_STRING("FILE").get(), getter_AddRefs(lit));
00519         break;
00520       case nsIDirIndex::TYPE_SYMLINK:
00521         rv = mDirRDF->GetLiteral(NS_LITERAL_STRING("SYMLINK").get(), getter_AddRefs(lit));
00522         break;
00523       }
00524       
00525       if (NS_FAILED(rv)) return rv;
00526       rv = Assert(entry, kNC_FileType, lit, PR_TRUE);
00527       if (NS_FAILED(rv)) return rv;
00528     }
00529 
00530     // Since the definition of a directory depends on the protocol, we would have
00531     // to do string comparisons all the time.
00532     // But we're told if we're a container right here - so save that fact
00533     if (isDirType)
00534       Assert(entry, kNC_IsContainer, kTrueLiteral, PR_TRUE);
00535     else
00536       Assert(entry, kNC_IsContainer, kFalseLiteral, PR_TRUE);
00537     
00538 //   instead of
00539 //       rv = Assert(parentRes, kNC_Child, entry, PR_TRUE);
00540 //       if (NS_FAILED(rv)) return rv;
00541 //   defer insertion onto a timer so that the UI isn't starved
00542     AddElement(parentRes, kNC_Child, entry);
00543   }
00544 
00545   return rv;
00546 }
00547 
00548 nsresult
00549 nsHTTPIndex::OnInformationAvailable(nsIRequest *aRequest,
00550                                   nsISupports *aCtxt,
00551                                   const nsAString& aInfo) {
00552   return NS_ERROR_NOT_IMPLEMENTED;
00553 }
00554 
00555 //----------------------------------------------------------------------
00556 //
00557 // nsHTTPIndex implementation
00558 //
00559 
00560 nsHTTPIndex::nsHTTPIndex()
00561   : mBindToGlobalObject(PR_TRUE),
00562     mRequestor(nsnull)
00563 {
00564 }
00565 
00566 
00567 nsHTTPIndex::nsHTTPIndex(nsIInterfaceRequestor* aRequestor)
00568   : mBindToGlobalObject(PR_TRUE),
00569     mRequestor(aRequestor)
00570 {
00571 }
00572 
00573 
00574 nsHTTPIndex::~nsHTTPIndex()
00575 {
00576   // note: these are NOT statics due to the native of nsHTTPIndex
00577   // where it may or may not be treated as a singleton
00578 
00579     if (mTimer)
00580     {
00581         // be sure to cancel the timer, as it holds a
00582         // weak reference back to nsHTTPIndex
00583         mTimer->Cancel();
00584         mTimer = nsnull;
00585     }
00586 
00587     mConnectionList = nsnull;
00588     mNodeList = nsnull;
00589     
00590     if (mDirRDF)
00591       {
00592         // UnregisterDataSource() may fail; just ignore errors
00593         mDirRDF->UnregisterDataSource(this);
00594       }
00595 }
00596 
00597 
00598 
00599 nsresult
00600 nsHTTPIndex::CommonInit()
00601 {
00602     nsresult  rv = NS_OK;
00603 
00604     // set initial/default encoding to ISO-8859-1 (not UTF-8)
00605     mEncoding = "ISO-8859-1";
00606 
00607     mDirRDF = do_GetService(kRDFServiceCID, &rv);
00608     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
00609     if (NS_FAILED(rv)) {
00610       return(rv);
00611     }
00612 
00613     mInner = do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource", &rv);
00614 
00615     if (NS_FAILED(rv))
00616       return rv;
00617 
00618     mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "child"),
00619                          getter_AddRefs(kNC_Child));
00620     mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "loading"),
00621                          getter_AddRefs(kNC_Loading));
00622     mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Comment"),
00623                          getter_AddRefs(kNC_Comment));
00624     mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "URL"),
00625                          getter_AddRefs(kNC_URL));
00626     mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Name"),
00627                          getter_AddRefs(kNC_Description));
00628     mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Content-Length"),
00629                          getter_AddRefs(kNC_ContentLength));
00630     mDirRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastModifiedDate"),
00631                          getter_AddRefs(kNC_LastModified));
00632     mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Content-Type"),
00633                          getter_AddRefs(kNC_ContentType));
00634     mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "File-Type"),
00635                          getter_AddRefs(kNC_FileType));
00636     mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "IsContainer"),
00637                          getter_AddRefs(kNC_IsContainer));
00638 
00639     rv = mDirRDF->GetLiteral(NS_LITERAL_STRING("true").get(), getter_AddRefs(kTrueLiteral));
00640     if (NS_FAILED(rv)) return(rv);
00641     rv = mDirRDF->GetLiteral(NS_LITERAL_STRING("false").get(), getter_AddRefs(kFalseLiteral));
00642     if (NS_FAILED(rv)) return(rv);
00643 
00644     rv = NS_NewISupportsArray(getter_AddRefs(mConnectionList));
00645     if (NS_FAILED(rv)) return(rv);
00646 
00647     // note: don't register DS here
00648     return rv;
00649 }
00650 
00651 
00652 nsresult
00653 nsHTTPIndex::Init()
00654 {
00655        nsresult      rv;
00656 
00657        // set initial/default encoding to ISO-8859-1 (not UTF-8)
00658        mEncoding = "ISO-8859-1";
00659 
00660        rv = CommonInit();
00661        if (NS_FAILED(rv))   return(rv);
00662 
00663        // (do this last) register this as a named data source with the RDF service
00664        rv = mDirRDF->RegisterDataSource(this, PR_FALSE);
00665        if (NS_FAILED(rv)) return(rv);
00666 
00667        return(NS_OK);
00668 }
00669 
00670 
00671 
00672 nsresult
00673 nsHTTPIndex::Init(nsIURI* aBaseURL)
00674 {
00675   NS_PRECONDITION(aBaseURL != nsnull, "null ptr");
00676   if (! aBaseURL)
00677     return NS_ERROR_NULL_POINTER;
00678 
00679   nsresult rv;
00680 
00681   rv = CommonInit();
00682   if (NS_FAILED(rv)) return(rv);
00683 
00684   // note: don't register DS here (singleton case)
00685 
00686   rv = aBaseURL->GetSpec(mBaseURL);
00687   if (NS_FAILED(rv)) return rv;
00688   
00689   // Mark the base url as a container
00690   nsCOMPtr<nsIRDFResource> baseRes;
00691   mDirRDF->GetResource(mBaseURL, getter_AddRefs(baseRes));
00692   Assert(baseRes, kNC_IsContainer, kTrueLiteral, PR_TRUE);
00693 
00694   return NS_OK;
00695 }
00696 
00697 
00698 
00699 nsresult
00700 nsHTTPIndex::Create(nsIURI* aBaseURL, nsIInterfaceRequestor* aRequestor,
00701                     nsIHTTPIndex** aResult)
00702 {
00703   *aResult = nsnull;
00704 
00705   nsHTTPIndex* result = new nsHTTPIndex(aRequestor);
00706   if (! result)
00707     return NS_ERROR_OUT_OF_MEMORY;
00708 
00709   nsresult rv = result->Init(aBaseURL);
00710   if (NS_SUCCEEDED(rv))
00711   {
00712     NS_ADDREF(result);
00713     *aResult = result;
00714   }
00715   else
00716   {
00717     delete result;
00718   }
00719   return rv;
00720 }
00721 
00722 NS_IMETHODIMP
00723 nsHTTPIndex::GetBaseURL(char** _result)
00724 {
00725   *_result = ToNewCString(mBaseURL);
00726   if (! *_result)
00727     return NS_ERROR_OUT_OF_MEMORY;
00728 
00729   return NS_OK;
00730 }
00731 
00732 NS_IMETHODIMP
00733 nsHTTPIndex::GetDataSource(nsIRDFDataSource** _result)
00734 {
00735   NS_ADDREF(*_result = this);
00736   return NS_OK;
00737 }
00738 
00739 // This function finds the destination when following a given nsIRDFResource
00740 // If the resource has a URL attribute, we use that. If not, just use
00741 // the uri.
00742 //
00743 // Do NOT try to get the destination of a uri in any other way
00744 void nsHTTPIndex::GetDestination(nsIRDFResource* r, nsXPIDLCString& dest) {
00745   // First try the URL attribute
00746   nsCOMPtr<nsIRDFNode> node;
00747   
00748   GetTarget(r, kNC_URL, PR_TRUE, getter_AddRefs(node));
00749   nsCOMPtr<nsIRDFLiteral> url;
00750   
00751   if (node)
00752     url = do_QueryInterface(node);
00753 
00754   if (!url) {
00755      const char* temp;
00756      r->GetValueConst(&temp);
00757      dest.Adopt(temp ? nsCRT::strdup(temp) : 0);
00758   } else {
00759     const PRUnichar* uri;
00760     url->GetValueConst(&uri);
00761     dest.Adopt(ToNewUTF8String(nsDependentString(uri)));
00762   }
00763 }
00764 
00765 // rjc: isWellknownContainerURI() decides whether a URI is a container for which,
00766 // when asked (say, by the template builder), we'll make a network connection
00767 // to get its contents. For the moment, all we speak is ftp:// URLs, even though
00768 //    a) we can get "http-index" mimetypes for really anything
00769 //    b) we could easily handle file:// URLs here
00770 //         Q: Why don't we?
00771 //         A: The file system datasource ("rdf:file"); at some point, the two
00772 //            should be perhaps united.  Until then, we can't aggregate both
00773 //            "rdf:file" and "http-index" (such as with bookmarks) because we'd
00774 //            get double the # of answers we really want... also, "rdf:file" is
00775 //            less expensive in terms of both memory usage as well as speed
00776 
00777 // We also handle gopher now
00778 
00779 
00780 // We use an rdf attribute to mark if this is a container or not.
00781 // Note that we still have to do string comparisons as a fallback
00782 // because stuff like the personal toolbar and bookmarks check whether
00783 // a URL is a container, and we have no attribute in that case.
00784 PRBool
00785 nsHTTPIndex::isWellknownContainerURI(nsIRDFResource *r)
00786 {
00787   nsCOMPtr<nsIRDFNode> node;
00788   GetTarget(r, kNC_IsContainer, PR_TRUE, getter_AddRefs(node));
00789 
00790   PRBool isContainerFlag = PR_FALSE;
00791 
00792   if (node && NS_SUCCEEDED(node->EqualsNode(kTrueLiteral, &isContainerFlag))) {
00793     return isContainerFlag;
00794   } else {
00795     nsXPIDLCString uri;
00796     
00797     // For gopher, we need to follow the URL attribute to get the
00798     // real destination
00799     GetDestination(r,uri);
00800 
00801     if ((uri.get()) && (!strncmp(uri, kFTPProtocol, sizeof(kFTPProtocol) - 1))) {
00802       if (uri.Last() == '/') {
00803         isContainerFlag = PR_TRUE;
00804       }
00805     }
00806 
00807     // A gopher url is of the form:
00808     // gopher://example.com/xFileNameToGet
00809     // where x is a single character representing the type of file
00810     // 1 is a directory, and 7 is a search.
00811     // Searches will cause a dialog to be popped up (asking the user what
00812     // to search for), and so even though searches return a directory as a
00813     // result, don't treat it as a directory here.
00814 
00815     // The isContainerFlag test above will correctly handle this when a
00816     // search url is passed in as the baseuri
00817     if ((uri.get()) &&
00818         (!strncmp(uri,kGopherProtocol, sizeof(kGopherProtocol)-1))) {
00819       char* pos = PL_strchr(uri+sizeof(kGopherProtocol)-1, '/');
00820       if (!pos || pos[1] == '\0' || pos[1] == '1')
00821         isContainerFlag = PR_TRUE;
00822     }
00823   }
00824   return isContainerFlag;
00825 }
00826 
00827 
00828 NS_IMETHODIMP
00829 nsHTTPIndex::GetURI(char * *uri)
00830 {
00831        NS_PRECONDITION(uri != nsnull, "null ptr");
00832        if (! uri)
00833               return(NS_ERROR_NULL_POINTER);
00834 
00835        if ((*uri = nsCRT::strdup("rdf:httpindex")) == nsnull)
00836               return(NS_ERROR_OUT_OF_MEMORY);
00837 
00838        return(NS_OK);
00839 }
00840 
00841 
00842 
00843 NS_IMETHODIMP
00844 nsHTTPIndex::GetSource(nsIRDFResource *aProperty, nsIRDFNode *aTarget, PRBool aTruthValue,
00845                      nsIRDFResource **_retval)
00846 {
00847        nsresult      rv = NS_ERROR_UNEXPECTED;
00848 
00849        *_retval = nsnull;
00850 
00851        if (mInner)
00852        {
00853               rv = mInner->GetSource(aProperty, aTarget, aTruthValue, _retval);
00854        }
00855        return(rv);
00856 }
00857 
00858 NS_IMETHODIMP
00859 nsHTTPIndex::GetSources(nsIRDFResource *aProperty, nsIRDFNode *aTarget, PRBool aTruthValue,
00860                      nsISimpleEnumerator **_retval)
00861 {
00862        nsresult      rv = NS_ERROR_UNEXPECTED;
00863 
00864        if (mInner)
00865        {
00866               rv = mInner->GetSources(aProperty, aTarget, aTruthValue, _retval);
00867        }
00868        else
00869        {
00870               rv = NS_NewEmptyEnumerator(_retval);
00871        }
00872        return(rv);
00873 }
00874 
00875 NS_IMETHODIMP
00876 nsHTTPIndex::GetTarget(nsIRDFResource *aSource, nsIRDFResource *aProperty, PRBool aTruthValue,
00877                      nsIRDFNode **_retval)
00878 {
00879        nsresult      rv = NS_ERROR_UNEXPECTED;
00880 
00881        *_retval = nsnull;
00882 
00883         if ((aTruthValue) && (aProperty == kNC_Child) && isWellknownContainerURI(aSource))
00884        {
00885               // fake out the generic builder (i.e. return anything in this case)
00886               // so that search containers never appear to be empty
00887               NS_IF_ADDREF(aSource);
00888               *_retval = aSource;
00889               return(NS_OK);
00890        }
00891 
00892        if (mInner)
00893        {
00894               rv = mInner->GetTarget(aSource, aProperty, aTruthValue, _retval);
00895        }
00896        return(rv);
00897 }
00898 
00899 NS_IMETHODIMP
00900 nsHTTPIndex::GetTargets(nsIRDFResource *aSource, nsIRDFResource *aProperty, PRBool aTruthValue,
00901                      nsISimpleEnumerator **_retval)
00902 {
00903        nsresult      rv = NS_ERROR_UNEXPECTED;
00904 
00905        if (mInner)
00906        {
00907               rv = mInner->GetTargets(aSource, aProperty, aTruthValue, _retval);
00908        }
00909        else
00910        {
00911               rv = NS_NewEmptyEnumerator(_retval);
00912        }
00913 
00914        if ((aProperty == kNC_Child) && isWellknownContainerURI(aSource))
00915        {
00916               PRBool        doNetworkRequest = PR_TRUE;
00917               if (NS_SUCCEEDED(rv) && (_retval))
00918               {
00919                      // check and see if we already have data for the search in question;
00920                      // if we do, don't bother doing the search again
00921                      PRBool        hasResults = PR_FALSE;
00922                      if (NS_SUCCEEDED((*_retval)->HasMoreElements(&hasResults)) &&
00923                          (hasResults == PR_TRUE))
00924                      {
00925                             doNetworkRequest = PR_FALSE;
00926                      }
00927               }
00928 
00929         // Note: if we need to do a network request, do it out-of-band
00930         // (because the XUL template builder isn't re-entrant)
00931         // by using a global connection list and an immediately-firing timer
00932 
00933               if ((doNetworkRequest == PR_TRUE) && (mConnectionList))
00934               {
00935                   PRInt32 connectionIndex = mConnectionList->IndexOf(aSource);
00936                   if (connectionIndex < 0)
00937                   {
00938                   // add aSource into list of connections to make
00939                   mConnectionList->AppendElement(aSource);
00940 
00941                 // if we don't have a timer about to fire, create one
00942                 // which should fire as soon as possible (out-of-band)
00943               if (!mTimer)
00944               {
00945                      mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
00946                      NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a timer");
00947                      if (NS_SUCCEEDED(rv))
00948                      {
00949                             mTimer->InitWithFuncCallback(nsHTTPIndex::FireTimer, this, 1,
00950                                 nsITimer::TYPE_ONE_SHOT);
00951                             // Note: don't addref "this" as we'll cancel the
00952                             // timer in the httpIndex destructor
00953                      }
00954               }
00955               }
00956               }
00957        }
00958 
00959        return(rv);
00960 }
00961 
00962 
00963 nsresult
00964 nsHTTPIndex::AddElement(nsIRDFResource *parent, nsIRDFResource *prop, nsIRDFNode *child)
00965 {
00966     nsresult    rv;
00967 
00968     if (!mNodeList)
00969     {
00970         rv = NS_NewISupportsArray(getter_AddRefs(mNodeList));
00971         if (NS_FAILED(rv)) return(rv);
00972     }
00973 
00974     // order required: parent, prop, then child
00975     mNodeList->AppendElement(parent);
00976     mNodeList->AppendElement(prop);
00977     mNodeList->AppendElement(child);
00978 
00979        if (!mTimer)
00980        {
00981               mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
00982               NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a timer");
00983               if (NS_FAILED(rv))  return(rv);
00984 
00985               mTimer->InitWithFuncCallback(nsHTTPIndex::FireTimer, this, 1,
00986                   nsITimer::TYPE_ONE_SHOT);
00987               // Note: don't addref "this" as we'll cancel the
00988               // timer in the httpIndex destructor
00989        }
00990 
00991     return(NS_OK);
00992 }
00993 
00994 void
00995 nsHTTPIndex::FireTimer(nsITimer* aTimer, void* aClosure)
00996 {
00997   nsHTTPIndex *httpIndex = NS_STATIC_CAST(nsHTTPIndex *, aClosure);
00998   if (!httpIndex)    return;
00999   
01000   // don't return out of this loop as mTimer may need to be cancelled afterwards
01001   PRBool      refireTimer = PR_FALSE;
01002   
01003   PRUint32    numItems = 0;
01004   if (httpIndex->mConnectionList)
01005   {
01006         httpIndex->mConnectionList->Count(&numItems);
01007         if (numItems > 0)
01008         {
01009           nsCOMPtr<nsISupports>   isupports;
01010           httpIndex->mConnectionList->GetElementAt((PRUint32)0, getter_AddRefs(isupports));
01011           httpIndex->mConnectionList->RemoveElementAt((PRUint32)0);
01012           
01013           nsCOMPtr<nsIRDFResource>    aSource;
01014           if (isupports)  aSource = do_QueryInterface(isupports);
01015           
01016           nsXPIDLCString uri;
01017           if (aSource) {
01018             httpIndex->GetDestination(aSource, uri);
01019           }
01020           
01021           if (!uri) {
01022             NS_ERROR("Could not reconstruct uri");
01023             return;
01024           }
01025           
01026           nsresult            rv = NS_OK;
01027           nsCOMPtr<nsIURI>  url;
01028           
01029           rv = NS_NewURI(getter_AddRefs(url), uri.get());
01030           nsCOMPtr<nsIChannel>     channel;
01031           if (NS_SUCCEEDED(rv) && (url)) {
01032             rv = NS_NewChannel(getter_AddRefs(channel), url, nsnull, nsnull);
01033           }
01034           if (NS_SUCCEEDED(rv) && (channel)) {
01035             channel->SetNotificationCallbacks(httpIndex);
01036             rv = channel->AsyncOpen(httpIndex, aSource);
01037           }
01038         }
01039     }
01040     if (httpIndex->mNodeList)
01041     {
01042         httpIndex->mNodeList->Count(&numItems);
01043         if (numItems > 0)
01044         {
01045             // account for order required: src, prop, then target
01046             numItems /=3;
01047             if (numItems > 10)  numItems = 10;
01048           
01049             PRInt32 loop;
01050             for (loop=0; loop<(PRInt32)numItems; loop++)
01051             {
01052                 nsCOMPtr<nsISupports>   isupports;
01053                 httpIndex->mNodeList->GetElementAt((PRUint32)0, getter_AddRefs(isupports));
01054                 httpIndex->mNodeList->RemoveElementAt((PRUint32)0);
01055                 nsCOMPtr<nsIRDFResource>    src;
01056                 if (isupports)  src = do_QueryInterface(isupports);
01057                 httpIndex->mNodeList->GetElementAt((PRUint32)0, getter_AddRefs(isupports));
01058                 httpIndex->mNodeList->RemoveElementAt((PRUint32)0);
01059                 nsCOMPtr<nsIRDFResource>    prop;
01060                 if (isupports)  prop = do_QueryInterface(isupports);
01061                 
01062                 httpIndex->mNodeList->GetElementAt((PRUint32)0, getter_AddRefs(isupports));
01063                 httpIndex->mNodeList->RemoveElementAt((PRUint32)0);
01064                 nsCOMPtr<nsIRDFNode>    target;
01065                 if (isupports)  target = do_QueryInterface(isupports);
01066                 
01067                 if (src && prop && target)
01068                 {
01069                     if (prop.get() == httpIndex->kNC_Loading)
01070                     {
01071                         httpIndex->Unassert(src, prop, target);
01072                     }
01073                     else
01074                     {
01075                         httpIndex->Assert(src, prop, target, PR_TRUE);
01076                     }
01077                 }
01078             }                
01079         }
01080     }
01081     
01082     // check both lists to see if the timer needs to continue firing
01083     if (httpIndex->mConnectionList)
01084     {
01085         httpIndex->mConnectionList->Count(&numItems);
01086         if (numItems > 0)
01087         {
01088             refireTimer = PR_TRUE;
01089         }
01090         else
01091         {
01092             httpIndex->mConnectionList->Clear();
01093         }
01094     }
01095     if (httpIndex->mNodeList)
01096     {
01097         httpIndex->mNodeList->Count(&numItems);
01098         if (numItems > 0)
01099         {
01100             refireTimer = PR_TRUE;
01101         }
01102         else
01103         {
01104             httpIndex->mNodeList->Clear();
01105         }
01106     }
01107 
01108     // be sure to cancel the timer, as it holds a
01109     // weak reference back to nsHTTPIndex
01110     httpIndex->mTimer->Cancel();
01111     httpIndex->mTimer = nsnull;
01112     
01113     // after firing off any/all of the connections be sure
01114     // to cancel the timer if we don't need to refire it
01115     if (refireTimer)
01116     {
01117       httpIndex->mTimer = do_CreateInstance("@mozilla.org/timer;1");
01118       if (httpIndex->mTimer)
01119       {
01120         httpIndex->mTimer->InitWithFuncCallback(nsHTTPIndex::FireTimer, aClosure, 10,
01121                                                 nsITimer::TYPE_ONE_SHOT);
01122         // Note: don't addref "this" as we'll cancel the
01123         // timer in the httpIndex destructor
01124       }
01125     }
01126 }
01127 
01128 NS_IMETHODIMP
01129 nsHTTPIndex::Assert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget,
01130                      PRBool aTruthValue)
01131 {
01132        nsresult      rv = NS_ERROR_UNEXPECTED;
01133        if (mInner)
01134        {
01135               rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
01136        }
01137        return(rv);
01138 }
01139 
01140 NS_IMETHODIMP
01141 nsHTTPIndex::Unassert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget)
01142 {
01143        nsresult      rv = NS_ERROR_UNEXPECTED;
01144        if (mInner)
01145        {
01146               rv = mInner->Unassert(aSource, aProperty, aTarget);
01147        }
01148        return(rv);
01149 }
01150 
01151 NS_IMETHODIMP
01152 nsHTTPIndex::Change(nsIRDFResource *aSource, nsIRDFResource *aProperty,
01153                      nsIRDFNode *aOldTarget, nsIRDFNode *aNewTarget)
01154 {
01155        nsresult      rv = NS_ERROR_UNEXPECTED;
01156        if (mInner)
01157        {
01158               rv = mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
01159        }
01160        return(rv);
01161 }
01162 
01163 NS_IMETHODIMP
01164 nsHTTPIndex::Move(nsIRDFResource *aOldSource, nsIRDFResource *aNewSource,
01165                      nsIRDFResource *aProperty, nsIRDFNode *aTarget)
01166 {
01167        nsresult      rv = NS_ERROR_UNEXPECTED;
01168        if (mInner)
01169        {
01170               rv = mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
01171        }
01172        return(rv);
01173 }
01174 
01175 NS_IMETHODIMP
01176 nsHTTPIndex::HasAssertion(nsIRDFResource *aSource, nsIRDFResource *aProperty,
01177                      nsIRDFNode *aTarget, PRBool aTruthValue, PRBool *_retval)
01178 {
01179        nsresult      rv = NS_ERROR_UNEXPECTED;
01180        if (mInner)
01181        {
01182               rv = mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, _retval);
01183        }
01184        return(rv);
01185 }
01186 
01187 NS_IMETHODIMP
01188 nsHTTPIndex::AddObserver(nsIRDFObserver *aObserver)
01189 {
01190        nsresult      rv = NS_ERROR_UNEXPECTED;
01191        if (mInner)
01192        {
01193               rv = mInner->AddObserver(aObserver);
01194        }
01195        return(rv);
01196 }
01197 
01198 NS_IMETHODIMP
01199 nsHTTPIndex::RemoveObserver(nsIRDFObserver *aObserver)
01200 {
01201        nsresult      rv = NS_ERROR_UNEXPECTED;
01202        if (mInner)
01203        {
01204               rv = mInner->RemoveObserver(aObserver);
01205        }
01206        return(rv);
01207 }
01208 
01209 NS_IMETHODIMP 
01210 nsHTTPIndex::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, PRBool *result)
01211 {
01212   if (!mInner) {
01213     *result = PR_FALSE;
01214     return NS_OK;
01215   }
01216   return mInner->HasArcIn(aNode, aArc, result);
01217 }
01218 
01219 NS_IMETHODIMP 
01220 nsHTTPIndex::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, PRBool *result)
01221 {
01222     if (aArc == kNC_Child && isWellknownContainerURI(aSource)) {
01223       *result = PR_TRUE;
01224       return NS_OK;
01225     }
01226 
01227     if (mInner) {
01228       return mInner->HasArcOut(aSource, aArc, result);
01229     }
01230 
01231     *result = PR_FALSE;
01232     return NS_OK;
01233 }
01234 
01235 NS_IMETHODIMP
01236 nsHTTPIndex::ArcLabelsIn(nsIRDFNode *aNode, nsISimpleEnumerator **_retval)
01237 {
01238        nsresult      rv = NS_ERROR_UNEXPECTED;
01239        if (mInner)
01240        {
01241               rv = mInner->ArcLabelsIn(aNode, _retval);
01242        }
01243        return(rv);
01244 }
01245 
01246 NS_IMETHODIMP
01247 nsHTTPIndex::ArcLabelsOut(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
01248 {
01249        nsresult      rv = NS_ERROR_UNEXPECTED;
01250 
01251        *_retval = nsnull;
01252 
01253        nsCOMPtr<nsISupportsArray> array;
01254        rv = NS_NewISupportsArray(getter_AddRefs(array));
01255        if (NS_FAILED(rv)) return rv;
01256 
01257        if (isWellknownContainerURI(aSource))
01258        {
01259               array->AppendElement(kNC_Child);
01260        }
01261 
01262        if (mInner)
01263        {
01264               nsCOMPtr<nsISimpleEnumerator>      anonArcs;
01265               rv = mInner->ArcLabelsOut(aSource, getter_AddRefs(anonArcs));
01266               PRBool hasResults = PR_TRUE;
01267               while (NS_SUCCEEDED(rv) &&
01268                        NS_SUCCEEDED(anonArcs->HasMoreElements(&hasResults)) &&
01269                        hasResults == PR_TRUE)
01270               {
01271                      nsCOMPtr<nsISupports>       anonArc;
01272                      if (NS_FAILED(anonArcs->GetNext(getter_AddRefs(anonArc))))
01273                             break;
01274                      array->AppendElement(anonArc);
01275               }
01276        }
01277 
01278        nsISimpleEnumerator* result = new nsArrayEnumerator(array);
01279        if (! result)
01280        return NS_ERROR_OUT_OF_MEMORY;
01281 
01282        NS_ADDREF(result);
01283        *_retval = result;
01284        return(NS_OK);
01285 }
01286 
01287 NS_IMETHODIMP
01288 nsHTTPIndex::GetAllResources(nsISimpleEnumerator **_retval)
01289 {
01290        nsresult      rv = NS_ERROR_UNEXPECTED;
01291        if (mInner)
01292        {
01293               rv = mInner->GetAllResources(_retval);
01294        }
01295        return(rv);
01296 }
01297 
01298 NS_IMETHODIMP
01299 nsHTTPIndex::IsCommandEnabled(nsISupportsArray *aSources, nsIRDFResource *aCommand,
01300                             nsISupportsArray *aArguments, PRBool *_retval)
01301 {
01302        nsresult      rv = NS_ERROR_UNEXPECTED;
01303        if (mInner)
01304        {
01305               rv = mInner->IsCommandEnabled(aSources, aCommand, aArguments, _retval);
01306        }
01307        return(rv);
01308 }
01309 
01310 NS_IMETHODIMP
01311 nsHTTPIndex::DoCommand(nsISupportsArray *aSources, nsIRDFResource *aCommand,
01312                             nsISupportsArray *aArguments)
01313 {
01314        nsresult      rv = NS_ERROR_UNEXPECTED;
01315        if (mInner)
01316        {
01317               rv = mInner->DoCommand(aSources, aCommand, aArguments);
01318        }
01319        return(rv);
01320 }
01321 
01322 NS_IMETHODIMP
01323 nsHTTPIndex::BeginUpdateBatch()
01324 {
01325         return mInner->BeginUpdateBatch();
01326 }
01327 
01328 NS_IMETHODIMP
01329 nsHTTPIndex::EndUpdateBatch()
01330 {
01331         return mInner->EndUpdateBatch();
01332 }
01333 
01334 NS_IMETHODIMP
01335 nsHTTPIndex::GetAllCmds(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
01336 {
01337        nsresult      rv = NS_ERROR_UNEXPECTED;
01338        if (mInner)
01339        {
01340               rv = mInner->GetAllCmds(aSource, _retval);
01341        }
01342        return(rv);
01343 }
01344 
01345 
01346 
01347 //----------------------------------------------------------------------
01348 //
01349 // nsDirectoryViewerFactory
01350 //
01351 nsDirectoryViewerFactory::nsDirectoryViewerFactory()
01352 {
01353 }
01354 
01355 
01356 
01357 nsDirectoryViewerFactory::~nsDirectoryViewerFactory()
01358 {
01359 }
01360 
01361 
01362 NS_IMPL_ISUPPORTS1(nsDirectoryViewerFactory, nsIDocumentLoaderFactory)
01363 
01364 
01365 
01366 NS_IMETHODIMP
01367 nsDirectoryViewerFactory::CreateInstance(const char *aCommand,
01368                                          nsIChannel* aChannel,
01369                                          nsILoadGroup* aLoadGroup,
01370                                          const char* aContentType, 
01371                                          nsISupports* aContainer,
01372                                          nsISupports* aExtraInfo,
01373                                          nsIStreamListener** aDocListenerResult,
01374                                          nsIContentViewer** aDocViewerResult)
01375 {
01376   nsresult rv;
01377 
01378   // OK - are we going to be using the html listing or not?
01379   nsCOMPtr<nsIPrefBranch> prefSrv = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
01380   if (NS_FAILED(rv)) return rv;
01381 
01382   PRBool useXUL = PR_FALSE;
01383   PRInt32 dirPref;
01384   rv = prefSrv->GetIntPref("network.dir.format", &dirPref);
01385   if (NS_SUCCEEDED(rv) && dirPref == FORMAT_XUL) {
01386     useXUL = PR_TRUE;
01387   }
01388 
01389   PRBool viewSource = (PL_strstr(aContentType,"view-source") != 0);
01390 
01391   if ((NS_FAILED(rv) || useXUL) && !viewSource) {
01392     // ... and setup the original channel's content type
01393     (void)aChannel->SetContentType(NS_LITERAL_CSTRING("application/vnd.mozilla.xul+xml"));
01394 
01395     // This is where we shunt the HTTP/Index stream into our datasource,
01396     // and open the directory viewer XUL file as the content stream to
01397     // load in its place.
01398     
01399     // Create a dummy loader that will load a stub XUL document.
01400     nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
01401     if (NS_FAILED(rv))
01402       return rv;
01403     nsXPIDLCString contractID;
01404     rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "application/vnd.mozilla.xul+xml",
01405                                   getter_Copies(contractID));
01406     if (NS_FAILED(rv))
01407       return rv;
01408 
01409     nsCOMPtr<nsIDocumentLoaderFactory> factory(do_GetService(contractID, &rv));
01410     if (NS_FAILED(rv)) return rv;
01411     
01412     nsCOMPtr<nsIURI> uri;
01413     rv = NS_NewURI(getter_AddRefs(uri), "chrome://communicator/content/directory/directory.xul");
01414     if (NS_FAILED(rv)) return rv;
01415     
01416     nsCOMPtr<nsIChannel> channel;
01417     rv = NS_NewChannel(getter_AddRefs(channel), uri, nsnull, aLoadGroup);
01418     if (NS_FAILED(rv)) return rv;
01419     
01420     nsCOMPtr<nsIStreamListener> listener;
01421     rv = factory->CreateInstance(aCommand, channel, aLoadGroup, "application/vnd.mozilla.xul+xml",
01422                                  aContainer, aExtraInfo, getter_AddRefs(listener),
01423                                  aDocViewerResult);
01424     if (NS_FAILED(rv)) return rv;
01425 
01426     rv = channel->AsyncOpen(listener, nsnull);
01427     if (NS_FAILED(rv)) return rv;
01428     
01429     // Create an HTTPIndex object so that we can stuff it into the script context
01430     nsCOMPtr<nsIURI> baseuri;
01431     rv = aChannel->GetURI(getter_AddRefs(baseuri));
01432     if (NS_FAILED(rv)) return rv;
01433     
01434     nsCOMPtr<nsIInterfaceRequestor> requestor = do_QueryInterface(aContainer,&rv);
01435     if (NS_FAILED(rv)) return rv;
01436     
01437     nsCOMPtr<nsIHTTPIndex> httpindex;
01438     rv = nsHTTPIndex::Create(baseuri, requestor, getter_AddRefs(httpindex));
01439     if (NS_FAILED(rv)) return rv;
01440     
01441     // Now shanghai the stream into our http-index parsing datasource
01442     // wrapper beastie.
01443     listener = do_QueryInterface(httpindex,&rv);
01444     *aDocListenerResult = listener.get();
01445     NS_ADDREF(*aDocListenerResult);
01446     
01447     return NS_OK;
01448   }
01449 
01450   // setup the original channel's content type
01451   (void)aChannel->SetContentType(NS_LITERAL_CSTRING("text/html"));
01452 
01453   // Otherwise, lets use the html listing
01454   nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
01455   if (NS_FAILED(rv))
01456     return rv;
01457   nsXPIDLCString contractID;
01458   rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "text/html",
01459                                 getter_Copies(contractID));
01460   if (NS_FAILED(rv))
01461     return rv;
01462 
01463   nsCOMPtr<nsIDocumentLoaderFactory> factory(do_GetService(contractID, &rv));
01464   if (NS_FAILED(rv)) return rv;
01465   
01466   nsCOMPtr<nsIStreamListener> listener;
01467 
01468   if (viewSource) {
01469     rv = factory->CreateInstance("view-source", aChannel, aLoadGroup, "text/html; x-view-type=view-source",
01470                                  aContainer, aExtraInfo, getter_AddRefs(listener),
01471                                  aDocViewerResult);
01472   } else {
01473     rv = factory->CreateInstance("view", aChannel, aLoadGroup, "text/html",
01474                                  aContainer, aExtraInfo, getter_AddRefs(listener),
01475                                  aDocViewerResult);
01476   }
01477 
01478   if (NS_FAILED(rv)) return rv;
01479 
01480   nsCOMPtr<nsIStreamConverterService> scs = do_GetService("@mozilla.org/streamConverters;1", &rv);
01481   if (NS_FAILED(rv)) return rv;
01482 
01483   rv = scs->AsyncConvertData("application/http-index-format",
01484                              "text/html",
01485                              listener,
01486                              nsnull,
01487                              aDocListenerResult);
01488 
01489   if (NS_FAILED(rv)) return rv;
01490 
01491   return NS_OK;
01492 }
01493 
01494 
01495 
01496 NS_IMETHODIMP
01497 nsDirectoryViewerFactory::CreateInstanceForDocument(nsISupports* aContainer,
01498                                                     nsIDocument* aDocument,
01499                                                     const char *aCommand,
01500                                                     nsIContentViewer** aDocViewerResult)
01501 {
01502   NS_NOTYETIMPLEMENTED("didn't expect to get here");
01503   return NS_ERROR_NOT_IMPLEMENTED;
01504 }
01505 
01506 NS_IMETHODIMP
01507 nsDirectoryViewerFactory::CreateBlankDocument(nsILoadGroup *aLoadGroup,
01508                                               nsIDocument **_retval) {
01509 
01510   NS_NOTYETIMPLEMENTED("didn't expect to get here");
01511   return NS_ERROR_NOT_IMPLEMENTED;
01512 }
01513