extensions/gnomevfs/nsGnomeVFSProtocolHandler.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* vim:set ts=2 sw=2 et cindent: */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 // GnomeVFS v2.2.2 is missing G_BEGIN_DECLS in gnome-vfs-module-callback.h
michael@0 7 extern "C" {
michael@0 8 #include <libgnomevfs/gnome-vfs.h>
michael@0 9 #include <libgnomevfs/gnome-vfs-standard-callbacks.h>
michael@0 10 #include <libgnomevfs/gnome-vfs-mime-utils.h>
michael@0 11 }
michael@0 12
michael@0 13 #include <algorithm>
michael@0 14
michael@0 15 #include "nsServiceManagerUtils.h"
michael@0 16 #include "nsComponentManagerUtils.h"
michael@0 17 #include "mozilla/ModuleUtils.h"
michael@0 18 #include "nsIInterfaceRequestorUtils.h"
michael@0 19 #include "nsIPrefService.h"
michael@0 20 #include "nsIPrefBranch.h"
michael@0 21 #include "nsIObserver.h"
michael@0 22 #include "nsThreadUtils.h"
michael@0 23 #include "nsProxyRelease.h"
michael@0 24 #include "nsIAuthPrompt.h"
michael@0 25 #include "nsIStringBundle.h"
michael@0 26 #include "nsIStandardURL.h"
michael@0 27 #include "nsIURL.h"
michael@0 28 #include "nsMimeTypes.h"
michael@0 29 #include "nsNetUtil.h"
michael@0 30 #include "nsINetUtil.h"
michael@0 31 #include "nsAutoPtr.h"
michael@0 32 #include "nsError.h"
michael@0 33 #include "prlog.h"
michael@0 34 #include "prtime.h"
michael@0 35 #include "prprf.h"
michael@0 36 #include "plstr.h"
michael@0 37 #include "mozilla/Attributes.h"
michael@0 38
michael@0 39 #define MOZ_GNOMEVFS_SCHEME "moz-gnomevfs"
michael@0 40 #define MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS "network.gnomevfs.supported-protocols"
michael@0 41
michael@0 42 //-----------------------------------------------------------------------------
michael@0 43
michael@0 44 // NSPR_LOG_MODULES=gnomevfs:5
michael@0 45 #ifdef PR_LOGGING
michael@0 46 static PRLogModuleInfo *sGnomeVFSLog;
michael@0 47 #define LOG(args) PR_LOG(sGnomeVFSLog, PR_LOG_DEBUG, args)
michael@0 48 #else
michael@0 49 #define LOG(args)
michael@0 50 #endif
michael@0 51
michael@0 52 //-----------------------------------------------------------------------------
michael@0 53
michael@0 54 static nsresult
michael@0 55 MapGnomeVFSResult(GnomeVFSResult result)
michael@0 56 {
michael@0 57 switch (result)
michael@0 58 {
michael@0 59 case GNOME_VFS_OK: return NS_OK;
michael@0 60 case GNOME_VFS_ERROR_NOT_FOUND: return NS_ERROR_FILE_NOT_FOUND;
michael@0 61 case GNOME_VFS_ERROR_INTERNAL: return NS_ERROR_UNEXPECTED;
michael@0 62 case GNOME_VFS_ERROR_BAD_PARAMETERS: return NS_ERROR_INVALID_ARG;
michael@0 63 case GNOME_VFS_ERROR_NOT_SUPPORTED: return NS_ERROR_NOT_AVAILABLE;
michael@0 64 case GNOME_VFS_ERROR_CORRUPTED_DATA: return NS_ERROR_FILE_CORRUPTED;
michael@0 65 case GNOME_VFS_ERROR_TOO_BIG: return NS_ERROR_FILE_TOO_BIG;
michael@0 66 case GNOME_VFS_ERROR_NO_SPACE: return NS_ERROR_FILE_NO_DEVICE_SPACE;
michael@0 67 case GNOME_VFS_ERROR_READ_ONLY:
michael@0 68 case GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM: return NS_ERROR_FILE_READ_ONLY;
michael@0 69 case GNOME_VFS_ERROR_INVALID_URI:
michael@0 70 case GNOME_VFS_ERROR_INVALID_HOST_NAME: return NS_ERROR_MALFORMED_URI;
michael@0 71 case GNOME_VFS_ERROR_ACCESS_DENIED:
michael@0 72 case GNOME_VFS_ERROR_NOT_PERMITTED:
michael@0 73 case GNOME_VFS_ERROR_LOGIN_FAILED: return NS_ERROR_FILE_ACCESS_DENIED;
michael@0 74 case GNOME_VFS_ERROR_EOF: return NS_BASE_STREAM_CLOSED;
michael@0 75 case GNOME_VFS_ERROR_NOT_A_DIRECTORY: return NS_ERROR_FILE_NOT_DIRECTORY;
michael@0 76 case GNOME_VFS_ERROR_IN_PROGRESS: return NS_ERROR_IN_PROGRESS;
michael@0 77 case GNOME_VFS_ERROR_FILE_EXISTS: return NS_ERROR_FILE_ALREADY_EXISTS;
michael@0 78 case GNOME_VFS_ERROR_IS_DIRECTORY: return NS_ERROR_FILE_IS_DIRECTORY;
michael@0 79 case GNOME_VFS_ERROR_NO_MEMORY: return NS_ERROR_OUT_OF_MEMORY;
michael@0 80 case GNOME_VFS_ERROR_HOST_NOT_FOUND:
michael@0 81 case GNOME_VFS_ERROR_HOST_HAS_NO_ADDRESS: return NS_ERROR_UNKNOWN_HOST;
michael@0 82 case GNOME_VFS_ERROR_CANCELLED:
michael@0 83 case GNOME_VFS_ERROR_INTERRUPTED: return NS_ERROR_ABORT;
michael@0 84 case GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY: return NS_ERROR_FILE_DIR_NOT_EMPTY;
michael@0 85 case GNOME_VFS_ERROR_NAME_TOO_LONG: return NS_ERROR_FILE_NAME_TOO_LONG;
michael@0 86 case GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE: return NS_ERROR_UNKNOWN_PROTOCOL;
michael@0 87
michael@0 88 /* No special mapping for these error codes...
michael@0 89
michael@0 90 case GNOME_VFS_ERROR_GENERIC:
michael@0 91 case GNOME_VFS_ERROR_IO:
michael@0 92 case GNOME_VFS_ERROR_WRONG_FORMAT:
michael@0 93 case GNOME_VFS_ERROR_BAD_FILE:
michael@0 94 case GNOME_VFS_ERROR_NOT_OPEN:
michael@0 95 case GNOME_VFS_ERROR_INVALID_OPEN_MODE:
michael@0 96 case GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES:
michael@0 97 case GNOME_VFS_ERROR_LOOP:
michael@0 98 case GNOME_VFS_ERROR_DIRECTORY_BUSY:
michael@0 99 case GNOME_VFS_ERROR_TOO_MANY_LINKS:
michael@0 100 case GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM:
michael@0 101 case GNOME_VFS_ERROR_SERVICE_OBSOLETE:
michael@0 102 case GNOME_VFS_ERROR_PROTOCOL_ERROR:
michael@0 103 case GNOME_VFS_ERROR_NO_MASTER_BROWSER:
michael@0 104
michael@0 105 */
michael@0 106
michael@0 107 // Make GCC happy
michael@0 108 default:
michael@0 109 return NS_ERROR_FAILURE;
michael@0 110 }
michael@0 111
michael@0 112 return NS_ERROR_FAILURE;
michael@0 113 }
michael@0 114
michael@0 115 //-----------------------------------------------------------------------------
michael@0 116
michael@0 117 static void
michael@0 118 ProxiedAuthCallback(gconstpointer in,
michael@0 119 gsize in_size,
michael@0 120 gpointer out,
michael@0 121 gsize out_size,
michael@0 122 gpointer callback_data)
michael@0 123 {
michael@0 124 GnomeVFSModuleCallbackAuthenticationIn *authIn =
michael@0 125 (GnomeVFSModuleCallbackAuthenticationIn *) in;
michael@0 126 GnomeVFSModuleCallbackAuthenticationOut *authOut =
michael@0 127 (GnomeVFSModuleCallbackAuthenticationOut *) out;
michael@0 128
michael@0 129 LOG(("gnomevfs: ProxiedAuthCallback [uri=%s]\n", authIn->uri));
michael@0 130
michael@0 131 // Without a channel, we have no way of getting a prompter.
michael@0 132 nsIChannel *channel = (nsIChannel *) callback_data;
michael@0 133 if (!channel)
michael@0 134 return;
michael@0 135
michael@0 136 nsCOMPtr<nsIAuthPrompt> prompt;
michael@0 137 NS_QueryNotificationCallbacks(channel, prompt);
michael@0 138
michael@0 139 // If no auth prompt, then give up. We could failover to using the
michael@0 140 // WindowWatcher service, but that might defeat a consumer's purposeful
michael@0 141 // attempt to disable authentication (for whatever reason).
michael@0 142 if (!prompt)
michael@0 143 return;
michael@0 144
michael@0 145 // Parse out the host and port...
michael@0 146 nsCOMPtr<nsIURI> uri;
michael@0 147 channel->GetURI(getter_AddRefs(uri));
michael@0 148 if (!uri)
michael@0 149 return;
michael@0 150
michael@0 151 #ifdef DEBUG
michael@0 152 {
michael@0 153 //
michael@0 154 // Make sure authIn->uri is consistent with the channel's URI.
michael@0 155 //
michael@0 156 // XXX This check is probably not IDN safe, and it might incorrectly
michael@0 157 // fire as a result of escaping differences. It's unclear what
michael@0 158 // kind of transforms GnomeVFS might have applied to the URI spec
michael@0 159 // that we originally gave to it. In spite of the likelihood of
michael@0 160 // false hits, this check is probably still valuable.
michael@0 161 //
michael@0 162 nsAutoCString spec;
michael@0 163 uri->GetSpec(spec);
michael@0 164 int uriLen = strlen(authIn->uri);
michael@0 165 if (!StringHead(spec, uriLen).Equals(nsDependentCString(authIn->uri, uriLen)))
michael@0 166 {
michael@0 167 LOG(("gnomevfs: [spec=%s authIn->uri=%s]\n", spec.get(), authIn->uri));
michael@0 168 NS_ERROR("URI mismatch");
michael@0 169 }
michael@0 170 }
michael@0 171 #endif
michael@0 172
michael@0 173 nsAutoCString scheme, hostPort;
michael@0 174 uri->GetScheme(scheme);
michael@0 175 uri->GetHostPort(hostPort);
michael@0 176
michael@0 177 // It doesn't make sense for either of these strings to be empty. What kind
michael@0 178 // of funky URI is this?
michael@0 179 if (scheme.IsEmpty() || hostPort.IsEmpty())
michael@0 180 return;
michael@0 181
michael@0 182 // Construct the single signon key. Altering the value of this key will
michael@0 183 // cause people's remembered passwords to be forgotten. Think carefully
michael@0 184 // before changing the way this key is constructed.
michael@0 185 nsAutoString key, realm;
michael@0 186
michael@0 187 NS_ConvertUTF8toUTF16 dispHost(scheme);
michael@0 188 dispHost.Append(NS_LITERAL_STRING("://"));
michael@0 189 dispHost.Append(NS_ConvertUTF8toUTF16(hostPort));
michael@0 190
michael@0 191 key = dispHost;
michael@0 192 if (authIn->realm)
michael@0 193 {
michael@0 194 // We assume the realm string is ASCII. That might be a bogus assumption,
michael@0 195 // but we have no idea what encoding GnomeVFS is using, so for now we'll
michael@0 196 // limit ourselves to ISO-Latin-1. XXX What is a better solution?
michael@0 197 realm.Append('"');
michael@0 198 realm.Append(NS_ConvertASCIItoUTF16(authIn->realm));
michael@0 199 realm.Append('"');
michael@0 200 key.Append(' ');
michael@0 201 key.Append(realm);
michael@0 202 }
michael@0 203
michael@0 204 // Construct the message string...
michael@0 205 //
michael@0 206 // We use Necko's string bundle here. This code really should be encapsulated
michael@0 207 // behind some Necko API, after all this code is based closely on the code in
michael@0 208 // nsHttpChannel.cpp.
michael@0 209
michael@0 210 nsCOMPtr<nsIStringBundleService> bundleSvc =
michael@0 211 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
michael@0 212 if (!bundleSvc)
michael@0 213 return;
michael@0 214
michael@0 215 nsCOMPtr<nsIStringBundle> bundle;
michael@0 216 bundleSvc->CreateBundle("chrome://global/locale/commonDialogs.properties",
michael@0 217 getter_AddRefs(bundle));
michael@0 218 if (!bundle)
michael@0 219 return;
michael@0 220
michael@0 221 nsString message;
michael@0 222 if (!realm.IsEmpty())
michael@0 223 {
michael@0 224 const char16_t *strings[] = { realm.get(), dispHost.get() };
michael@0 225 bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordForRealm"),
michael@0 226 strings, 2, getter_Copies(message));
michael@0 227 }
michael@0 228 else
michael@0 229 {
michael@0 230 const char16_t *strings[] = { dispHost.get() };
michael@0 231 bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordFor"),
michael@0 232 strings, 1, getter_Copies(message));
michael@0 233 }
michael@0 234 if (message.IsEmpty())
michael@0 235 return;
michael@0 236
michael@0 237 // Prompt the user...
michael@0 238 nsresult rv;
michael@0 239 bool retval = false;
michael@0 240 char16_t *user = nullptr, *pass = nullptr;
michael@0 241
michael@0 242 rv = prompt->PromptUsernameAndPassword(nullptr, message.get(),
michael@0 243 key.get(),
michael@0 244 nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
michael@0 245 &user, &pass, &retval);
michael@0 246 if (NS_FAILED(rv))
michael@0 247 return;
michael@0 248 if (!retval || !user || !pass)
michael@0 249 return;
michael@0 250
michael@0 251 // XXX We need to convert the UTF-16 username and password from our dialog to
michael@0 252 // strings that GnomeVFS can understand. It's unclear what encoding GnomeVFS
michael@0 253 // expects, so for now we assume 7-bit ASCII. Hopefully, we can get a better
michael@0 254 // solution at some point.
michael@0 255
michael@0 256 // One copy is never enough...
michael@0 257 authOut->username = g_strdup(NS_LossyConvertUTF16toASCII(user).get());
michael@0 258 authOut->password = g_strdup(NS_LossyConvertUTF16toASCII(pass).get());
michael@0 259
michael@0 260 nsMemory::Free(user);
michael@0 261 nsMemory::Free(pass);
michael@0 262 }
michael@0 263
michael@0 264 struct nsGnomeVFSAuthCallbackEvent : public nsRunnable
michael@0 265 {
michael@0 266 gconstpointer in;
michael@0 267 gsize in_size;
michael@0 268 gpointer out;
michael@0 269 gsize out_size;
michael@0 270 gpointer callback_data;
michael@0 271
michael@0 272 NS_IMETHOD Run() {
michael@0 273 ProxiedAuthCallback(in, in_size, out, out_size, callback_data);
michael@0 274 return NS_OK;
michael@0 275 }
michael@0 276 };
michael@0 277
michael@0 278 static void
michael@0 279 AuthCallback(gconstpointer in,
michael@0 280 gsize in_size,
michael@0 281 gpointer out,
michael@0 282 gsize out_size,
michael@0 283 gpointer callback_data)
michael@0 284 {
michael@0 285 // Need to proxy this callback over to the main thread. Synchronous dispatch
michael@0 286 // is required in order to provide data to the GnomeVFS callback.
michael@0 287
michael@0 288 nsRefPtr<nsGnomeVFSAuthCallbackEvent> ev = new nsGnomeVFSAuthCallbackEvent();
michael@0 289 if (!ev)
michael@0 290 return; // OOM
michael@0 291
michael@0 292 ev->in = in;
michael@0 293 ev->in_size = in_size;
michael@0 294 ev->out = out;
michael@0 295 ev->out_size = out_size;
michael@0 296 ev->callback_data = callback_data;
michael@0 297
michael@0 298 NS_DispatchToMainThread(ev, NS_DISPATCH_SYNC);
michael@0 299 }
michael@0 300
michael@0 301 //-----------------------------------------------------------------------------
michael@0 302
michael@0 303 static gint
michael@0 304 FileInfoComparator(gconstpointer a, gconstpointer b)
michael@0 305 {
michael@0 306 const GnomeVFSFileInfo *ia = (const GnomeVFSFileInfo *) a;
michael@0 307 const GnomeVFSFileInfo *ib = (const GnomeVFSFileInfo *) b;
michael@0 308
michael@0 309 return strcasecmp(ia->name, ib->name);
michael@0 310 }
michael@0 311
michael@0 312 //-----------------------------------------------------------------------------
michael@0 313
michael@0 314 class nsGnomeVFSInputStream MOZ_FINAL : public nsIInputStream
michael@0 315 {
michael@0 316 public:
michael@0 317 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 318 NS_DECL_NSIINPUTSTREAM
michael@0 319
michael@0 320 nsGnomeVFSInputStream(const nsCString &uriSpec)
michael@0 321 : mSpec(uriSpec)
michael@0 322 , mChannel(nullptr)
michael@0 323 , mHandle(nullptr)
michael@0 324 , mBytesRemaining(UINT64_MAX)
michael@0 325 , mStatus(NS_OK)
michael@0 326 , mDirList(nullptr)
michael@0 327 , mDirListPtr(nullptr)
michael@0 328 , mDirBufCursor(0)
michael@0 329 , mDirOpen(false) {}
michael@0 330
michael@0 331 ~nsGnomeVFSInputStream() { Close(); }
michael@0 332
michael@0 333 void SetChannel(nsIChannel *channel)
michael@0 334 {
michael@0 335 // We need to hold an owning reference to our channel. This is done
michael@0 336 // so we can access the channel's notification callbacks to acquire
michael@0 337 // a reference to a nsIAuthPrompt if we need to handle a GnomeVFS
michael@0 338 // authentication callback.
michael@0 339 //
michael@0 340 // However, the channel can only be accessed on the main thread, so
michael@0 341 // we have to be very careful with ownership. Moreover, it doesn't
michael@0 342 // support threadsafe addref/release, so proxying is the answer.
michael@0 343 //
michael@0 344 // Also, it's important to note that this likely creates a reference
michael@0 345 // cycle since the channel likely owns this stream. This reference
michael@0 346 // cycle is broken in our Close method.
michael@0 347
michael@0 348 NS_ADDREF(mChannel = channel);
michael@0 349 }
michael@0 350
michael@0 351 private:
michael@0 352 GnomeVFSResult DoOpen();
michael@0 353 GnomeVFSResult DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead);
michael@0 354 nsresult SetContentTypeOfChannel(const char *contentType);
michael@0 355
michael@0 356 private:
michael@0 357 nsCString mSpec;
michael@0 358 nsIChannel *mChannel; // manually refcounted
michael@0 359 GnomeVFSHandle *mHandle;
michael@0 360 uint64_t mBytesRemaining;
michael@0 361 nsresult mStatus;
michael@0 362 GList *mDirList;
michael@0 363 GList *mDirListPtr;
michael@0 364 nsCString mDirBuf;
michael@0 365 uint32_t mDirBufCursor;
michael@0 366 bool mDirOpen;
michael@0 367 };
michael@0 368
michael@0 369 GnomeVFSResult
michael@0 370 nsGnomeVFSInputStream::DoOpen()
michael@0 371 {
michael@0 372 GnomeVFSResult rv;
michael@0 373
michael@0 374 NS_ASSERTION(mHandle == nullptr, "already open");
michael@0 375
michael@0 376 // Push a callback handler on the stack for this thread, so we can intercept
michael@0 377 // authentication requests from GnomeVFS. We'll use the channel to get a
michael@0 378 // nsIAuthPrompt instance.
michael@0 379
michael@0 380 gnome_vfs_module_callback_push(GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION,
michael@0 381 AuthCallback, mChannel, nullptr);
michael@0 382
michael@0 383 // Query the mime type first (this could return nullptr).
michael@0 384 //
michael@0 385 // XXX We need to do this up-front in order to determine how to open the URI.
michael@0 386 // Unfortunately, the error code GNOME_VFS_ERROR_IS_DIRECTORY is not
michael@0 387 // always returned by gnome_vfs_open when we pass it a URI to a directory!
michael@0 388 // Otherwise, we could have used that as a way to failover to opening the
michael@0 389 // URI as a directory. Also, it would have been ideal if
michael@0 390 // gnome_vfs_get_file_info_from_handle were actually implemented by the
michael@0 391 // smb:// module, since that would have allowed us to potentially save a
michael@0 392 // round trip to the server to discover the mime type of the document in
michael@0 393 // the case where gnome_vfs_open would have been used. (Oh well! /me
michael@0 394 // throws hands up in the air and moves on...)
michael@0 395
michael@0 396 GnomeVFSFileInfo info = {0};
michael@0 397 rv = gnome_vfs_get_file_info(mSpec.get(), &info, GnomeVFSFileInfoOptions(
michael@0 398 GNOME_VFS_FILE_INFO_DEFAULT |
michael@0 399 GNOME_VFS_FILE_INFO_FOLLOW_LINKS));
michael@0 400 if (rv == GNOME_VFS_OK)
michael@0 401 {
michael@0 402 if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY)
michael@0 403 {
michael@0 404 rv = gnome_vfs_directory_list_load(&mDirList, mSpec.get(),
michael@0 405 GNOME_VFS_FILE_INFO_DEFAULT);
michael@0 406
michael@0 407 LOG(("gnomevfs: gnome_vfs_directory_list_load returned %d (%s) [spec=\"%s\"]\n",
michael@0 408 rv, gnome_vfs_result_to_string(rv), mSpec.get()));
michael@0 409 }
michael@0 410 else
michael@0 411 {
michael@0 412 rv = gnome_vfs_open(&mHandle, mSpec.get(), GNOME_VFS_OPEN_READ);
michael@0 413
michael@0 414 LOG(("gnomevfs: gnome_vfs_open returned %d (%s) [spec=\"%s\"]\n",
michael@0 415 rv, gnome_vfs_result_to_string(rv), mSpec.get()));
michael@0 416 }
michael@0 417 }
michael@0 418
michael@0 419 gnome_vfs_module_callback_pop(GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION);
michael@0 420
michael@0 421 if (rv == GNOME_VFS_OK)
michael@0 422 {
michael@0 423 if (mHandle)
michael@0 424 {
michael@0 425 // Here we set the content type of the channel to the value of the mime
michael@0 426 // type determined by GnomeVFS. However, if GnomeVFS is telling us that
michael@0 427 // the document is binary, we'll ignore that and keep the channel's
michael@0 428 // content type unspecified. That will enable our content type sniffing
michael@0 429 // algorithms. This should provide more consistent mime type handling.
michael@0 430
michael@0 431 if (info.mime_type && (strcmp(info.mime_type, APPLICATION_OCTET_STREAM) != 0))
michael@0 432 SetContentTypeOfChannel(info.mime_type);
michael@0 433
michael@0 434 mBytesRemaining = info.size;
michael@0 435
michael@0 436 // Update the content length attribute on the channel. We do this
michael@0 437 // synchronously without proxying. This hack is not as bad as it looks!
michael@0 438 if (mBytesRemaining > INT64_MAX) {
michael@0 439 mChannel->SetContentLength(-1);
michael@0 440 } else {
michael@0 441 mChannel->SetContentLength(mBytesRemaining);
michael@0 442 }
michael@0 443 }
michael@0 444 else
michael@0 445 {
michael@0 446 mDirOpen = true;
michael@0 447
michael@0 448 // Sort mDirList
michael@0 449 mDirList = g_list_sort(mDirList, FileInfoComparator);
michael@0 450 mDirListPtr = mDirList;
michael@0 451
michael@0 452 // Write base URL (make sure it ends with a '/')
michael@0 453 mDirBuf.Append("300: ");
michael@0 454 mDirBuf.Append(mSpec);
michael@0 455 if (mSpec.get()[mSpec.Length() - 1] != '/')
michael@0 456 mDirBuf.Append('/');
michael@0 457 mDirBuf.Append('\n');
michael@0 458
michael@0 459 // Write column names
michael@0 460 mDirBuf.Append("200: filename content-length last-modified file-type\n");
michael@0 461
michael@0 462 // Write charset (assume UTF-8)
michael@0 463 // XXX is this correct?
michael@0 464 mDirBuf.Append("301: UTF-8\n");
michael@0 465
michael@0 466 SetContentTypeOfChannel(APPLICATION_HTTP_INDEX_FORMAT);
michael@0 467 }
michael@0 468 }
michael@0 469
michael@0 470 gnome_vfs_file_info_clear(&info);
michael@0 471 return rv;
michael@0 472 }
michael@0 473
michael@0 474 GnomeVFSResult
michael@0 475 nsGnomeVFSInputStream::DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead)
michael@0 476 {
michael@0 477 GnomeVFSResult rv;
michael@0 478
michael@0 479 if (mHandle)
michael@0 480 {
michael@0 481 GnomeVFSFileSize bytesRead;
michael@0 482 rv = gnome_vfs_read(mHandle, aBuf, aCount, &bytesRead);
michael@0 483 if (rv == GNOME_VFS_OK)
michael@0 484 {
michael@0 485 // aCount is 32-bit, so aCountRead is under 32-bit value.
michael@0 486 *aCountRead = (uint32_t) bytesRead;
michael@0 487 mBytesRemaining -= *aCountRead;
michael@0 488 }
michael@0 489 }
michael@0 490 else if (mDirOpen)
michael@0 491 {
michael@0 492 rv = GNOME_VFS_OK;
michael@0 493
michael@0 494 while (aCount && rv != GNOME_VFS_ERROR_EOF)
michael@0 495 {
michael@0 496 // Copy data out of our buffer
michael@0 497 uint32_t bufLen = mDirBuf.Length() - mDirBufCursor;
michael@0 498 if (bufLen)
michael@0 499 {
michael@0 500 uint32_t n = std::min(bufLen, aCount);
michael@0 501 memcpy(aBuf, mDirBuf.get() + mDirBufCursor, n);
michael@0 502 *aCountRead += n;
michael@0 503 aBuf += n;
michael@0 504 aCount -= n;
michael@0 505 mDirBufCursor += n;
michael@0 506 }
michael@0 507
michael@0 508 if (!mDirListPtr) // Are we at the end of the directory list?
michael@0 509 {
michael@0 510 rv = GNOME_VFS_ERROR_EOF;
michael@0 511 }
michael@0 512 else if (aCount) // Do we need more data?
michael@0 513 {
michael@0 514 GnomeVFSFileInfo *info = (GnomeVFSFileInfo *) mDirListPtr->data;
michael@0 515
michael@0 516 // Prune '.' and '..' from directory listing.
michael@0 517 if (info->name[0] == '.' &&
michael@0 518 (info->name[1] == '\0' ||
michael@0 519 (info->name[1] == '.' && info->name[2] == '\0')))
michael@0 520 {
michael@0 521 mDirListPtr = mDirListPtr->next;
michael@0 522 continue;
michael@0 523 }
michael@0 524
michael@0 525 mDirBuf.Assign("201: ");
michael@0 526
michael@0 527 // The "filename" field
michael@0 528 nsCString escName;
michael@0 529 nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID);
michael@0 530 if (nu) {
michael@0 531 nu->EscapeString(nsDependentCString(info->name),
michael@0 532 nsINetUtil::ESCAPE_URL_PATH, escName);
michael@0 533
michael@0 534 mDirBuf.Append(escName);
michael@0 535 mDirBuf.Append(' ');
michael@0 536 }
michael@0 537
michael@0 538 // The "content-length" field
michael@0 539 // XXX truncates size from 64-bit to 32-bit
michael@0 540 mDirBuf.AppendInt(int32_t(info->size));
michael@0 541 mDirBuf.Append(' ');
michael@0 542
michael@0 543 // The "last-modified" field
michael@0 544 //
michael@0 545 // NSPR promises: PRTime is compatible with time_t
michael@0 546 // we just need to convert from seconds to microseconds
michael@0 547 PRExplodedTime tm;
michael@0 548 PRTime pt = ((PRTime) info->mtime) * 1000000;
michael@0 549 PR_ExplodeTime(pt, PR_GMTParameters, &tm);
michael@0 550 {
michael@0 551 char buf[64];
michael@0 552 PR_FormatTimeUSEnglish(buf, sizeof(buf),
michael@0 553 "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm);
michael@0 554 mDirBuf.Append(buf);
michael@0 555 }
michael@0 556
michael@0 557 // The "file-type" field
michael@0 558 switch (info->type)
michael@0 559 {
michael@0 560 case GNOME_VFS_FILE_TYPE_REGULAR:
michael@0 561 mDirBuf.Append("FILE ");
michael@0 562 break;
michael@0 563 case GNOME_VFS_FILE_TYPE_DIRECTORY:
michael@0 564 mDirBuf.Append("DIRECTORY ");
michael@0 565 break;
michael@0 566 case GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK:
michael@0 567 mDirBuf.Append("SYMBOLIC-LINK ");
michael@0 568 break;
michael@0 569 default:
michael@0 570 break;
michael@0 571 }
michael@0 572
michael@0 573 mDirBuf.Append('\n');
michael@0 574
michael@0 575 mDirBufCursor = 0;
michael@0 576 mDirListPtr = mDirListPtr->next;
michael@0 577 }
michael@0 578 }
michael@0 579 }
michael@0 580 else
michael@0 581 {
michael@0 582 NS_NOTREACHED("reading from what?");
michael@0 583 rv = GNOME_VFS_ERROR_GENERIC;
michael@0 584 }
michael@0 585
michael@0 586 return rv;
michael@0 587 }
michael@0 588
michael@0 589 // This class is used to implement SetContentTypeOfChannel.
michael@0 590 class nsGnomeVFSSetContentTypeEvent : public nsRunnable
michael@0 591 {
michael@0 592 public:
michael@0 593 nsGnomeVFSSetContentTypeEvent(nsIChannel *channel, const char *contentType)
michael@0 594 : mChannel(channel), mContentType(contentType)
michael@0 595 {
michael@0 596 // stash channel reference in mChannel. no AddRef here! see note
michael@0 597 // in SetContentTypeOfchannel.
michael@0 598 }
michael@0 599
michael@0 600 NS_IMETHOD Run()
michael@0 601 {
michael@0 602 mChannel->SetContentType(mContentType);
michael@0 603 return NS_OK;
michael@0 604 }
michael@0 605
michael@0 606 private:
michael@0 607 nsIChannel *mChannel;
michael@0 608 nsCString mContentType;
michael@0 609 };
michael@0 610
michael@0 611 nsresult
michael@0 612 nsGnomeVFSInputStream::SetContentTypeOfChannel(const char *contentType)
michael@0 613 {
michael@0 614 // We need to proxy this call over to the main thread. We post an
michael@0 615 // asynchronous event in this case so that we don't delay reading data, and
michael@0 616 // we know that this is safe to do since the channel's reference will be
michael@0 617 // released asynchronously as well. We trust the ordering of the main
michael@0 618 // thread's event queue to protect us against memory corruption.
michael@0 619
michael@0 620 nsresult rv;
michael@0 621 nsCOMPtr<nsIRunnable> ev =
michael@0 622 new nsGnomeVFSSetContentTypeEvent(mChannel, contentType);
michael@0 623 if (!ev)
michael@0 624 {
michael@0 625 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 626 }
michael@0 627 else
michael@0 628 {
michael@0 629 rv = NS_DispatchToMainThread(ev);
michael@0 630 }
michael@0 631 return rv;
michael@0 632 }
michael@0 633
michael@0 634 NS_IMPL_ISUPPORTS(nsGnomeVFSInputStream, nsIInputStream)
michael@0 635
michael@0 636 NS_IMETHODIMP
michael@0 637 nsGnomeVFSInputStream::Close()
michael@0 638 {
michael@0 639 if (mHandle)
michael@0 640 {
michael@0 641 gnome_vfs_close(mHandle);
michael@0 642 mHandle = nullptr;
michael@0 643 }
michael@0 644
michael@0 645 if (mDirList)
michael@0 646 {
michael@0 647 // Destroy the list of GnomeVFSFileInfo objects...
michael@0 648 g_list_foreach(mDirList, (GFunc) gnome_vfs_file_info_unref, nullptr);
michael@0 649 g_list_free(mDirList);
michael@0 650 mDirList = nullptr;
michael@0 651 mDirListPtr = nullptr;
michael@0 652 }
michael@0 653
michael@0 654 if (mChannel)
michael@0 655 {
michael@0 656 nsresult rv = NS_OK;
michael@0 657
michael@0 658 nsCOMPtr<nsIThread> thread = do_GetMainThread();
michael@0 659 if (thread)
michael@0 660 rv = NS_ProxyRelease(thread, mChannel);
michael@0 661
michael@0 662 NS_ASSERTION(thread && NS_SUCCEEDED(rv), "leaking channel reference");
michael@0 663 mChannel = nullptr;
michael@0 664 }
michael@0 665
michael@0 666 mSpec.Truncate(); // free memory
michael@0 667
michael@0 668 // Prevent future reads from re-opening the handle.
michael@0 669 if (NS_SUCCEEDED(mStatus))
michael@0 670 mStatus = NS_BASE_STREAM_CLOSED;
michael@0 671
michael@0 672 return NS_OK;
michael@0 673 }
michael@0 674
michael@0 675 NS_IMETHODIMP
michael@0 676 nsGnomeVFSInputStream::Available(uint64_t *aResult)
michael@0 677 {
michael@0 678 if (NS_FAILED(mStatus))
michael@0 679 return mStatus;
michael@0 680
michael@0 681 *aResult = mBytesRemaining;
michael@0 682 return NS_OK;
michael@0 683 }
michael@0 684
michael@0 685 NS_IMETHODIMP
michael@0 686 nsGnomeVFSInputStream::Read(char *aBuf,
michael@0 687 uint32_t aCount,
michael@0 688 uint32_t *aCountRead)
michael@0 689 {
michael@0 690 *aCountRead = 0;
michael@0 691
michael@0 692 if (mStatus == NS_BASE_STREAM_CLOSED)
michael@0 693 return NS_OK;
michael@0 694 if (NS_FAILED(mStatus))
michael@0 695 return mStatus;
michael@0 696
michael@0 697 GnomeVFSResult rv = GNOME_VFS_OK;
michael@0 698
michael@0 699 // If this is our first-time through here, then open the URI.
michael@0 700 if (!mHandle && !mDirOpen)
michael@0 701 rv = DoOpen();
michael@0 702
michael@0 703 if (rv == GNOME_VFS_OK)
michael@0 704 rv = DoRead(aBuf, aCount, aCountRead);
michael@0 705
michael@0 706 if (rv != GNOME_VFS_OK)
michael@0 707 {
michael@0 708 // If we reach here, we hit some kind of error. EOF is not an error.
michael@0 709 mStatus = MapGnomeVFSResult(rv);
michael@0 710 if (mStatus == NS_BASE_STREAM_CLOSED)
michael@0 711 return NS_OK;
michael@0 712
michael@0 713 LOG(("gnomevfs: result %d [%s] mapped to 0x%x\n",
michael@0 714 rv, gnome_vfs_result_to_string(rv), mStatus));
michael@0 715 }
michael@0 716 return mStatus;
michael@0 717 }
michael@0 718
michael@0 719 NS_IMETHODIMP
michael@0 720 nsGnomeVFSInputStream::ReadSegments(nsWriteSegmentFun aWriter,
michael@0 721 void *aClosure,
michael@0 722 uint32_t aCount,
michael@0 723 uint32_t *aResult)
michael@0 724 {
michael@0 725 // There is no way to implement this using GnomeVFS, but fortunately
michael@0 726 // that doesn't matter. Because we are a blocking input stream, Necko
michael@0 727 // isn't going to call our ReadSegments method.
michael@0 728 NS_NOTREACHED("nsGnomeVFSInputStream::ReadSegments");
michael@0 729 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 730 }
michael@0 731
michael@0 732 NS_IMETHODIMP
michael@0 733 nsGnomeVFSInputStream::IsNonBlocking(bool *aResult)
michael@0 734 {
michael@0 735 *aResult = false;
michael@0 736 return NS_OK;
michael@0 737 }
michael@0 738
michael@0 739 //-----------------------------------------------------------------------------
michael@0 740
michael@0 741 class nsGnomeVFSProtocolHandler MOZ_FINAL : public nsIProtocolHandler
michael@0 742 , public nsIObserver
michael@0 743 {
michael@0 744 public:
michael@0 745 NS_DECL_ISUPPORTS
michael@0 746 NS_DECL_NSIPROTOCOLHANDLER
michael@0 747 NS_DECL_NSIOBSERVER
michael@0 748
michael@0 749 nsresult Init();
michael@0 750
michael@0 751 private:
michael@0 752 void InitSupportedProtocolsPref(nsIPrefBranch *prefs);
michael@0 753 bool IsSupportedProtocol(const nsCString &spec);
michael@0 754
michael@0 755 nsCString mSupportedProtocols;
michael@0 756 };
michael@0 757
michael@0 758 NS_IMPL_ISUPPORTS(nsGnomeVFSProtocolHandler, nsIProtocolHandler, nsIObserver)
michael@0 759
michael@0 760 nsresult
michael@0 761 nsGnomeVFSProtocolHandler::Init()
michael@0 762 {
michael@0 763 #ifdef PR_LOGGING
michael@0 764 sGnomeVFSLog = PR_NewLogModule("gnomevfs");
michael@0 765 #endif
michael@0 766
michael@0 767 if (!gnome_vfs_initialized())
michael@0 768 {
michael@0 769 if (!gnome_vfs_init())
michael@0 770 {
michael@0 771 NS_WARNING("gnome_vfs_init failed");
michael@0 772 return NS_ERROR_UNEXPECTED;
michael@0 773 }
michael@0 774 }
michael@0 775
michael@0 776 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 777 if (prefs)
michael@0 778 {
michael@0 779 InitSupportedProtocolsPref(prefs);
michael@0 780 prefs->AddObserver(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS, this, false);
michael@0 781 }
michael@0 782
michael@0 783 return NS_OK;
michael@0 784 }
michael@0 785
michael@0 786 void
michael@0 787 nsGnomeVFSProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch *prefs)
michael@0 788 {
michael@0 789 // read preferences
michael@0 790 nsresult rv = prefs->GetCharPref(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS,
michael@0 791 getter_Copies(mSupportedProtocols));
michael@0 792 if (NS_SUCCEEDED(rv)) {
michael@0 793 mSupportedProtocols.StripWhitespace();
michael@0 794 ToLowerCase(mSupportedProtocols);
michael@0 795 }
michael@0 796 else
michael@0 797 mSupportedProtocols.Assign("smb:,sftp:"); // use defaults
michael@0 798
michael@0 799 LOG(("gnomevfs: supported protocols \"%s\"\n", mSupportedProtocols.get()));
michael@0 800 }
michael@0 801
michael@0 802 bool
michael@0 803 nsGnomeVFSProtocolHandler::IsSupportedProtocol(const nsCString &aSpec)
michael@0 804 {
michael@0 805 const char *specString = aSpec.get();
michael@0 806 const char *colon = strchr(specString, ':');
michael@0 807 if (!colon)
michael@0 808 return false;
michael@0 809
michael@0 810 uint32_t length = colon - specString + 1;
michael@0 811
michael@0 812 // <scheme> + ':'
michael@0 813 nsCString scheme(specString, length);
michael@0 814
michael@0 815 char *found = PL_strcasestr(mSupportedProtocols.get(), scheme.get());
michael@0 816 if (!found)
michael@0 817 return false;
michael@0 818
michael@0 819 if (found[length] != ',' && found[length] != '\0')
michael@0 820 return false;
michael@0 821
michael@0 822 return true;
michael@0 823 }
michael@0 824
michael@0 825 NS_IMETHODIMP
michael@0 826 nsGnomeVFSProtocolHandler::GetScheme(nsACString &aScheme)
michael@0 827 {
michael@0 828 aScheme.Assign(MOZ_GNOMEVFS_SCHEME);
michael@0 829 return NS_OK;
michael@0 830 }
michael@0 831
michael@0 832 NS_IMETHODIMP
michael@0 833 nsGnomeVFSProtocolHandler::GetDefaultPort(int32_t *aDefaultPort)
michael@0 834 {
michael@0 835 *aDefaultPort = -1;
michael@0 836 return NS_OK;
michael@0 837 }
michael@0 838
michael@0 839 NS_IMETHODIMP
michael@0 840 nsGnomeVFSProtocolHandler::GetProtocolFlags(uint32_t *aProtocolFlags)
michael@0 841 {
michael@0 842 // Is URI_STD true of all GnomeVFS URI types?
michael@0 843 *aProtocolFlags = URI_STD | URI_DANGEROUS_TO_LOAD;
michael@0 844 return NS_OK;
michael@0 845 }
michael@0 846
michael@0 847 NS_IMETHODIMP
michael@0 848 nsGnomeVFSProtocolHandler::NewURI(const nsACString &aSpec,
michael@0 849 const char *aOriginCharset,
michael@0 850 nsIURI *aBaseURI,
michael@0 851 nsIURI **aResult)
michael@0 852 {
michael@0 853 const nsCString flatSpec(aSpec);
michael@0 854 LOG(("gnomevfs: NewURI [spec=%s]\n", flatSpec.get()));
michael@0 855
michael@0 856 if (!aBaseURI)
michael@0 857 {
michael@0 858 //
michael@0 859 // XXX This check is used to limit the gnome-vfs protocols we support. For
michael@0 860 // security reasons, it is best that we limit the protocols we support to
michael@0 861 // those with known characteristics. We might want to lessen this
michael@0 862 // restriction if it proves to be too heavy handed. A black list of
michael@0 863 // protocols we don't want to support might be better. For example, we
michael@0 864 // probably don't want to try to load "start-here:" inside the browser.
michael@0 865 // There are others that fall into this category, which are best handled
michael@0 866 // externally by Nautilus (or another app like it).
michael@0 867 //
michael@0 868 if (!IsSupportedProtocol(flatSpec))
michael@0 869 return NS_ERROR_UNKNOWN_PROTOCOL;
michael@0 870
michael@0 871 // Verify that GnomeVFS supports this URI scheme.
michael@0 872 GnomeVFSURI *uri = gnome_vfs_uri_new(flatSpec.get());
michael@0 873 if (!uri)
michael@0 874 return NS_ERROR_UNKNOWN_PROTOCOL;
michael@0 875 }
michael@0 876
michael@0 877 //
michael@0 878 // XXX Can we really assume that all gnome-vfs URIs can be parsed using
michael@0 879 // nsStandardURL? We probably really need to implement nsIURI/nsIURL
michael@0 880 // in terms of the gnome_vfs_uri_XXX methods, but at least this works
michael@0 881 // correctly for smb:// URLs ;-)
michael@0 882 //
michael@0 883 // Also, it might not be possible to fully implement nsIURI/nsIURL in
michael@0 884 // terms of GnomeVFSURI since some Necko methods have no GnomeVFS
michael@0 885 // equivalent.
michael@0 886 //
michael@0 887 nsresult rv;
michael@0 888 nsCOMPtr<nsIStandardURL> url =
michael@0 889 do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
michael@0 890 if (NS_FAILED(rv))
michael@0 891 return rv;
michael@0 892
michael@0 893 rv = url->Init(nsIStandardURL::URLTYPE_STANDARD, -1, flatSpec,
michael@0 894 aOriginCharset, aBaseURI);
michael@0 895 if (NS_SUCCEEDED(rv))
michael@0 896 rv = CallQueryInterface(url, aResult);
michael@0 897
michael@0 898 return rv;
michael@0 899 }
michael@0 900
michael@0 901 NS_IMETHODIMP
michael@0 902 nsGnomeVFSProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **aResult)
michael@0 903 {
michael@0 904 NS_ENSURE_ARG_POINTER(aURI);
michael@0 905 nsresult rv;
michael@0 906
michael@0 907 nsAutoCString spec;
michael@0 908 rv = aURI->GetSpec(spec);
michael@0 909 if (NS_FAILED(rv))
michael@0 910 return rv;
michael@0 911
michael@0 912 nsRefPtr<nsGnomeVFSInputStream> stream = new nsGnomeVFSInputStream(spec);
michael@0 913 if (!stream)
michael@0 914 {
michael@0 915 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 916 }
michael@0 917 else
michael@0 918 {
michael@0 919 // start out assuming an unknown content-type. we'll set the content-type
michael@0 920 // to something better once we open the URI.
michael@0 921 rv = NS_NewInputStreamChannel(aResult, aURI, stream,
michael@0 922 NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE));
michael@0 923 if (NS_SUCCEEDED(rv))
michael@0 924 stream->SetChannel(*aResult);
michael@0 925 }
michael@0 926 return rv;
michael@0 927 }
michael@0 928
michael@0 929 NS_IMETHODIMP
michael@0 930 nsGnomeVFSProtocolHandler::AllowPort(int32_t aPort,
michael@0 931 const char *aScheme,
michael@0 932 bool *aResult)
michael@0 933 {
michael@0 934 // Don't override anything.
michael@0 935 *aResult = false;
michael@0 936 return NS_OK;
michael@0 937 }
michael@0 938
michael@0 939 NS_IMETHODIMP
michael@0 940 nsGnomeVFSProtocolHandler::Observe(nsISupports *aSubject,
michael@0 941 const char *aTopic,
michael@0 942 const char16_t *aData)
michael@0 943 {
michael@0 944 if (strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
michael@0 945 nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
michael@0 946 InitSupportedProtocolsPref(prefs);
michael@0 947 }
michael@0 948 return NS_OK;
michael@0 949 }
michael@0 950
michael@0 951 //-----------------------------------------------------------------------------
michael@0 952
michael@0 953 #define NS_GNOMEVFSPROTOCOLHANDLER_CID \
michael@0 954 { /* 9b6dc177-a2e4-49e1-9c98-0a8384de7f6c */ \
michael@0 955 0x9b6dc177, \
michael@0 956 0xa2e4, \
michael@0 957 0x49e1, \
michael@0 958 {0x9c, 0x98, 0x0a, 0x83, 0x84, 0xde, 0x7f, 0x6c} \
michael@0 959 }
michael@0 960
michael@0 961 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGnomeVFSProtocolHandler, Init)
michael@0 962 NS_DEFINE_NAMED_CID(NS_GNOMEVFSPROTOCOLHANDLER_CID);
michael@0 963
michael@0 964 static const mozilla::Module::CIDEntry kVFSCIDs[] = {
michael@0 965 { &kNS_GNOMEVFSPROTOCOLHANDLER_CID, false, nullptr, nsGnomeVFSProtocolHandlerConstructor },
michael@0 966 { nullptr }
michael@0 967 };
michael@0 968
michael@0 969 static const mozilla::Module::ContractIDEntry kVFSContracts[] = {
michael@0 970 { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MOZ_GNOMEVFS_SCHEME, &kNS_GNOMEVFSPROTOCOLHANDLER_CID },
michael@0 971 { nullptr }
michael@0 972 };
michael@0 973
michael@0 974 static const mozilla::Module kVFSModule = {
michael@0 975 mozilla::Module::kVersion,
michael@0 976 kVFSCIDs,
michael@0 977 kVFSContracts
michael@0 978 };
michael@0 979
michael@0 980 NSMODULE_DEFN(nsGnomeVFSModule) = &kVFSModule;

mercurial