1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/extensions/gio/nsGIOProtocolHandler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1126 @@ 1.4 +/* vim:set ts=2 sw=2 et cindent: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * This code is based on original Mozilla gnome-vfs extension. It implements 1.11 + * input stream provided by GVFS/GIO. 1.12 +*/ 1.13 +#include "mozilla/ModuleUtils.h" 1.14 +#include "nsIPrefService.h" 1.15 +#include "nsIPrefBranch.h" 1.16 +#include "nsIObserver.h" 1.17 +#include "nsThreadUtils.h" 1.18 +#include "nsProxyRelease.h" 1.19 +#include "nsIStringBundle.h" 1.20 +#include "nsIStandardURL.h" 1.21 +#include "nsMimeTypes.h" 1.22 +#include "nsNetUtil.h" 1.23 +#include "mozilla/Monitor.h" 1.24 +#include <gio/gio.h> 1.25 +#include <algorithm> 1.26 + 1.27 +#define MOZ_GIO_SCHEME "moz-gio" 1.28 +#define MOZ_GIO_SUPPORTED_PROTOCOLS "network.gio.supported-protocols" 1.29 + 1.30 +//----------------------------------------------------------------------------- 1.31 + 1.32 +// NSPR_LOG_MODULES=gio:5 1.33 +#ifdef PR_LOGGING 1.34 +static PRLogModuleInfo *sGIOLog; 1.35 +#define LOG(args) PR_LOG(sGIOLog, PR_LOG_DEBUG, args) 1.36 +#else 1.37 +#define LOG(args) 1.38 +#endif 1.39 + 1.40 + 1.41 +//----------------------------------------------------------------------------- 1.42 +static nsresult 1.43 +MapGIOResult(gint code) 1.44 +{ 1.45 + switch (code) 1.46 + { 1.47 + case G_IO_ERROR_NOT_FOUND: return NS_ERROR_FILE_NOT_FOUND; // shows error 1.48 + case G_IO_ERROR_INVALID_ARGUMENT: return NS_ERROR_INVALID_ARG; 1.49 + case G_IO_ERROR_NOT_SUPPORTED: return NS_ERROR_NOT_AVAILABLE; 1.50 + case G_IO_ERROR_NO_SPACE: return NS_ERROR_FILE_NO_DEVICE_SPACE; 1.51 + case G_IO_ERROR_READ_ONLY: return NS_ERROR_FILE_READ_ONLY; 1.52 + case G_IO_ERROR_PERMISSION_DENIED: return NS_ERROR_FILE_ACCESS_DENIED; // wrong password/login 1.53 + case G_IO_ERROR_CLOSED: return NS_BASE_STREAM_CLOSED; // was EOF 1.54 + case G_IO_ERROR_NOT_DIRECTORY: return NS_ERROR_FILE_NOT_DIRECTORY; 1.55 + case G_IO_ERROR_PENDING: return NS_ERROR_IN_PROGRESS; 1.56 + case G_IO_ERROR_EXISTS: return NS_ERROR_FILE_ALREADY_EXISTS; 1.57 + case G_IO_ERROR_IS_DIRECTORY: return NS_ERROR_FILE_IS_DIRECTORY; 1.58 + case G_IO_ERROR_NOT_MOUNTED: return NS_ERROR_NOT_CONNECTED; // shows error 1.59 + case G_IO_ERROR_HOST_NOT_FOUND: return NS_ERROR_UNKNOWN_HOST; // shows error 1.60 + case G_IO_ERROR_CANCELLED: return NS_ERROR_ABORT; 1.61 + case G_IO_ERROR_NOT_EMPTY: return NS_ERROR_FILE_DIR_NOT_EMPTY; 1.62 + case G_IO_ERROR_FILENAME_TOO_LONG: return NS_ERROR_FILE_NAME_TOO_LONG; 1.63 + case G_IO_ERROR_INVALID_FILENAME: return NS_ERROR_FILE_INVALID_PATH; 1.64 + case G_IO_ERROR_TIMED_OUT: return NS_ERROR_NET_TIMEOUT; // shows error 1.65 + case G_IO_ERROR_WOULD_BLOCK: return NS_BASE_STREAM_WOULD_BLOCK; 1.66 + case G_IO_ERROR_FAILED_HANDLED: return NS_ERROR_ABORT; // Cancel on login dialog 1.67 + 1.68 +/* unhandled: 1.69 + G_IO_ERROR_NOT_REGULAR_FILE, 1.70 + G_IO_ERROR_NOT_SYMBOLIC_LINK, 1.71 + G_IO_ERROR_NOT_MOUNTABLE_FILE, 1.72 + G_IO_ERROR_TOO_MANY_LINKS, 1.73 + G_IO_ERROR_ALREADY_MOUNTED, 1.74 + G_IO_ERROR_CANT_CREATE_BACKUP, 1.75 + G_IO_ERROR_WRONG_ETAG, 1.76 + G_IO_ERROR_WOULD_RECURSE, 1.77 + G_IO_ERROR_BUSY, 1.78 + G_IO_ERROR_WOULD_MERGE, 1.79 + G_IO_ERROR_TOO_MANY_OPEN_FILES 1.80 +*/ 1.81 + // Make GCC happy 1.82 + default: 1.83 + return NS_ERROR_FAILURE; 1.84 + } 1.85 + 1.86 + return NS_ERROR_FAILURE; 1.87 +} 1.88 + 1.89 +static nsresult 1.90 +MapGIOResult(GError *result) 1.91 +{ 1.92 + if (!result) 1.93 + return NS_OK; 1.94 + else 1.95 + return MapGIOResult(result->code); 1.96 +} 1.97 +/** Return values for mount operation. 1.98 + * These enums are used as mount operation return values. 1.99 + */ 1.100 +typedef enum { 1.101 + MOUNT_OPERATION_IN_PROGRESS, /** \enum operation in progress */ 1.102 + MOUNT_OPERATION_SUCCESS, /** \enum operation successful */ 1.103 + MOUNT_OPERATION_FAILED /** \enum operation not successful */ 1.104 +} MountOperationResult; 1.105 +//----------------------------------------------------------------------------- 1.106 +/** 1.107 + * Sort function compares according to file type (directory/file) 1.108 + * and alphabethical order 1.109 + * @param a pointer to GFileInfo object to compare 1.110 + * @param b pointer to GFileInfo object to compare 1.111 + * @return -1 when first object should be before the second, 0 when equal, 1.112 + * +1 when second object should be before the first 1.113 + */ 1.114 +static gint 1.115 +FileInfoComparator(gconstpointer a, gconstpointer b) 1.116 +{ 1.117 + GFileInfo *ia = ( GFileInfo *) a; 1.118 + GFileInfo *ib = ( GFileInfo *) b; 1.119 + if (g_file_info_get_file_type(ia) == G_FILE_TYPE_DIRECTORY 1.120 + && g_file_info_get_file_type(ib) != G_FILE_TYPE_DIRECTORY) 1.121 + return -1; 1.122 + if (g_file_info_get_file_type(ib) == G_FILE_TYPE_DIRECTORY 1.123 + && g_file_info_get_file_type(ia) != G_FILE_TYPE_DIRECTORY) 1.124 + return 1; 1.125 + 1.126 + return strcasecmp(g_file_info_get_name(ia), g_file_info_get_name(ib)); 1.127 +} 1.128 + 1.129 +/* Declaration of mount callback functions */ 1.130 +static void mount_enclosing_volume_finished (GObject *source_object, 1.131 + GAsyncResult *res, 1.132 + gpointer user_data); 1.133 +static void mount_operation_ask_password (GMountOperation *mount_op, 1.134 + const char *message, 1.135 + const char *default_user, 1.136 + const char *default_domain, 1.137 + GAskPasswordFlags flags, 1.138 + gpointer user_data); 1.139 +//----------------------------------------------------------------------------- 1.140 + 1.141 +class nsGIOInputStream MOZ_FINAL : public nsIInputStream 1.142 +{ 1.143 + public: 1.144 + NS_DECL_THREADSAFE_ISUPPORTS 1.145 + NS_DECL_NSIINPUTSTREAM 1.146 + 1.147 + nsGIOInputStream(const nsCString &uriSpec) 1.148 + : mSpec(uriSpec) 1.149 + , mChannel(nullptr) 1.150 + , mHandle(nullptr) 1.151 + , mStream(nullptr) 1.152 + , mBytesRemaining(UINT64_MAX) 1.153 + , mStatus(NS_OK) 1.154 + , mDirList(nullptr) 1.155 + , mDirListPtr(nullptr) 1.156 + , mDirBufCursor(0) 1.157 + , mDirOpen(false) 1.158 + , mMonitorMountInProgress("GIOInputStream::MountFinished") { } 1.159 + 1.160 + ~nsGIOInputStream() { Close(); } 1.161 + 1.162 + void SetChannel(nsIChannel *channel) 1.163 + { 1.164 + // We need to hold an owning reference to our channel. This is done 1.165 + // so we can access the channel's notification callbacks to acquire 1.166 + // a reference to a nsIAuthPrompt if we need to handle an interactive 1.167 + // mount operation. 1.168 + // 1.169 + // However, the channel can only be accessed on the main thread, so 1.170 + // we have to be very careful with ownership. Moreover, it doesn't 1.171 + // support threadsafe addref/release, so proxying is the answer. 1.172 + // 1.173 + // Also, it's important to note that this likely creates a reference 1.174 + // cycle since the channel likely owns this stream. This reference 1.175 + // cycle is broken in our Close method. 1.176 + 1.177 + NS_ADDREF(mChannel = channel); 1.178 + } 1.179 + void SetMountResult(MountOperationResult result, gint error_code); 1.180 + private: 1.181 + nsresult DoOpen(); 1.182 + nsresult DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead); 1.183 + nsresult SetContentTypeOfChannel(const char *contentType); 1.184 + nsresult MountVolume(); 1.185 + nsresult DoOpenDirectory(); 1.186 + nsresult DoOpenFile(GFileInfo *info); 1.187 + nsCString mSpec; 1.188 + nsIChannel *mChannel; // manually refcounted 1.189 + GFile *mHandle; 1.190 + GFileInputStream *mStream; 1.191 + uint64_t mBytesRemaining; 1.192 + nsresult mStatus; 1.193 + GList *mDirList; 1.194 + GList *mDirListPtr; 1.195 + nsCString mDirBuf; 1.196 + uint32_t mDirBufCursor; 1.197 + bool mDirOpen; 1.198 + MountOperationResult mMountRes; 1.199 + mozilla::Monitor mMonitorMountInProgress; 1.200 + gint mMountErrorCode; 1.201 +}; 1.202 +/** 1.203 + * Set result of mount operation and notify monitor waiting for results. 1.204 + * This method is called in main thread as long as it is used only 1.205 + * in mount_enclosing_volume_finished function. 1.206 + * @param result Result of mount operation 1.207 + */ 1.208 +void 1.209 +nsGIOInputStream::SetMountResult(MountOperationResult result, gint error_code) 1.210 +{ 1.211 + mozilla::MonitorAutoLock mon(mMonitorMountInProgress); 1.212 + mMountRes = result; 1.213 + mMountErrorCode = error_code; 1.214 + mon.Notify(); 1.215 +} 1.216 + 1.217 +/** 1.218 + * Start mount operation and wait in loop until it is finished. This method is 1.219 + * called from thread which is trying to read from location. 1.220 + */ 1.221 +nsresult 1.222 +nsGIOInputStream::MountVolume() { 1.223 + GMountOperation* mount_op = g_mount_operation_new(); 1.224 + g_signal_connect (mount_op, "ask-password", 1.225 + G_CALLBACK (mount_operation_ask_password), mChannel); 1.226 + mMountRes = MOUNT_OPERATION_IN_PROGRESS; 1.227 + /* g_file_mount_enclosing_volume uses a dbus request to mount the volume. 1.228 + Callback mount_enclosing_volume_finished is called in main thread 1.229 + (not this thread on which this method is called). */ 1.230 + g_file_mount_enclosing_volume(mHandle, 1.231 + G_MOUNT_MOUNT_NONE, 1.232 + mount_op, 1.233 + nullptr, 1.234 + mount_enclosing_volume_finished, 1.235 + this); 1.236 + mozilla::MonitorAutoLock mon(mMonitorMountInProgress); 1.237 + /* Waiting for finish of mount operation thread */ 1.238 + while (mMountRes == MOUNT_OPERATION_IN_PROGRESS) 1.239 + mon.Wait(); 1.240 + 1.241 + g_object_unref(mount_op); 1.242 + 1.243 + if (mMountRes == MOUNT_OPERATION_FAILED) { 1.244 + return MapGIOResult(mMountErrorCode); 1.245 + } else { 1.246 + return NS_OK; 1.247 + } 1.248 +} 1.249 + 1.250 +/** 1.251 + * Create list of infos about objects in opened directory 1.252 + * Return: NS_OK when list obtained, otherwise error code according 1.253 + * to failed operation. 1.254 + */ 1.255 +nsresult 1.256 +nsGIOInputStream::DoOpenDirectory() 1.257 +{ 1.258 + GError *error = nullptr; 1.259 + 1.260 + GFileEnumerator *f_enum = g_file_enumerate_children(mHandle, 1.261 + "standard::*,time::*", 1.262 + G_FILE_QUERY_INFO_NONE, 1.263 + nullptr, 1.264 + &error); 1.265 + if (!f_enum) { 1.266 + nsresult rv = MapGIOResult(error); 1.267 + g_warning("Cannot read from directory: %s", error->message); 1.268 + g_error_free(error); 1.269 + return rv; 1.270 + } 1.271 + // fill list of file infos 1.272 + GFileInfo *info = g_file_enumerator_next_file(f_enum, nullptr, &error); 1.273 + while (info) { 1.274 + mDirList = g_list_append(mDirList, info); 1.275 + info = g_file_enumerator_next_file(f_enum, nullptr, &error); 1.276 + } 1.277 + g_object_unref(f_enum); 1.278 + if (error) { 1.279 + g_warning("Error reading directory content: %s", error->message); 1.280 + nsresult rv = MapGIOResult(error); 1.281 + g_error_free(error); 1.282 + return rv; 1.283 + } 1.284 + mDirOpen = true; 1.285 + 1.286 + // Sort list of file infos by using FileInfoComparator function 1.287 + mDirList = g_list_sort(mDirList, FileInfoComparator); 1.288 + mDirListPtr = mDirList; 1.289 + 1.290 + // Write base URL (make sure it ends with a '/') 1.291 + mDirBuf.Append("300: "); 1.292 + mDirBuf.Append(mSpec); 1.293 + if (mSpec.get()[mSpec.Length() - 1] != '/') 1.294 + mDirBuf.Append('/'); 1.295 + mDirBuf.Append('\n'); 1.296 + 1.297 + // Write column names 1.298 + mDirBuf.Append("200: filename content-length last-modified file-type\n"); 1.299 + 1.300 + // Write charset (assume UTF-8) 1.301 + // XXX is this correct? 1.302 + mDirBuf.Append("301: UTF-8\n"); 1.303 + SetContentTypeOfChannel(APPLICATION_HTTP_INDEX_FORMAT); 1.304 + return NS_OK; 1.305 +} 1.306 + 1.307 +/** 1.308 + * Create file stream and set mime type for channel 1.309 + * @param info file info used to determine mime type 1.310 + * @return NS_OK when file stream created successfuly, error code otherwise 1.311 + */ 1.312 +nsresult 1.313 +nsGIOInputStream::DoOpenFile(GFileInfo *info) 1.314 +{ 1.315 + GError *error = nullptr; 1.316 + 1.317 + mStream = g_file_read(mHandle, nullptr, &error); 1.318 + if (!mStream) { 1.319 + nsresult rv = MapGIOResult(error); 1.320 + g_warning("Cannot read from file: %s", error->message); 1.321 + g_error_free(error); 1.322 + return rv; 1.323 + } 1.324 + 1.325 + const char * content_type = g_file_info_get_content_type(info); 1.326 + if (content_type) { 1.327 + char *mime_type = g_content_type_get_mime_type(content_type); 1.328 + if (mime_type) { 1.329 + if (strcmp(mime_type, APPLICATION_OCTET_STREAM) != 0) { 1.330 + SetContentTypeOfChannel(mime_type); 1.331 + } 1.332 + g_free(mime_type); 1.333 + } 1.334 + } else { 1.335 + g_warning("Missing content type."); 1.336 + } 1.337 + 1.338 + mBytesRemaining = g_file_info_get_size(info); 1.339 + // Update the content length attribute on the channel. We do this 1.340 + // synchronously without proxying. This hack is not as bad as it looks! 1.341 + mChannel->SetContentLength(mBytesRemaining); 1.342 + 1.343 + return NS_OK; 1.344 +} 1.345 + 1.346 +/** 1.347 + * Start file open operation, mount volume when needed and according to file type 1.348 + * create file output stream or read directory content. 1.349 + * @return NS_OK when file or directory opened successfully, error code otherwise 1.350 + */ 1.351 +nsresult 1.352 +nsGIOInputStream::DoOpen() 1.353 +{ 1.354 + nsresult rv; 1.355 + GError *error = nullptr; 1.356 + 1.357 + NS_ASSERTION(mHandle == nullptr, "already open"); 1.358 + 1.359 + mHandle = g_file_new_for_uri( mSpec.get() ); 1.360 + 1.361 + GFileInfo *info = g_file_query_info(mHandle, 1.362 + "standard::*", 1.363 + G_FILE_QUERY_INFO_NONE, 1.364 + nullptr, 1.365 + &error); 1.366 + 1.367 + if (error) { 1.368 + if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_MOUNTED) { 1.369 + // location is not yet mounted, try to mount 1.370 + g_error_free(error); 1.371 + if (NS_IsMainThread()) 1.372 + return NS_ERROR_NOT_CONNECTED; 1.373 + error = nullptr; 1.374 + rv = MountVolume(); 1.375 + if (rv != NS_OK) { 1.376 + return rv; 1.377 + } 1.378 + // get info again 1.379 + info = g_file_query_info(mHandle, 1.380 + "standard::*", 1.381 + G_FILE_QUERY_INFO_NONE, 1.382 + nullptr, 1.383 + &error); 1.384 + // second try to get file info from remote files after media mount 1.385 + if (!info) { 1.386 + g_warning("Unable to get file info: %s", error->message); 1.387 + rv = MapGIOResult(error); 1.388 + g_error_free(error); 1.389 + return rv; 1.390 + } 1.391 + } else { 1.392 + g_warning("Unable to get file info: %s", error->message); 1.393 + rv = MapGIOResult(error); 1.394 + g_error_free(error); 1.395 + return rv; 1.396 + } 1.397 + } 1.398 + // Get file type to handle directories and file differently 1.399 + GFileType f_type = g_file_info_get_file_type(info); 1.400 + if (f_type == G_FILE_TYPE_DIRECTORY) { 1.401 + // directory 1.402 + rv = DoOpenDirectory(); 1.403 + } else if (f_type != G_FILE_TYPE_UNKNOWN) { 1.404 + // file 1.405 + rv = DoOpenFile(info); 1.406 + } else { 1.407 + g_warning("Unable to get file type."); 1.408 + rv = NS_ERROR_FILE_NOT_FOUND; 1.409 + } 1.410 + if (info) 1.411 + g_object_unref(info); 1.412 + return rv; 1.413 +} 1.414 + 1.415 +/** 1.416 + * Read content of file or create file list from directory 1.417 + * @param aBuf read destination buffer 1.418 + * @param aCount length of destination buffer 1.419 + * @param aCountRead number of read characters 1.420 + * @return NS_OK when read successfully, NS_BASE_STREAM_CLOSED when end of file, 1.421 + * error code otherwise 1.422 + */ 1.423 +nsresult 1.424 +nsGIOInputStream::DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead) 1.425 +{ 1.426 + nsresult rv = NS_ERROR_NOT_AVAILABLE; 1.427 + if (mStream) { 1.428 + // file read 1.429 + GError *error = nullptr; 1.430 + uint32_t bytes_read = g_input_stream_read(G_INPUT_STREAM(mStream), 1.431 + aBuf, 1.432 + aCount, 1.433 + nullptr, 1.434 + &error); 1.435 + if (error) { 1.436 + rv = MapGIOResult(error); 1.437 + *aCountRead = 0; 1.438 + g_warning("Cannot read from file: %s", error->message); 1.439 + g_error_free(error); 1.440 + return rv; 1.441 + } 1.442 + *aCountRead = bytes_read; 1.443 + mBytesRemaining -= *aCountRead; 1.444 + return NS_OK; 1.445 + } 1.446 + else if (mDirOpen) { 1.447 + // directory read 1.448 + while (aCount && rv != NS_BASE_STREAM_CLOSED) 1.449 + { 1.450 + // Copy data out of our buffer 1.451 + uint32_t bufLen = mDirBuf.Length() - mDirBufCursor; 1.452 + if (bufLen) 1.453 + { 1.454 + uint32_t n = std::min(bufLen, aCount); 1.455 + memcpy(aBuf, mDirBuf.get() + mDirBufCursor, n); 1.456 + *aCountRead += n; 1.457 + aBuf += n; 1.458 + aCount -= n; 1.459 + mDirBufCursor += n; 1.460 + } 1.461 + 1.462 + if (!mDirListPtr) // Are we at the end of the directory list? 1.463 + { 1.464 + rv = NS_BASE_STREAM_CLOSED; 1.465 + } 1.466 + else if (aCount) // Do we need more data? 1.467 + { 1.468 + GFileInfo *info = (GFileInfo *) mDirListPtr->data; 1.469 + 1.470 + // Prune '.' and '..' from directory listing. 1.471 + const char * fname = g_file_info_get_name(info); 1.472 + if (fname && fname[0] == '.' && 1.473 + (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))) 1.474 + { 1.475 + mDirListPtr = mDirListPtr->next; 1.476 + continue; 1.477 + } 1.478 + 1.479 + mDirBuf.Assign("201: "); 1.480 + 1.481 + // The "filename" field 1.482 + nsCString escName; 1.483 + nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID); 1.484 + if (nu && fname) { 1.485 + nu->EscapeString(nsDependentCString(fname), 1.486 + nsINetUtil::ESCAPE_URL_PATH, escName); 1.487 + 1.488 + mDirBuf.Append(escName); 1.489 + mDirBuf.Append(' '); 1.490 + } 1.491 + 1.492 + // The "content-length" field 1.493 + // XXX truncates size from 64-bit to 32-bit 1.494 + mDirBuf.AppendInt(int32_t(g_file_info_get_size(info))); 1.495 + mDirBuf.Append(' '); 1.496 + 1.497 + // The "last-modified" field 1.498 + // 1.499 + // NSPR promises: PRTime is compatible with time_t 1.500 + // we just need to convert from seconds to microseconds 1.501 + GTimeVal gtime; 1.502 + g_file_info_get_modification_time(info, >ime); 1.503 + 1.504 + PRExplodedTime tm; 1.505 + PRTime pt = ((PRTime) gtime.tv_sec) * 1000000; 1.506 + PR_ExplodeTime(pt, PR_GMTParameters, &tm); 1.507 + { 1.508 + char buf[64]; 1.509 + PR_FormatTimeUSEnglish(buf, sizeof(buf), 1.510 + "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm); 1.511 + mDirBuf.Append(buf); 1.512 + } 1.513 + 1.514 + // The "file-type" field 1.515 + switch (g_file_info_get_file_type(info)) 1.516 + { 1.517 + case G_FILE_TYPE_REGULAR: 1.518 + mDirBuf.Append("FILE "); 1.519 + break; 1.520 + case G_FILE_TYPE_DIRECTORY: 1.521 + mDirBuf.Append("DIRECTORY "); 1.522 + break; 1.523 + case G_FILE_TYPE_SYMBOLIC_LINK: 1.524 + mDirBuf.Append("SYMBOLIC-LINK "); 1.525 + break; 1.526 + default: 1.527 + break; 1.528 + } 1.529 + mDirBuf.Append('\n'); 1.530 + 1.531 + mDirBufCursor = 0; 1.532 + mDirListPtr = mDirListPtr->next; 1.533 + } 1.534 + } 1.535 + } 1.536 + return rv; 1.537 +} 1.538 + 1.539 +/** 1.540 + * This class is used to implement SetContentTypeOfChannel. 1.541 + */ 1.542 +class nsGIOSetContentTypeEvent : public nsRunnable 1.543 +{ 1.544 + public: 1.545 + nsGIOSetContentTypeEvent(nsIChannel *channel, const char *contentType) 1.546 + : mChannel(channel), mContentType(contentType) 1.547 + { 1.548 + // stash channel reference in mChannel. no AddRef here! see note 1.549 + // in SetContentTypeOfchannel. 1.550 + } 1.551 + 1.552 + NS_IMETHOD Run() 1.553 + { 1.554 + mChannel->SetContentType(mContentType); 1.555 + return NS_OK; 1.556 + } 1.557 + 1.558 + private: 1.559 + nsIChannel *mChannel; 1.560 + nsCString mContentType; 1.561 +}; 1.562 + 1.563 +nsresult 1.564 +nsGIOInputStream::SetContentTypeOfChannel(const char *contentType) 1.565 +{ 1.566 + // We need to proxy this call over to the main thread. We post an 1.567 + // asynchronous event in this case so that we don't delay reading data, and 1.568 + // we know that this is safe to do since the channel's reference will be 1.569 + // released asynchronously as well. We trust the ordering of the main 1.570 + // thread's event queue to protect us against memory corruption. 1.571 + 1.572 + nsresult rv; 1.573 + nsCOMPtr<nsIRunnable> ev = 1.574 + new nsGIOSetContentTypeEvent(mChannel, contentType); 1.575 + if (!ev) 1.576 + { 1.577 + rv = NS_ERROR_OUT_OF_MEMORY; 1.578 + } 1.579 + else 1.580 + { 1.581 + rv = NS_DispatchToMainThread(ev); 1.582 + } 1.583 + return rv; 1.584 +} 1.585 + 1.586 +NS_IMPL_ISUPPORTS(nsGIOInputStream, nsIInputStream) 1.587 + 1.588 +/** 1.589 + * Free all used memory and close stream. 1.590 + */ 1.591 +NS_IMETHODIMP 1.592 +nsGIOInputStream::Close() 1.593 +{ 1.594 + if (mStream) 1.595 + { 1.596 + g_object_unref(mStream); 1.597 + mStream = nullptr; 1.598 + } 1.599 + 1.600 + if (mHandle) 1.601 + { 1.602 + g_object_unref(mHandle); 1.603 + mHandle = nullptr; 1.604 + } 1.605 + 1.606 + if (mDirList) 1.607 + { 1.608 + // Destroy the list of GIOFileInfo objects... 1.609 + g_list_foreach(mDirList, (GFunc) g_object_unref, nullptr); 1.610 + g_list_free(mDirList); 1.611 + mDirList = nullptr; 1.612 + mDirListPtr = nullptr; 1.613 + } 1.614 + 1.615 + if (mChannel) 1.616 + { 1.617 + nsresult rv = NS_OK; 1.618 + 1.619 + nsCOMPtr<nsIThread> thread = do_GetMainThread(); 1.620 + if (thread) 1.621 + rv = NS_ProxyRelease(thread, mChannel); 1.622 + 1.623 + NS_ASSERTION(thread && NS_SUCCEEDED(rv), "leaking channel reference"); 1.624 + mChannel = nullptr; 1.625 + (void) rv; 1.626 + } 1.627 + 1.628 + mSpec.Truncate(); // free memory 1.629 + 1.630 + // Prevent future reads from re-opening the handle. 1.631 + if (NS_SUCCEEDED(mStatus)) 1.632 + mStatus = NS_BASE_STREAM_CLOSED; 1.633 + 1.634 + return NS_OK; 1.635 +} 1.636 + 1.637 +/** 1.638 + * Return number of remaining bytes available on input 1.639 + * @param aResult remaining bytes 1.640 + */ 1.641 +NS_IMETHODIMP 1.642 +nsGIOInputStream::Available(uint64_t *aResult) 1.643 +{ 1.644 + if (NS_FAILED(mStatus)) 1.645 + return mStatus; 1.646 + 1.647 + *aResult = mBytesRemaining; 1.648 + 1.649 + return NS_OK; 1.650 +} 1.651 + 1.652 +/** 1.653 + * Trying to read from stream. When location is not available it tries to mount it. 1.654 + * @param aBuf buffer to put read data 1.655 + * @param aCount length of aBuf 1.656 + * @param aCountRead number of bytes actually read 1.657 + */ 1.658 +NS_IMETHODIMP 1.659 +nsGIOInputStream::Read(char *aBuf, 1.660 + uint32_t aCount, 1.661 + uint32_t *aCountRead) 1.662 +{ 1.663 + *aCountRead = 0; 1.664 + // Check if file is already opened, otherwise open it 1.665 + if (!mStream && !mDirOpen && mStatus == NS_OK) { 1.666 + mStatus = DoOpen(); 1.667 + if (NS_FAILED(mStatus)) { 1.668 + return mStatus; 1.669 + } 1.670 + } 1.671 + 1.672 + mStatus = DoRead(aBuf, aCount, aCountRead); 1.673 + // Check if all data has been read 1.674 + if (mStatus == NS_BASE_STREAM_CLOSED) 1.675 + return NS_OK; 1.676 + 1.677 + // Check whenever any error appears while reading 1.678 + return mStatus; 1.679 +} 1.680 + 1.681 +NS_IMETHODIMP 1.682 +nsGIOInputStream::ReadSegments(nsWriteSegmentFun aWriter, 1.683 + void *aClosure, 1.684 + uint32_t aCount, 1.685 + uint32_t *aResult) 1.686 +{ 1.687 + // There is no way to implement this using GnomeVFS, but fortunately 1.688 + // that doesn't matter. Because we are a blocking input stream, Necko 1.689 + // isn't going to call our ReadSegments method. 1.690 + NS_NOTREACHED("nsGIOInputStream::ReadSegments"); 1.691 + return NS_ERROR_NOT_IMPLEMENTED; 1.692 +} 1.693 + 1.694 +NS_IMETHODIMP 1.695 +nsGIOInputStream::IsNonBlocking(bool *aResult) 1.696 +{ 1.697 + *aResult = false; 1.698 + return NS_OK; 1.699 +} 1.700 + 1.701 +//----------------------------------------------------------------------------- 1.702 + 1.703 +/** 1.704 + * Called when finishing mount operation. Result of operation is set in 1.705 + * nsGIOInputStream. This function is called in main thread as an async request 1.706 + * typically from dbus. 1.707 + * @param source_object GFile object which requested the mount 1.708 + * @param res result object 1.709 + * @param user_data pointer to nsGIOInputStream 1.710 + */ 1.711 +static void 1.712 +mount_enclosing_volume_finished (GObject *source_object, 1.713 + GAsyncResult *res, 1.714 + gpointer user_data) 1.715 +{ 1.716 + GError *error = nullptr; 1.717 + 1.718 + nsGIOInputStream* istream = static_cast<nsGIOInputStream*>(user_data); 1.719 + 1.720 + g_file_mount_enclosing_volume_finish(G_FILE (source_object), res, &error); 1.721 + 1.722 + if (error) { 1.723 + g_warning("Mount failed: %s %d", error->message, error->code); 1.724 + istream->SetMountResult(MOUNT_OPERATION_FAILED, error->code); 1.725 + g_error_free(error); 1.726 + } else { 1.727 + istream->SetMountResult(MOUNT_OPERATION_SUCCESS, 0); 1.728 + } 1.729 +} 1.730 + 1.731 +/** 1.732 + * This function is called when username or password are requested from user. 1.733 + * This function is called in main thread as async request from dbus. 1.734 + * @param mount_op mount operation 1.735 + * @param message message to show to user 1.736 + * @param default_user preffered user 1.737 + * @param default_domain domain name 1.738 + * @param flags what type of information is required 1.739 + * @param user_data nsIChannel 1.740 + */ 1.741 +static void 1.742 +mount_operation_ask_password (GMountOperation *mount_op, 1.743 + const char *message, 1.744 + const char *default_user, 1.745 + const char *default_domain, 1.746 + GAskPasswordFlags flags, 1.747 + gpointer user_data) 1.748 +{ 1.749 + nsIChannel *channel = (nsIChannel *) user_data; 1.750 + if (!channel) { 1.751 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); 1.752 + return; 1.753 + } 1.754 + // We can't handle request for domain 1.755 + if (flags & G_ASK_PASSWORD_NEED_DOMAIN) { 1.756 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); 1.757 + return; 1.758 + } 1.759 + 1.760 + nsCOMPtr<nsIAuthPrompt> prompt; 1.761 + NS_QueryNotificationCallbacks(channel, prompt); 1.762 + 1.763 + // If no auth prompt, then give up. We could failover to using the 1.764 + // WindowWatcher service, but that might defeat a consumer's purposeful 1.765 + // attempt to disable authentication (for whatever reason). 1.766 + if (!prompt) { 1.767 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); 1.768 + return; 1.769 + } 1.770 + // Parse out the host and port... 1.771 + nsCOMPtr<nsIURI> uri; 1.772 + channel->GetURI(getter_AddRefs(uri)); 1.773 + if (!uri) { 1.774 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); 1.775 + return; 1.776 + } 1.777 + 1.778 + nsAutoCString scheme, hostPort; 1.779 + uri->GetScheme(scheme); 1.780 + uri->GetHostPort(hostPort); 1.781 + 1.782 + // It doesn't make sense for either of these strings to be empty. What kind 1.783 + // of funky URI is this? 1.784 + if (scheme.IsEmpty() || hostPort.IsEmpty()) { 1.785 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); 1.786 + return; 1.787 + } 1.788 + // Construct the single signon key. Altering the value of this key will 1.789 + // cause people's remembered passwords to be forgotten. Think carefully 1.790 + // before changing the way this key is constructed. 1.791 + nsAutoString key, realm; 1.792 + 1.793 + NS_ConvertUTF8toUTF16 dispHost(scheme); 1.794 + dispHost.Append(NS_LITERAL_STRING("://")); 1.795 + dispHost.Append(NS_ConvertUTF8toUTF16(hostPort)); 1.796 + 1.797 + key = dispHost; 1.798 + if (*default_domain != '\0') 1.799 + { 1.800 + // We assume the realm string is ASCII. That might be a bogus assumption, 1.801 + // but we have no idea what encoding GnomeVFS is using, so for now we'll 1.802 + // limit ourselves to ISO-Latin-1. XXX What is a better solution? 1.803 + realm.Append('"'); 1.804 + realm.Append(NS_ConvertASCIItoUTF16(default_domain)); 1.805 + realm.Append('"'); 1.806 + key.Append(' '); 1.807 + key.Append(realm); 1.808 + } 1.809 + // Construct the message string... 1.810 + // 1.811 + // We use Necko's string bundle here. This code really should be encapsulated 1.812 + // behind some Necko API, after all this code is based closely on the code in 1.813 + // nsHttpChannel.cpp. 1.814 + nsCOMPtr<nsIStringBundleService> bundleSvc = 1.815 + do_GetService(NS_STRINGBUNDLE_CONTRACTID); 1.816 + if (!bundleSvc) { 1.817 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); 1.818 + return; 1.819 + } 1.820 + nsCOMPtr<nsIStringBundle> bundle; 1.821 + bundleSvc->CreateBundle("chrome://global/locale/commonDialogs.properties", 1.822 + getter_AddRefs(bundle)); 1.823 + if (!bundle) { 1.824 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); 1.825 + return; 1.826 + } 1.827 + nsAutoString nsmessage; 1.828 + 1.829 + if (flags & G_ASK_PASSWORD_NEED_PASSWORD) { 1.830 + if (flags & G_ASK_PASSWORD_NEED_USERNAME) { 1.831 + if (!realm.IsEmpty()) { 1.832 + const char16_t *strings[] = { realm.get(), dispHost.get() }; 1.833 + bundle->FormatStringFromName(MOZ_UTF16("EnterLoginForRealm"), 1.834 + strings, 2, getter_Copies(nsmessage)); 1.835 + } else { 1.836 + const char16_t *strings[] = { dispHost.get() }; 1.837 + bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordFor"), 1.838 + strings, 1, getter_Copies(nsmessage)); 1.839 + } 1.840 + } else { 1.841 + NS_ConvertUTF8toUTF16 userName(default_user); 1.842 + const char16_t *strings[] = { userName.get(), dispHost.get() }; 1.843 + bundle->FormatStringFromName(MOZ_UTF16("EnterPasswordFor"), 1.844 + strings, 2, getter_Copies(nsmessage)); 1.845 + } 1.846 + } else { 1.847 + g_warning("Unknown mount operation request (flags: %x)", flags); 1.848 + } 1.849 + 1.850 + if (nsmessage.IsEmpty()) { 1.851 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); 1.852 + return; 1.853 + } 1.854 + // Prompt the user... 1.855 + nsresult rv; 1.856 + bool retval = false; 1.857 + char16_t *user = nullptr, *pass = nullptr; 1.858 + if (default_user) { 1.859 + // user will be freed by PromptUsernameAndPassword 1.860 + user = ToNewUnicode(NS_ConvertUTF8toUTF16(default_user)); 1.861 + } 1.862 + if (flags & G_ASK_PASSWORD_NEED_USERNAME) { 1.863 + rv = prompt->PromptUsernameAndPassword(nullptr, nsmessage.get(), 1.864 + key.get(), 1.865 + nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, 1.866 + &user, &pass, &retval); 1.867 + } else { 1.868 + rv = prompt->PromptPassword(nullptr, nsmessage.get(), 1.869 + key.get(), 1.870 + nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, 1.871 + &pass, &retval); 1.872 + } 1.873 + if (NS_FAILED(rv) || !retval) { // was || user == '\0' || pass == '\0' 1.874 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED); 1.875 + return; 1.876 + } 1.877 + /* GIO should accept UTF8 */ 1.878 + g_mount_operation_set_username(mount_op, NS_ConvertUTF16toUTF8(user).get()); 1.879 + g_mount_operation_set_password(mount_op, NS_ConvertUTF16toUTF8(pass).get()); 1.880 + nsMemory::Free(user); 1.881 + nsMemory::Free(pass); 1.882 + g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_HANDLED); 1.883 +} 1.884 + 1.885 +//----------------------------------------------------------------------------- 1.886 + 1.887 +class nsGIOProtocolHandler MOZ_FINAL : public nsIProtocolHandler 1.888 + , public nsIObserver 1.889 +{ 1.890 + public: 1.891 + NS_DECL_ISUPPORTS 1.892 + NS_DECL_NSIPROTOCOLHANDLER 1.893 + NS_DECL_NSIOBSERVER 1.894 + 1.895 + nsresult Init(); 1.896 + 1.897 + private: 1.898 + void InitSupportedProtocolsPref(nsIPrefBranch *prefs); 1.899 + bool IsSupportedProtocol(const nsCString &spec); 1.900 + 1.901 + nsCString mSupportedProtocols; 1.902 +}; 1.903 + 1.904 +NS_IMPL_ISUPPORTS(nsGIOProtocolHandler, nsIProtocolHandler, nsIObserver) 1.905 + 1.906 +nsresult 1.907 +nsGIOProtocolHandler::Init() 1.908 +{ 1.909 +#ifdef PR_LOGGING 1.910 + sGIOLog = PR_NewLogModule("gio"); 1.911 +#endif 1.912 + 1.913 + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); 1.914 + if (prefs) 1.915 + { 1.916 + InitSupportedProtocolsPref(prefs); 1.917 + prefs->AddObserver(MOZ_GIO_SUPPORTED_PROTOCOLS, this, false); 1.918 + } 1.919 + 1.920 + return NS_OK; 1.921 +} 1.922 + 1.923 +void 1.924 +nsGIOProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch *prefs) 1.925 +{ 1.926 + // Get user preferences to determine which protocol is supported. 1.927 + // Gvfs/GIO has a set of supported protocols like obex, network, archive, 1.928 + // computer, dav, cdda, gphoto2, trash, etc. Some of these seems to be 1.929 + // irrelevant to process by browser. By default accept only smb and sftp 1.930 + // protocols so far. 1.931 + nsresult rv = prefs->GetCharPref(MOZ_GIO_SUPPORTED_PROTOCOLS, 1.932 + getter_Copies(mSupportedProtocols)); 1.933 + if (NS_SUCCEEDED(rv)) { 1.934 + mSupportedProtocols.StripWhitespace(); 1.935 + ToLowerCase(mSupportedProtocols); 1.936 + } 1.937 + else 1.938 + mSupportedProtocols.Assign("smb:,sftp:"); // use defaults 1.939 + 1.940 + LOG(("gio: supported protocols \"%s\"\n", mSupportedProtocols.get())); 1.941 +} 1.942 + 1.943 +bool 1.944 +nsGIOProtocolHandler::IsSupportedProtocol(const nsCString &aSpec) 1.945 +{ 1.946 + const char *specString = aSpec.get(); 1.947 + const char *colon = strchr(specString, ':'); 1.948 + if (!colon) 1.949 + return false; 1.950 + 1.951 + uint32_t length = colon - specString + 1; 1.952 + 1.953 + // <scheme> + ':' 1.954 + nsCString scheme(specString, length); 1.955 + 1.956 + char *found = PL_strcasestr(mSupportedProtocols.get(), scheme.get()); 1.957 + if (!found) 1.958 + return false; 1.959 + 1.960 + if (found[length] != ',' && found[length] != '\0') 1.961 + return false; 1.962 + 1.963 + return true; 1.964 +} 1.965 + 1.966 +NS_IMETHODIMP 1.967 +nsGIOProtocolHandler::GetScheme(nsACString &aScheme) 1.968 +{ 1.969 + aScheme.Assign(MOZ_GIO_SCHEME); 1.970 + return NS_OK; 1.971 +} 1.972 + 1.973 +NS_IMETHODIMP 1.974 +nsGIOProtocolHandler::GetDefaultPort(int32_t *aDefaultPort) 1.975 +{ 1.976 + *aDefaultPort = -1; 1.977 + return NS_OK; 1.978 +} 1.979 + 1.980 +NS_IMETHODIMP 1.981 +nsGIOProtocolHandler::GetProtocolFlags(uint32_t *aProtocolFlags) 1.982 +{ 1.983 + // Is URI_STD true of all GnomeVFS URI types? 1.984 + *aProtocolFlags = URI_STD | URI_DANGEROUS_TO_LOAD; 1.985 + return NS_OK; 1.986 +} 1.987 + 1.988 +NS_IMETHODIMP 1.989 +nsGIOProtocolHandler::NewURI(const nsACString &aSpec, 1.990 + const char *aOriginCharset, 1.991 + nsIURI *aBaseURI, 1.992 + nsIURI **aResult) 1.993 +{ 1.994 + const nsCString flatSpec(aSpec); 1.995 + LOG(("gio: NewURI [spec=%s]\n", flatSpec.get())); 1.996 + 1.997 + if (!aBaseURI) 1.998 + { 1.999 + // XXX Is it good to support all GIO protocols? 1.1000 + if (!IsSupportedProtocol(flatSpec)) 1.1001 + return NS_ERROR_UNKNOWN_PROTOCOL; 1.1002 + 1.1003 + int32_t colon_location = flatSpec.FindChar(':'); 1.1004 + if (colon_location <= 0) 1.1005 + return NS_ERROR_UNKNOWN_PROTOCOL; 1.1006 + 1.1007 + // Verify that GIO supports this URI scheme. 1.1008 + bool uri_scheme_supported = false; 1.1009 + 1.1010 + GVfs *gvfs = g_vfs_get_default(); 1.1011 + 1.1012 + if (!gvfs) { 1.1013 + g_warning("Cannot get GVfs object."); 1.1014 + return NS_ERROR_UNKNOWN_PROTOCOL; 1.1015 + } 1.1016 + 1.1017 + const gchar* const * uri_schemes = g_vfs_get_supported_uri_schemes(gvfs); 1.1018 + 1.1019 + while (*uri_schemes != nullptr) { 1.1020 + // While flatSpec ends with ':' the uri_scheme does not. Therefore do not 1.1021 + // compare last character. 1.1022 + if (StringHead(flatSpec, colon_location).Equals(*uri_schemes)) { 1.1023 + uri_scheme_supported = true; 1.1024 + break; 1.1025 + } 1.1026 + uri_schemes++; 1.1027 + } 1.1028 + 1.1029 + if (!uri_scheme_supported) { 1.1030 + return NS_ERROR_UNKNOWN_PROTOCOL; 1.1031 + } 1.1032 + } 1.1033 + 1.1034 + nsresult rv; 1.1035 + nsCOMPtr<nsIStandardURL> url = 1.1036 + do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv); 1.1037 + if (NS_FAILED(rv)) 1.1038 + return rv; 1.1039 + 1.1040 + rv = url->Init(nsIStandardURL::URLTYPE_STANDARD, -1, flatSpec, 1.1041 + aOriginCharset, aBaseURI); 1.1042 + if (NS_SUCCEEDED(rv)) 1.1043 + rv = CallQueryInterface(url, aResult); 1.1044 + return rv; 1.1045 + 1.1046 +} 1.1047 + 1.1048 +NS_IMETHODIMP 1.1049 +nsGIOProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **aResult) 1.1050 +{ 1.1051 + NS_ENSURE_ARG_POINTER(aURI); 1.1052 + nsresult rv; 1.1053 + 1.1054 + nsAutoCString spec; 1.1055 + rv = aURI->GetSpec(spec); 1.1056 + if (NS_FAILED(rv)) 1.1057 + return rv; 1.1058 + 1.1059 + nsRefPtr<nsGIOInputStream> stream = new nsGIOInputStream(spec); 1.1060 + if (!stream) 1.1061 + { 1.1062 + rv = NS_ERROR_OUT_OF_MEMORY; 1.1063 + } 1.1064 + else 1.1065 + { 1.1066 + // start out assuming an unknown content-type. we'll set the content-type 1.1067 + // to something better once we open the URI. 1.1068 + rv = NS_NewInputStreamChannel(aResult, 1.1069 + aURI, 1.1070 + stream, 1.1071 + NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE)); 1.1072 + if (NS_SUCCEEDED(rv)) 1.1073 + stream->SetChannel(*aResult); 1.1074 + } 1.1075 + return rv; 1.1076 +} 1.1077 + 1.1078 +NS_IMETHODIMP 1.1079 +nsGIOProtocolHandler::AllowPort(int32_t aPort, 1.1080 + const char *aScheme, 1.1081 + bool *aResult) 1.1082 +{ 1.1083 + // Don't override anything. 1.1084 + *aResult = false; 1.1085 + return NS_OK; 1.1086 +} 1.1087 + 1.1088 +NS_IMETHODIMP 1.1089 +nsGIOProtocolHandler::Observe(nsISupports *aSubject, 1.1090 + const char *aTopic, 1.1091 + const char16_t *aData) 1.1092 +{ 1.1093 + if (strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { 1.1094 + nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject); 1.1095 + InitSupportedProtocolsPref(prefs); 1.1096 + } 1.1097 + return NS_OK; 1.1098 +} 1.1099 + 1.1100 +//----------------------------------------------------------------------------- 1.1101 + 1.1102 +#define NS_GIOPROTOCOLHANDLER_CID \ 1.1103 +{ /* ee706783-3af8-4d19-9e84-e2ebfe213480 */ \ 1.1104 + 0xee706783, \ 1.1105 + 0x3af8, \ 1.1106 + 0x4d19, \ 1.1107 + {0x9e, 0x84, 0xe2, 0xeb, 0xfe, 0x21, 0x34, 0x80} \ 1.1108 +} 1.1109 + 1.1110 +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGIOProtocolHandler, Init) 1.1111 +NS_DEFINE_NAMED_CID(NS_GIOPROTOCOLHANDLER_CID); 1.1112 + 1.1113 +static const mozilla::Module::CIDEntry kVFSCIDs[] = { 1.1114 + { &kNS_GIOPROTOCOLHANDLER_CID, false, nullptr, nsGIOProtocolHandlerConstructor }, 1.1115 + { nullptr } 1.1116 +}; 1.1117 + 1.1118 +static const mozilla::Module::ContractIDEntry kVFSContracts[] = { 1.1119 + { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MOZ_GIO_SCHEME, &kNS_GIOPROTOCOLHANDLER_CID }, 1.1120 + { nullptr } 1.1121 +}; 1.1122 + 1.1123 +static const mozilla::Module kVFSModule = { 1.1124 + mozilla::Module::kVersion, 1.1125 + kVFSCIDs, 1.1126 + kVFSContracts 1.1127 +}; 1.1128 + 1.1129 +NSMODULE_DEFN(nsGIOModule) = &kVFSModule;