gecko-dev/xpfe/components/directory/nsDirectoryViewer.cpp
Eric Rahm d986dfc66d Bug 1309409 - Part 1: Remove nsISupportsArray usage from nsIRDFDataSource. r=Pike
This converts the usage of nsISupportsArray in nsIRDFDataSource to just
nsISupports. Internally none of the params are used, all external usages in
the addons repo appear to just be passthroughs.

Regardless, any external implementors wanting to pass in an nsISupportsArray
can still do so as it is derived from nsISupports.

Additionally the |IsCommandEnabled| and |DoCommand| stubs are updated to just
return NS_ERROR_NOT_IMPLEMENTED as this functionallity is currently not
supported.

MozReview-Commit-ID: JJSHAQKiLSZ
2016-11-04 11:03:26 -07:00

1394 lines
39 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
A directory viewer object. Parses "application/http-index-format"
per Lou Montulli's original spec:
http://www.mozilla.org/projects/netlib/dirindexformat.html
One added change is for a description entry, for when the
target does not match the filename
*/
#include "nsDirectoryViewer.h"
#include "nsArray.h"
#include "nsArrayUtils.h"
#include "nsIDirIndex.h"
#include "nsIDocShell.h"
#include "jsapi.h"
#include "nsCOMPtr.h"
#include "nsEnumeratorUtils.h"
#include "nsEscape.h"
#include "nsIRDFService.h"
#include "nsRDFCID.h"
#include "rdf.h"
#include "nsIServiceManager.h"
#include "nsIXPConnect.h"
#include "nsEnumeratorUtils.h"
#include "nsString.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "nsITextToSubURI.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIFTPChannel.h"
#include "nsIWindowWatcher.h"
#include "nsIPrompt.h"
#include "nsIAuthPrompt.h"
#include "nsIProgressEventSink.h"
#include "nsIDOMWindow.h"
#include "nsIDOMElement.h"
#include "nsIStreamConverterService.h"
#include "nsICategoryManager.h"
#include "nsXPCOMCID.h"
#include "nsIDocument.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsContentUtils.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
using namespace mozilla;
static const int FORMAT_XUL = 3;
//----------------------------------------------------------------------
//
// Common CIDs
//
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
// Various protocols we have to special case
static const char kFTPProtocol[] = "ftp://";
//----------------------------------------------------------------------
//
// nsHTTPIndex
//
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHTTPIndex)
NS_INTERFACE_MAP_ENTRY(nsIHTTPIndex)
NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIDirIndexListener)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIFTPEventSink)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIHTTPIndex)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION(nsHTTPIndex, mInner)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHTTPIndex)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHTTPIndex)
NS_IMETHODIMP
nsHTTPIndex::GetInterface(const nsIID &anIID, void **aResult )
{
if (anIID.Equals(NS_GET_IID(nsIFTPEventSink))) {
// If we don't have a container to store the logged data
// then don't report ourselves back to the caller
if (!mRequestor)
return NS_ERROR_NO_INTERFACE;
*aResult = static_cast<nsIFTPEventSink*>(this);
NS_ADDREF(this);
return NS_OK;
}
if (anIID.Equals(NS_GET_IID(nsIPrompt))) {
if (!mRequestor)
return NS_ERROR_NO_INTERFACE;
nsCOMPtr<nsPIDOMWindowOuter> aDOMWindow = do_GetInterface(mRequestor);
if (!aDOMWindow)
return NS_ERROR_NO_INTERFACE;
nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
return wwatch->GetNewPrompter(aDOMWindow, (nsIPrompt**)aResult);
}
if (anIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
if (!mRequestor)
return NS_ERROR_NO_INTERFACE;
nsCOMPtr<nsPIDOMWindowOuter> aDOMWindow = do_GetInterface(mRequestor);
if (!aDOMWindow)
return NS_ERROR_NO_INTERFACE;
nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
return wwatch->GetNewAuthPrompter(aDOMWindow, (nsIAuthPrompt**)aResult);
}
if (anIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
if (!mRequestor)
return NS_ERROR_NO_INTERFACE;
nsCOMPtr<nsIProgressEventSink> sink = do_GetInterface(mRequestor);
if (!sink)
return NS_ERROR_NO_INTERFACE;
*aResult = sink;
NS_ADDREF((nsISupports*)*aResult);
return NS_OK;
}
return NS_ERROR_NO_INTERFACE;
}
NS_IMETHODIMP
nsHTTPIndex::OnFTPControlLog(bool server, const char *msg)
{
NS_ENSURE_TRUE(mRequestor, NS_OK);
nsCOMPtr<nsIGlobalObject> globalObject = do_GetInterface(mRequestor);
NS_ENSURE_TRUE(globalObject, NS_OK);
// We're going to run script via JS_CallFunctionName, so we need an
// AutoEntryScript. This is Gecko specific and not in any spec.
dom::AutoEntryScript aes(globalObject,
"nsHTTPIndex OnFTPControlLog");
JSContext* cx = aes.cx();
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
NS_ENSURE_TRUE(global, NS_OK);
nsString unicodeMsg;
unicodeMsg.AssignWithConversion(msg);
JSString* jsMsgStr = JS_NewUCStringCopyZ(cx, unicodeMsg.get());
NS_ENSURE_TRUE(jsMsgStr, NS_ERROR_OUT_OF_MEMORY);
JS::AutoValueArray<2> params(cx);
params[0].setBoolean(server);
params[1].setString(jsMsgStr);
JS::Rooted<JS::Value> val(cx);
JS_CallFunctionName(cx,
global,
"OnFTPControlLog",
params,
&val);
return NS_OK;
}
NS_IMETHODIMP
nsHTTPIndex::SetEncoding(const char *encoding)
{
mEncoding = encoding;
return(NS_OK);
}
NS_IMETHODIMP
nsHTTPIndex::GetEncoding(char **encoding)
{
NS_PRECONDITION(encoding, "null ptr");
if (! encoding)
return(NS_ERROR_NULL_POINTER);
*encoding = ToNewCString(mEncoding);
if (!*encoding)
return(NS_ERROR_OUT_OF_MEMORY);
return(NS_OK);
}
NS_IMETHODIMP
nsHTTPIndex::OnStartRequest(nsIRequest *request, nsISupports* aContext)
{
nsresult rv;
mParser = do_CreateInstance(NS_DIRINDEXPARSER_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = mParser->SetEncoding(mEncoding.get());
if (NS_FAILED(rv)) return rv;
rv = mParser->SetListener(this);
if (NS_FAILED(rv)) return rv;
rv = mParser->OnStartRequest(request,aContext);
if (NS_FAILED(rv)) return rv;
// This should only run once...
// Unless we don't have a container to start with
// (ie called from bookmarks as an rdf datasource)
if (mBindToGlobalObject && mRequestor) {
mBindToGlobalObject = false;
nsCOMPtr<nsIGlobalObject> globalObject = do_GetInterface(mRequestor);
NS_ENSURE_TRUE(globalObject, NS_ERROR_FAILURE);
// We might run script via JS_SetProperty, so we need an AutoEntryScript.
// This is Gecko specific and not in any spec.
dom::AutoEntryScript aes(globalObject,
"nsHTTPIndex set HTTPIndex property");
JSContext* cx = aes.cx();
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
// Using XPConnect, wrap the HTTP index object...
static NS_DEFINE_CID(kXPConnectCID, NS_XPCONNECT_CID);
nsCOMPtr<nsIXPConnect> xpc(do_GetService(kXPConnectCID, &rv));
if (NS_FAILED(rv)) return rv;
JS::Rooted<JSObject*> jsobj(cx);
rv = xpc->WrapNative(cx,
global,
static_cast<nsIHTTPIndex*>(this),
NS_GET_IID(nsIHTTPIndex),
jsobj.address());
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to xpconnect-wrap http-index");
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(jsobj,
"unable to get jsobj from xpconnect wrapper");
if (!jsobj) return NS_ERROR_UNEXPECTED;
JS::Rooted<JS::Value> jslistener(cx, JS::ObjectValue(*jsobj));
// ...and stuff it into the global context
bool ok = JS_SetProperty(cx, global, "HTTPIndex", jslistener);
NS_ASSERTION(ok, "unable to set Listener property");
if (!ok)
return NS_ERROR_FAILURE;
}
if (!aContext) {
nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
NS_ASSERTION(channel, "request should be a channel");
// lets hijack the notifications:
channel->SetNotificationCallbacks(this);
// now create the top most resource
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
nsAutoCString entryuriC;
rv = uri->GetSpec(entryuriC);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFResource> entry;
rv = mDirRDF->GetResource(entryuriC, getter_AddRefs(entry));
NS_ConvertUTF8toUTF16 uriUnicode(entryuriC);
nsCOMPtr<nsIRDFLiteral> URLVal;
rv = mDirRDF->GetLiteral(uriUnicode.get(), getter_AddRefs(URLVal));
Assert(entry, kNC_URL, URLVal, true);
mDirectory = do_QueryInterface(entry);
}
else
{
// Get the directory from the context
mDirectory = do_QueryInterface(aContext);
}
if (!mDirectory) {
request->Cancel(NS_BINDING_ABORTED);
return NS_BINDING_ABORTED;
}
// Mark the directory as "loading"
rv = Assert(mDirectory, kNC_Loading,
kTrueLiteral, true);
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
NS_IMETHODIMP
nsHTTPIndex::OnStopRequest(nsIRequest *request,
nsISupports* aContext,
nsresult aStatus)
{
// If mDirectory isn't set, then we should just bail. Either an
// error occurred and OnStartRequest() never got called, or
// something exploded in OnStartRequest().
if (! mDirectory)
return NS_BINDING_ABORTED;
mParser->OnStopRequest(request,aContext,aStatus);
nsresult rv;
nsXPIDLCString commentStr;
mParser->GetComment(getter_Copies(commentStr));
nsCOMPtr<nsIRDFLiteral> comment;
rv = mDirRDF->GetLiteral(NS_ConvertASCIItoUTF16(commentStr).get(), getter_AddRefs(comment));
if (NS_FAILED(rv)) return rv;
rv = Assert(mDirectory, kNC_Comment, comment, true);
if (NS_FAILED(rv)) return rv;
// hack: Remove the 'loading' annotation (ignore errors)
AddElement(mDirectory, kNC_Loading, kTrueLiteral);
return NS_OK;
}
NS_IMETHODIMP
nsHTTPIndex::OnDataAvailable(nsIRequest *request,
nsISupports* aContext,
nsIInputStream* aStream,
uint64_t aSourceOffset,
uint32_t aCount)
{
// If mDirectory isn't set, then we should just bail. Either an
// error occurred and OnStartRequest() never got called, or
// something exploded in OnStartRequest().
if (! mDirectory)
return NS_BINDING_ABORTED;
return mParser->OnDataAvailable(request, mDirectory, aStream, aSourceOffset, aCount);
}
nsresult
nsHTTPIndex::OnIndexAvailable(nsIRequest* aRequest, nsISupports *aContext,
nsIDirIndex* aIndex)
{
nsCOMPtr<nsIRDFResource> parentRes = do_QueryInterface(aContext);
if (!parentRes) {
NS_ERROR("Could not obtain parent resource");
return(NS_ERROR_UNEXPECTED);
}
const char* baseStr;
parentRes->GetValueConst(&baseStr);
if (! baseStr) {
NS_ERROR("Could not reconstruct base uri");
return NS_ERROR_UNEXPECTED;
}
// we found the filename; construct a resource for its entry
nsAutoCString entryuriC(baseStr);
nsXPIDLCString filename;
nsresult rv = aIndex->GetLocation(getter_Copies(filename));
if (NS_FAILED(rv)) return rv;
entryuriC.Append(filename);
// if its a directory, make sure it ends with a trailing slash.
uint32_t type;
rv = aIndex->GetType(&type);
if (NS_FAILED(rv))
return rv;
bool isDirType = (type == nsIDirIndex::TYPE_DIRECTORY);
if (isDirType && entryuriC.Last() != '/') {
entryuriC.Append('/');
}
nsCOMPtr<nsIRDFResource> entry;
rv = mDirRDF->GetResource(entryuriC, getter_AddRefs(entry));
// At this point, we'll (hopefully) have found the filename and
// constructed a resource for it, stored in entry. So now take a
// second pass through the values and add as statements to the RDF
// datasource.
if (entry && NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIRDFLiteral> lit;
nsString str;
str.AssignWithConversion(entryuriC.get());
rv = mDirRDF->GetLiteral(str.get(), getter_AddRefs(lit));
if (NS_SUCCEEDED(rv)) {
rv = Assert(entry, kNC_URL, lit, true);
if (NS_FAILED(rv)) return rv;
nsXPIDLString xpstr;
// description
rv = aIndex->GetDescription(getter_Copies(xpstr));
if (NS_FAILED(rv)) return rv;
if (xpstr.Last() == '/')
xpstr.Truncate(xpstr.Length() - 1);
rv = mDirRDF->GetLiteral(xpstr.get(), getter_AddRefs(lit));
if (NS_FAILED(rv)) return rv;
rv = Assert(entry, kNC_Description, lit, true);
if (NS_FAILED(rv)) return rv;
// contentlength
int64_t size;
rv = aIndex->GetSize(&size);
if (NS_FAILED(rv)) return rv;
int64_t minus1 = UINT64_MAX;
if (size != minus1) {
int32_t intSize = int32_t(size);
// XXX RDF should support 64 bit integers (bug 240160)
nsCOMPtr<nsIRDFInt> val;
rv = mDirRDF->GetIntLiteral(intSize, getter_AddRefs(val));
if (NS_FAILED(rv)) return rv;
rv = Assert(entry, kNC_ContentLength, val, true);
if (NS_FAILED(rv)) return rv;
}
// lastmodified
PRTime tm;
rv = aIndex->GetLastModified(&tm);
if (NS_FAILED(rv)) return rv;
if (tm != -1) {
nsCOMPtr<nsIRDFDate> val;
rv = mDirRDF->GetDateLiteral(tm, getter_AddRefs(val));
if (NS_FAILED(rv)) return rv;
rv = Assert(entry, kNC_LastModified, val, true);
}
// filetype
uint32_t type;
rv = aIndex->GetType(&type);
switch (type) {
case nsIDirIndex::TYPE_UNKNOWN:
rv = mDirRDF->GetLiteral(u"UNKNOWN", getter_AddRefs(lit));
break;
case nsIDirIndex::TYPE_DIRECTORY:
rv = mDirRDF->GetLiteral(u"DIRECTORY", getter_AddRefs(lit));
break;
case nsIDirIndex::TYPE_FILE:
rv = mDirRDF->GetLiteral(u"FILE", getter_AddRefs(lit));
break;
case nsIDirIndex::TYPE_SYMLINK:
rv = mDirRDF->GetLiteral(u"SYMLINK", getter_AddRefs(lit));
break;
}
if (NS_FAILED(rv)) return rv;
rv = Assert(entry, kNC_FileType, lit, true);
if (NS_FAILED(rv)) return rv;
}
// Since the definition of a directory depends on the protocol, we would have
// to do string comparisons all the time.
// But we're told if we're a container right here - so save that fact
if (isDirType)
Assert(entry, kNC_IsContainer, kTrueLiteral, true);
else
Assert(entry, kNC_IsContainer, kFalseLiteral, true);
// instead of
// rv = Assert(parentRes, kNC_Child, entry, true);
// if (NS_FAILED(rv)) return rv;
// defer insertion onto a timer so that the UI isn't starved
AddElement(parentRes, kNC_Child, entry);
}
return rv;
}
nsresult
nsHTTPIndex::OnInformationAvailable(nsIRequest *aRequest,
nsISupports *aCtxt,
const nsAString& aInfo) {
return NS_ERROR_NOT_IMPLEMENTED;
}
//----------------------------------------------------------------------
//
// nsHTTPIndex implementation
//
nsHTTPIndex::nsHTTPIndex()
: mBindToGlobalObject(true),
mRequestor(nullptr)
{
}
nsHTTPIndex::nsHTTPIndex(nsIInterfaceRequestor* aRequestor)
: mBindToGlobalObject(true),
mRequestor(aRequestor)
{
}
nsHTTPIndex::~nsHTTPIndex()
{
// note: these are NOT statics due to the native of nsHTTPIndex
// where it may or may not be treated as a singleton
if (mTimer)
{
// be sure to cancel the timer, as it holds a
// weak reference back to nsHTTPIndex
mTimer->Cancel();
mTimer = nullptr;
}
mConnectionList = nullptr;
mNodeList = nullptr;
if (mDirRDF)
{
// UnregisterDataSource() may fail; just ignore errors
mDirRDF->UnregisterDataSource(this);
}
}
nsresult
nsHTTPIndex::CommonInit()
{
nsresult rv = NS_OK;
// set initial/default encoding to ISO-8859-1 (not UTF-8)
mEncoding = "ISO-8859-1";
mDirRDF = do_GetService(kRDFServiceCID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
if (NS_FAILED(rv)) {
return(rv);
}
mInner = do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource", &rv);
if (NS_FAILED(rv))
return rv;
mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "child"),
getter_AddRefs(kNC_Child));
mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "loading"),
getter_AddRefs(kNC_Loading));
mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Comment"),
getter_AddRefs(kNC_Comment));
mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "URL"),
getter_AddRefs(kNC_URL));
mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Name"),
getter_AddRefs(kNC_Description));
mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Content-Length"),
getter_AddRefs(kNC_ContentLength));
mDirRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastModifiedDate"),
getter_AddRefs(kNC_LastModified));
mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Content-Type"),
getter_AddRefs(kNC_ContentType));
mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "File-Type"),
getter_AddRefs(kNC_FileType));
mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "IsContainer"),
getter_AddRefs(kNC_IsContainer));
rv = mDirRDF->GetLiteral(u"true", getter_AddRefs(kTrueLiteral));
if (NS_FAILED(rv)) return(rv);
rv = mDirRDF->GetLiteral(u"false", getter_AddRefs(kFalseLiteral));
if (NS_FAILED(rv)) return(rv);
mConnectionList = nsArray::Create();
// note: don't register DS here
return rv;
}
nsresult
nsHTTPIndex::Init()
{
nsresult rv;
// set initial/default encoding to ISO-8859-1 (not UTF-8)
mEncoding = "ISO-8859-1";
rv = CommonInit();
if (NS_FAILED(rv)) return(rv);
// (do this last) register this as a named data source with the RDF service
rv = mDirRDF->RegisterDataSource(this, false);
if (NS_FAILED(rv)) return(rv);
return(NS_OK);
}
nsresult
nsHTTPIndex::Init(nsIURI* aBaseURL)
{
NS_PRECONDITION(aBaseURL != nullptr, "null ptr");
if (! aBaseURL)
return NS_ERROR_NULL_POINTER;
nsresult rv;
rv = CommonInit();
if (NS_FAILED(rv)) return(rv);
// note: don't register DS here (singleton case)
rv = aBaseURL->GetSpec(mBaseURL);
if (NS_FAILED(rv)) return rv;
// Mark the base url as a container
nsCOMPtr<nsIRDFResource> baseRes;
mDirRDF->GetResource(mBaseURL, getter_AddRefs(baseRes));
Assert(baseRes, kNC_IsContainer, kTrueLiteral, true);
return NS_OK;
}
nsresult
nsHTTPIndex::Create(nsIURI* aBaseURL, nsIInterfaceRequestor* aRequestor,
nsIHTTPIndex** aResult)
{
*aResult = nullptr;
nsHTTPIndex* result = new nsHTTPIndex(aRequestor);
nsresult rv = result->Init(aBaseURL);
if (NS_SUCCEEDED(rv))
{
NS_ADDREF(result);
*aResult = result;
}
else
{
delete result;
}
return rv;
}
NS_IMETHODIMP
nsHTTPIndex::GetBaseURL(char** _result)
{
*_result = ToNewCString(mBaseURL);
if (! *_result)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
NS_IMETHODIMP
nsHTTPIndex::GetDataSource(nsIRDFDataSource** _result)
{
NS_ADDREF(*_result = this);
return NS_OK;
}
// This function finds the destination when following a given nsIRDFResource
// If the resource has a URL attribute, we use that. If not, just use
// the uri.
//
// Do NOT try to get the destination of a uri in any other way
void nsHTTPIndex::GetDestination(nsIRDFResource* r, nsXPIDLCString& dest) {
// First try the URL attribute
nsCOMPtr<nsIRDFNode> node;
GetTarget(r, kNC_URL, true, getter_AddRefs(node));
nsCOMPtr<nsIRDFLiteral> url;
if (node)
url = do_QueryInterface(node);
if (!url) {
const char* temp;
r->GetValueConst(&temp);
dest.Adopt(temp ? strdup(temp) : 0);
} else {
const char16_t* uri;
url->GetValueConst(&uri);
dest.Adopt(ToNewUTF8String(nsDependentString(uri)));
}
}
// rjc: isWellknownContainerURI() decides whether a URI is a container for which,
// when asked (say, by the template builder), we'll make a network connection
// to get its contents. For the moment, all we speak is ftp:// URLs, even though
// a) we can get "http-index" mimetypes for really anything
// b) we could easily handle file:// URLs here
// Q: Why don't we?
// A: The file system datasource ("rdf:file"); at some point, the two
// should be perhaps united. Until then, we can't aggregate both
// "rdf:file" and "http-index" (such as with bookmarks) because we'd
// get double the # of answers we really want... also, "rdf:file" is
// less expensive in terms of both memory usage as well as speed
// We use an rdf attribute to mark if this is a container or not.
// Note that we still have to do string comparisons as a fallback
// because stuff like the personal toolbar and bookmarks check whether
// a URL is a container, and we have no attribute in that case.
bool
nsHTTPIndex::isWellknownContainerURI(nsIRDFResource *r)
{
nsCOMPtr<nsIRDFNode> node;
GetTarget(r, kNC_IsContainer, true, getter_AddRefs(node));
if (node) {
bool isContainerFlag;
if (NS_SUCCEEDED(node->EqualsNode(kTrueLiteral, &isContainerFlag)))
return isContainerFlag;
}
nsXPIDLCString uri;
GetDestination(r, uri);
return uri.get() && !strncmp(uri, kFTPProtocol, sizeof(kFTPProtocol) - 1) &&
(uri.Last() == '/');
}
NS_IMETHODIMP
nsHTTPIndex::GetURI(char * *uri)
{
NS_PRECONDITION(uri != nullptr, "null ptr");
if (! uri)
return(NS_ERROR_NULL_POINTER);
if ((*uri = strdup("rdf:httpindex")) == nullptr)
return(NS_ERROR_OUT_OF_MEMORY);
return(NS_OK);
}
NS_IMETHODIMP
nsHTTPIndex::GetSource(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue,
nsIRDFResource **_retval)
{
nsresult rv = NS_ERROR_UNEXPECTED;
*_retval = nullptr;
if (mInner)
{
rv = mInner->GetSource(aProperty, aTarget, aTruthValue, _retval);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::GetSources(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue,
nsISimpleEnumerator **_retval)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->GetSources(aProperty, aTarget, aTruthValue, _retval);
}
else
{
rv = NS_NewEmptyEnumerator(_retval);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::GetTarget(nsIRDFResource *aSource, nsIRDFResource *aProperty, bool aTruthValue,
nsIRDFNode **_retval)
{
nsresult rv = NS_ERROR_UNEXPECTED;
*_retval = nullptr;
if ((aTruthValue) && (aProperty == kNC_Child) && isWellknownContainerURI(aSource))
{
// fake out the generic builder (i.e. return anything in this case)
// so that search containers never appear to be empty
NS_IF_ADDREF(aSource);
*_retval = aSource;
return(NS_OK);
}
if (mInner)
{
rv = mInner->GetTarget(aSource, aProperty, aTruthValue, _retval);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::GetTargets(nsIRDFResource *aSource, nsIRDFResource *aProperty, bool aTruthValue,
nsISimpleEnumerator **_retval)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->GetTargets(aSource, aProperty, aTruthValue, _retval);
}
else
{
rv = NS_NewEmptyEnumerator(_retval);
}
if ((aProperty == kNC_Child) && isWellknownContainerURI(aSource))
{
bool doNetworkRequest = true;
if (NS_SUCCEEDED(rv) && (_retval))
{
// check and see if we already have data for the search in question;
// if we do, don't bother doing the search again
bool hasResults;
if (NS_SUCCEEDED((*_retval)->HasMoreElements(&hasResults)) &&
hasResults)
doNetworkRequest = false;
}
// Note: if we need to do a network request, do it out-of-band
// (because the XUL template builder isn't re-entrant)
// by using a global connection list and an immediately-firing timer
if (doNetworkRequest && mConnectionList)
{
uint32_t connectionIndex;
nsresult idx_rv = mConnectionList->IndexOf(0, aSource, &connectionIndex);
if (NS_FAILED(idx_rv))
{
// add aSource into list of connections to make
mConnectionList->AppendElement(aSource, /*weak =*/ false);
// if we don't have a timer about to fire, create one
// which should fire as soon as possible (out-of-band)
if (!mTimer)
{
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a timer");
if (NS_SUCCEEDED(rv))
{
mTimer->InitWithFuncCallback(nsHTTPIndex::FireTimer, this, 1,
nsITimer::TYPE_ONE_SHOT);
// Note: don't addref "this" as we'll cancel the
// timer in the httpIndex destructor
}
}
}
}
}
return(rv);
}
nsresult
nsHTTPIndex::AddElement(nsIRDFResource *parent, nsIRDFResource *prop, nsIRDFNode *child)
{
nsresult rv;
if (!mNodeList)
{
mNodeList = nsArray::Create();
}
// order required: parent, prop, then child
mNodeList->AppendElement(parent, /*weak =*/ false);
mNodeList->AppendElement(prop, /*weak =*/ false);
mNodeList->AppendElement(child, /*weak = */ false);
if (!mTimer)
{
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a timer");
if (NS_FAILED(rv)) return(rv);
mTimer->InitWithFuncCallback(nsHTTPIndex::FireTimer, this, 1,
nsITimer::TYPE_ONE_SHOT);
// Note: don't addref "this" as we'll cancel the
// timer in the httpIndex destructor
}
return(NS_OK);
}
void
nsHTTPIndex::FireTimer(nsITimer* aTimer, void* aClosure)
{
nsHTTPIndex *httpIndex = static_cast<nsHTTPIndex *>(aClosure);
if (!httpIndex)
return;
// don't return out of this loop as mTimer may need to be cancelled afterwards
uint32_t numItems = 0;
if (httpIndex->mConnectionList)
{
httpIndex->mConnectionList->GetLength(&numItems);
if (numItems > 0)
{
nsCOMPtr<nsIRDFResource> source =
do_QueryElementAt(httpIndex->mConnectionList, 0);
httpIndex->mConnectionList->RemoveElementAt(0);
nsXPIDLCString uri;
if (source) {
httpIndex->GetDestination(source, uri);
}
if (!uri) {
NS_ERROR("Could not reconstruct uri");
return;
}
nsresult rv = NS_OK;
nsCOMPtr<nsIURI> url;
rv = NS_NewURI(getter_AddRefs(url), uri.get());
nsCOMPtr<nsIChannel> channel;
if (NS_SUCCEEDED(rv) && (url)) {
rv = NS_NewChannel(getter_AddRefs(channel),
url,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER);
}
if (NS_SUCCEEDED(rv) && (channel)) {
channel->SetNotificationCallbacks(httpIndex);
rv = channel->AsyncOpen2(httpIndex);
}
}
}
if (httpIndex->mNodeList)
{
httpIndex->mNodeList->GetLength(&numItems);
if (numItems > 0)
{
// account for order required: src, prop, then target
numItems /=3;
if (numItems > 10)
numItems = 10;
int32_t loop;
for (loop=0; loop<(int32_t)numItems; loop++)
{
nsCOMPtr<nsIRDFResource> src = do_QueryElementAt(httpIndex->mNodeList, 0);
httpIndex->mNodeList->RemoveElementAt(0);
nsCOMPtr<nsIRDFResource> prop = do_QueryElementAt(httpIndex->mNodeList, 0);
httpIndex->mNodeList->RemoveElementAt(0);
nsCOMPtr<nsIRDFNode> target = do_QueryElementAt(httpIndex->mNodeList, 0);
httpIndex->mNodeList->RemoveElementAt(0);
if (src && prop && target)
{
if (prop.get() == httpIndex->kNC_Loading)
{
httpIndex->Unassert(src, prop, target);
}
else
{
httpIndex->Assert(src, prop, target, true);
}
}
}
}
}
bool refireTimer = false;
// check both lists to see if the timer needs to continue firing
if (httpIndex->mConnectionList)
{
httpIndex->mConnectionList->GetLength(&numItems);
if (numItems > 0)
{
refireTimer = true;
}
else
{
httpIndex->mConnectionList->Clear();
}
}
if (httpIndex->mNodeList)
{
httpIndex->mNodeList->GetLength(&numItems);
if (numItems > 0)
{
refireTimer = true;
}
else
{
httpIndex->mNodeList->Clear();
}
}
// be sure to cancel the timer, as it holds a
// weak reference back to nsHTTPIndex
httpIndex->mTimer->Cancel();
httpIndex->mTimer = nullptr;
// after firing off any/all of the connections be sure
// to cancel the timer if we don't need to refire it
if (refireTimer)
{
httpIndex->mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (httpIndex->mTimer)
{
httpIndex->mTimer->InitWithFuncCallback(nsHTTPIndex::FireTimer, aClosure, 10,
nsITimer::TYPE_ONE_SHOT);
// Note: don't addref "this" as we'll cancel the
// timer in the httpIndex destructor
}
}
}
NS_IMETHODIMP
nsHTTPIndex::Assert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget,
bool aTruthValue)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::Unassert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->Unassert(aSource, aProperty, aTarget);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::Change(nsIRDFResource *aSource, nsIRDFResource *aProperty,
nsIRDFNode *aOldTarget, nsIRDFNode *aNewTarget)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::Move(nsIRDFResource *aOldSource, nsIRDFResource *aNewSource,
nsIRDFResource *aProperty, nsIRDFNode *aTarget)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::HasAssertion(nsIRDFResource *aSource, nsIRDFResource *aProperty,
nsIRDFNode *aTarget, bool aTruthValue, bool *_retval)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, _retval);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::AddObserver(nsIRDFObserver *aObserver)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->AddObserver(aObserver);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::RemoveObserver(nsIRDFObserver *aObserver)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->RemoveObserver(aObserver);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
{
if (!mInner) {
*result = false;
return NS_OK;
}
return mInner->HasArcIn(aNode, aArc, result);
}
NS_IMETHODIMP
nsHTTPIndex::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
{
if (aArc == kNC_Child && isWellknownContainerURI(aSource)) {
*result = true;
return NS_OK;
}
if (mInner) {
return mInner->HasArcOut(aSource, aArc, result);
}
*result = false;
return NS_OK;
}
NS_IMETHODIMP
nsHTTPIndex::ArcLabelsIn(nsIRDFNode *aNode, nsISimpleEnumerator **_retval)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->ArcLabelsIn(aNode, _retval);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::ArcLabelsOut(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
{
*_retval = nullptr;
nsCOMPtr<nsISimpleEnumerator> child, anonArcs;
if (isWellknownContainerURI(aSource))
{
NS_NewSingletonEnumerator(getter_AddRefs(child), kNC_Child);
}
if (mInner)
{
mInner->ArcLabelsOut(aSource, getter_AddRefs(anonArcs));
}
return NS_NewUnionEnumerator(_retval, child, anonArcs);
}
NS_IMETHODIMP
nsHTTPIndex::GetAllResources(nsISimpleEnumerator **_retval)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->GetAllResources(_retval);
}
return(rv);
}
NS_IMETHODIMP
nsHTTPIndex::IsCommandEnabled(nsISupports *aSources, nsIRDFResource *aCommand,
nsISupports *aArguments, bool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsHTTPIndex::DoCommand(nsISupports *aSources, nsIRDFResource *aCommand,
nsISupports *aArguments)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsHTTPIndex::BeginUpdateBatch()
{
return mInner->BeginUpdateBatch();
}
NS_IMETHODIMP
nsHTTPIndex::EndUpdateBatch()
{
return mInner->EndUpdateBatch();
}
NS_IMETHODIMP
nsHTTPIndex::GetAllCmds(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (mInner)
{
rv = mInner->GetAllCmds(aSource, _retval);
}
return(rv);
}
//----------------------------------------------------------------------
//
// nsDirectoryViewerFactory
//
nsDirectoryViewerFactory::nsDirectoryViewerFactory()
{
}
nsDirectoryViewerFactory::~nsDirectoryViewerFactory()
{
}
NS_IMPL_ISUPPORTS(nsDirectoryViewerFactory, nsIDocumentLoaderFactory)
NS_IMETHODIMP
nsDirectoryViewerFactory::CreateInstance(const char *aCommand,
nsIChannel* aChannel,
nsILoadGroup* aLoadGroup,
const nsACString& aContentType,
nsIDocShell* aContainer,
nsISupports* aExtraInfo,
nsIStreamListener** aDocListenerResult,
nsIContentViewer** aDocViewerResult)
{
nsresult rv;
bool viewSource = FindInReadable(NS_LITERAL_CSTRING("view-source"),
aContentType);
if (!viewSource &&
Preferences::GetInt("network.dir.format", FORMAT_XUL) == FORMAT_XUL) {
// ... and setup the original channel's content type
(void)aChannel->SetContentType(NS_LITERAL_CSTRING("application/vnd.mozilla.xul+xml"));
// This is where we shunt the HTTP/Index stream into our datasource,
// and open the directory viewer XUL file as the content stream to
// load in its place.
// Create a dummy loader that will load a stub XUL document.
nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
if (NS_FAILED(rv))
return rv;
nsXPIDLCString contractID;
rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "application/vnd.mozilla.xul+xml",
getter_Copies(contractID));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIDocumentLoaderFactory> factory(do_GetService(contractID, &rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), "chrome://communicator/content/directory/directory.xul");
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER,
aLoadGroup);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIStreamListener> listener;
rv = factory->CreateInstance(aCommand, channel, aLoadGroup,
NS_LITERAL_CSTRING("application/vnd.mozilla.xul+xml"),
aContainer, aExtraInfo, getter_AddRefs(listener),
aDocViewerResult);
if (NS_FAILED(rv)) return rv;
rv = channel->AsyncOpen2(listener);
if (NS_FAILED(rv)) return rv;
// Create an HTTPIndex object so that we can stuff it into the script context
nsCOMPtr<nsIURI> baseuri;
rv = aChannel->GetURI(getter_AddRefs(baseuri));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIInterfaceRequestor> requestor = do_QueryInterface(aContainer,&rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIHTTPIndex> httpindex;
rv = nsHTTPIndex::Create(baseuri, requestor, getter_AddRefs(httpindex));
if (NS_FAILED(rv)) return rv;
// Now shanghai the stream into our http-index parsing datasource
// wrapper beastie.
listener = do_QueryInterface(httpindex,&rv);
*aDocListenerResult = listener.get();
NS_ADDREF(*aDocListenerResult);
return NS_OK;
}
// setup the original channel's content type
(void)aChannel->SetContentType(NS_LITERAL_CSTRING("text/html"));
// Otherwise, lets use the html listing
nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
if (NS_FAILED(rv))
return rv;
nsXPIDLCString contractID;
rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "text/html",
getter_Copies(contractID));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIDocumentLoaderFactory> factory(do_GetService(contractID, &rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIStreamListener> listener;
if (viewSource) {
rv = factory->CreateInstance("view-source", aChannel, aLoadGroup,
NS_LITERAL_CSTRING("text/html; x-view-type=view-source"),
aContainer, aExtraInfo, getter_AddRefs(listener),
aDocViewerResult);
} else {
rv = factory->CreateInstance("view", aChannel, aLoadGroup,
NS_LITERAL_CSTRING("text/html"),
aContainer, aExtraInfo, getter_AddRefs(listener),
aDocViewerResult);
}
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIStreamConverterService> scs = do_GetService("@mozilla.org/streamConverters;1", &rv);
if (NS_FAILED(rv)) return rv;
rv = scs->AsyncConvertData("application/http-index-format",
"text/html",
listener,
nullptr,
aDocListenerResult);
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
NS_IMETHODIMP
nsDirectoryViewerFactory::CreateInstanceForDocument(nsISupports* aContainer,
nsIDocument* aDocument,
const char *aCommand,
nsIContentViewer** aDocViewerResult)
{
NS_NOTYETIMPLEMENTED("didn't expect to get here");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDirectoryViewerFactory::CreateBlankDocument(nsILoadGroup *aLoadGroup,
nsIPrincipal *aPrincipal,
nsIDocument **_retval) {
NS_NOTYETIMPLEMENTED("didn't expect to get here");
return NS_ERROR_NOT_IMPLEMENTED;
}