summaryrefslogtreecommitdiff
path: root/netwerk/protocol/file
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/file')
-rw-r--r--netwerk/protocol/file/moz.build30
-rw-r--r--netwerk/protocol/file/nsFileChannel.cpp486
-rw-r--r--netwerk/protocol/file/nsFileChannel.h45
-rw-r--r--netwerk/protocol/file/nsFileProtocolHandler.cpp274
-rw-r--r--netwerk/protocol/file/nsFileProtocolHandler.h27
-rw-r--r--netwerk/protocol/file/nsIFileChannel.idl17
-rw-r--r--netwerk/protocol/file/nsIFileProtocolHandler.idl65
7 files changed, 944 insertions, 0 deletions
diff --git a/netwerk/protocol/file/moz.build b/netwerk/protocol/file/moz.build
new file mode 100644
index 0000000000..223ff2f2bd
--- /dev/null
+++ b/netwerk/protocol/file/moz.build
@@ -0,0 +1,30 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.net += [
+ 'nsFileProtocolHandler.h',
+]
+
+XPIDL_SOURCES += [
+ 'nsIFileChannel.idl',
+ 'nsIFileProtocolHandler.idl',
+]
+
+XPIDL_MODULE = 'necko_file'
+
+UNIFIED_SOURCES += [
+ 'nsFileChannel.cpp',
+ 'nsFileProtocolHandler.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '/netwerk/base',
+]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/netwerk/protocol/file/nsFileChannel.cpp b/netwerk/protocol/file/nsFileChannel.cpp
new file mode 100644
index 0000000000..0400aaec01
--- /dev/null
+++ b/netwerk/protocol/file/nsFileChannel.cpp
@@ -0,0 +1,486 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cin: */
+/* 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/. */
+
+#include "nsIOService.h"
+#include "nsFileChannel.h"
+#include "nsBaseContentStream.h"
+#include "nsDirectoryIndexStream.h"
+#include "nsThreadUtils.h"
+#include "nsTransportUtils.h"
+#include "nsStreamUtils.h"
+#include "nsMimeTypes.h"
+#include "nsNetUtil.h"
+#include "nsNetCID.h"
+#include "nsIOutputStream.h"
+#include "nsIFileStreams.h"
+#include "nsFileProtocolHandler.h"
+#include "nsProxyRelease.h"
+#include "nsAutoPtr.h"
+#include "nsIContentPolicy.h"
+#include "nsContentUtils.h"
+
+#include "nsIFileURL.h"
+#include "nsIFile.h"
+#include "nsIMIMEService.h"
+#include "prio.h"
+#include <algorithm>
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+//-----------------------------------------------------------------------------
+
+class nsFileCopyEvent : public Runnable {
+public:
+ nsFileCopyEvent(nsIOutputStream *dest, nsIInputStream *source, int64_t len)
+ : mDest(dest)
+ , mSource(source)
+ , mLen(len)
+ , mStatus(NS_OK)
+ , mInterruptStatus(NS_OK) {
+ }
+
+ // Read the current status of the file copy operation.
+ nsresult Status() { return mStatus; }
+
+ // Call this method to perform the file copy synchronously.
+ void DoCopy();
+
+ // Call this method to perform the file copy on a background thread. The
+ // callback is dispatched when the file copy completes.
+ nsresult Dispatch(nsIRunnable *callback,
+ nsITransportEventSink *sink,
+ nsIEventTarget *target);
+
+ // Call this method to interrupt a file copy operation that is occuring on
+ // a background thread. The status parameter passed to this function must
+ // be a failure code and is set as the status of this file copy operation.
+ void Interrupt(nsresult status) {
+ NS_ASSERTION(NS_FAILED(status), "must be a failure code");
+ mInterruptStatus = status;
+ }
+
+ NS_IMETHOD Run() override {
+ DoCopy();
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIEventTarget> mCallbackTarget;
+ nsCOMPtr<nsIRunnable> mCallback;
+ nsCOMPtr<nsITransportEventSink> mSink;
+ nsCOMPtr<nsIOutputStream> mDest;
+ nsCOMPtr<nsIInputStream> mSource;
+ int64_t mLen;
+ nsresult mStatus; // modified on i/o thread only
+ nsresult mInterruptStatus; // modified on main thread only
+};
+
+void
+nsFileCopyEvent::DoCopy()
+{
+ // We'll copy in chunks this large by default. This size affects how
+ // frequently we'll check for interrupts.
+ const int32_t chunk = nsIOService::gDefaultSegmentSize * nsIOService::gDefaultSegmentCount;
+
+ nsresult rv = NS_OK;
+
+ int64_t len = mLen, progress = 0;
+ while (len) {
+ // If we've been interrupted, then stop copying.
+ rv = mInterruptStatus;
+ if (NS_FAILED(rv))
+ break;
+
+ int32_t num = std::min((int32_t) len, chunk);
+
+ uint32_t result;
+ rv = mSource->ReadSegments(NS_CopySegmentToStream, mDest, num, &result);
+ if (NS_FAILED(rv))
+ break;
+ if (result != (uint32_t) num) {
+ rv = NS_ERROR_FILE_DISK_FULL; // stopped prematurely (out of disk space)
+ break;
+ }
+
+ // Dispatch progress notification
+ if (mSink) {
+ progress += num;
+ mSink->OnTransportStatus(nullptr, NS_NET_STATUS_WRITING, progress,
+ mLen);
+ }
+
+ len -= num;
+ }
+
+ if (NS_FAILED(rv))
+ mStatus = rv;
+
+ // Close the output stream before notifying our callback so that others may
+ // freely "play" with the file.
+ mDest->Close();
+
+ // Notify completion
+ if (mCallback) {
+ mCallbackTarget->Dispatch(mCallback, NS_DISPATCH_NORMAL);
+
+ // Release the callback on the target thread to avoid destroying stuff on
+ // the wrong thread.
+ NS_ProxyRelease(mCallbackTarget, mCallback.forget());
+ }
+}
+
+nsresult
+nsFileCopyEvent::Dispatch(nsIRunnable *callback,
+ nsITransportEventSink *sink,
+ nsIEventTarget *target)
+{
+ // Use the supplied event target for all asynchronous operations.
+
+ mCallback = callback;
+ mCallbackTarget = target;
+
+ // Build a coalescing proxy for progress events
+ nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(mSink), sink, target);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Dispatch ourselves to I/O thread pool...
+ nsCOMPtr<nsIEventTarget> pool =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return pool->Dispatch(this, NS_DISPATCH_NORMAL);
+}
+
+//-----------------------------------------------------------------------------
+
+// This is a dummy input stream that when read, performs the file copy. The
+// copy happens on a background thread via mCopyEvent.
+
+class nsFileUploadContentStream : public nsBaseContentStream {
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsFileUploadContentStream(bool nonBlocking,
+ nsIOutputStream *dest,
+ nsIInputStream *source,
+ int64_t len,
+ nsITransportEventSink *sink)
+ : nsBaseContentStream(nonBlocking)
+ , mCopyEvent(new nsFileCopyEvent(dest, source, len))
+ , mSink(sink) {
+ }
+
+ bool IsInitialized() {
+ return mCopyEvent != nullptr;
+ }
+
+ NS_IMETHOD ReadSegments(nsWriteSegmentFun fun, void *closure,
+ uint32_t count, uint32_t *result) override;
+ NS_IMETHOD AsyncWait(nsIInputStreamCallback *callback, uint32_t flags,
+ uint32_t count, nsIEventTarget *target) override;
+
+private:
+ virtual ~nsFileUploadContentStream() {}
+
+ void OnCopyComplete();
+
+ RefPtr<nsFileCopyEvent> mCopyEvent;
+ nsCOMPtr<nsITransportEventSink> mSink;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsFileUploadContentStream,
+ nsBaseContentStream)
+
+NS_IMETHODIMP
+nsFileUploadContentStream::ReadSegments(nsWriteSegmentFun fun, void *closure,
+ uint32_t count, uint32_t *result)
+{
+ *result = 0; // nothing is ever actually read from this stream
+
+ if (IsClosed())
+ return NS_OK;
+
+ if (IsNonBlocking()) {
+ // Inform the caller that they will have to wait for the copy operation to
+ // complete asynchronously. We'll kick of the copy operation once they
+ // call AsyncWait.
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ }
+
+ // Perform copy synchronously, and then close out the stream.
+ mCopyEvent->DoCopy();
+ nsresult status = mCopyEvent->Status();
+ CloseWithStatus(NS_FAILED(status) ? status : NS_BASE_STREAM_CLOSED);
+ return status;
+}
+
+NS_IMETHODIMP
+nsFileUploadContentStream::AsyncWait(nsIInputStreamCallback *callback,
+ uint32_t flags, uint32_t count,
+ nsIEventTarget *target)
+{
+ nsresult rv = nsBaseContentStream::AsyncWait(callback, flags, count, target);
+ if (NS_FAILED(rv) || IsClosed())
+ return rv;
+
+ if (IsNonBlocking()) {
+ nsCOMPtr<nsIRunnable> callback =
+ NewRunnableMethod(this, &nsFileUploadContentStream::OnCopyComplete);
+ mCopyEvent->Dispatch(callback, mSink, target);
+ }
+
+ return NS_OK;
+}
+
+void
+nsFileUploadContentStream::OnCopyComplete()
+{
+ // This method is being called to indicate that we are done copying.
+ nsresult status = mCopyEvent->Status();
+
+ CloseWithStatus(NS_FAILED(status) ? status : NS_BASE_STREAM_CLOSED);
+}
+
+//-----------------------------------------------------------------------------
+
+nsFileChannel::nsFileChannel(nsIURI *uri)
+{
+ // If we have a link file, we should resolve its target right away.
+ // This is to protect against a same origin attack where the same link file
+ // can point to different resources right after the first resource is loaded.
+ nsCOMPtr<nsIFile> file;
+ nsCOMPtr <nsIURI> targetURI;
+ nsAutoCString fileTarget;
+ nsCOMPtr<nsIFile> resolvedFile;
+ bool symLink;
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
+ if (fileURL &&
+ NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
+ NS_SUCCEEDED(file->IsSymlink(&symLink)) &&
+ symLink &&
+ NS_SUCCEEDED(file->GetNativeTarget(fileTarget)) &&
+ NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, PR_TRUE,
+ getter_AddRefs(resolvedFile))) &&
+ NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI),
+ resolvedFile, nullptr))) {
+ // Make an effort to match up the query strings.
+ nsCOMPtr<nsIURL> origURL = do_QueryInterface(uri);
+ nsCOMPtr<nsIURL> targetURL = do_QueryInterface(targetURI);
+ nsAutoCString queryString;
+ if (origURL && targetURL && NS_SUCCEEDED(origURL->GetQuery(queryString))) {
+ targetURL->SetQuery(queryString);
+ }
+
+ SetURI(targetURI);
+ SetOriginalURI(uri);
+ nsLoadFlags loadFlags = 0;
+ GetLoadFlags(&loadFlags);
+ SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
+ } else {
+ SetURI(uri);
+ }
+}
+
+nsFileChannel::~nsFileChannel()
+{
+}
+
+nsresult
+nsFileChannel::MakeFileInputStream(nsIFile *file,
+ nsCOMPtr<nsIInputStream> &stream,
+ nsCString &contentType,
+ bool async)
+{
+ // we accept that this might result in a disk hit to stat the file
+ bool isDir;
+ nsresult rv = file->IsDirectory(&isDir);
+ if (NS_FAILED(rv)) {
+ // canonicalize error message
+ if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
+ rv = NS_ERROR_FILE_NOT_FOUND;
+
+ if (async && (NS_ERROR_FILE_NOT_FOUND == rv)) {
+ // We don't return "Not Found" errors here. Since we could not find
+ // the file, it's not a directory anyway.
+ isDir = false;
+ } else {
+ return rv;
+ }
+ }
+
+ if (isDir) {
+ rv = nsDirectoryIndexStream::Create(file, getter_AddRefs(stream));
+ if (NS_SUCCEEDED(rv) && !HasContentTypeHint())
+ contentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
+ } else {
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file, -1, -1,
+ async? nsIFileInputStream::DEFER_OPEN : 0);
+ if (NS_SUCCEEDED(rv) && !HasContentTypeHint()) {
+ // Use file extension to infer content type
+ nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv);
+ if (NS_SUCCEEDED(rv)) {
+ mime->GetTypeFromFile(file, contentType);
+ }
+ }
+ }
+ return rv;
+}
+
+nsresult
+nsFileChannel::OpenContentStream(bool async, nsIInputStream **result,
+ nsIChannel** channel)
+{
+ // NOTE: the resulting file is a clone, so it is safe to pass it to the
+ // file input stream which will be read on a background thread.
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = GetFile(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIFileProtocolHandler> fileHandler;
+ rv = NS_GetFileProtocolHandler(getter_AddRefs(fileHandler));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIURI> newURI;
+ rv = fileHandler->ReadURLFile(file, getter_AddRefs(newURI));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIChannel> newChannel;
+ rv = NS_NewChannel(getter_AddRefs(newChannel),
+ newURI,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ *result = nullptr;
+ newChannel.forget(channel);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIInputStream> stream;
+
+ if (mUploadStream) {
+ // Pass back a nsFileUploadContentStream instance that knows how to perform
+ // the file copy when "read" (the resulting stream in this case does not
+ // actually return any data).
+
+ nsCOMPtr<nsIOutputStream> fileStream;
+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileStream), file,
+ PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
+ PR_IRUSR | PR_IWUSR);
+ if (NS_FAILED(rv))
+ return rv;
+
+ RefPtr<nsFileUploadContentStream> uploadStream =
+ new nsFileUploadContentStream(async, fileStream, mUploadStream,
+ mUploadLength, this);
+ if (!uploadStream || !uploadStream->IsInitialized()) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ stream = uploadStream.forget();
+
+ mContentLength = 0;
+
+ // Since there isn't any content to speak of we just set the content-type
+ // to something other than "unknown" to avoid triggering the content-type
+ // sniffer code in nsBaseChannel.
+ // However, don't override explicitly set types.
+ if (!HasContentTypeHint())
+ SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
+ } else {
+ nsAutoCString contentType;
+ rv = MakeFileInputStream(file, stream, contentType, async);
+ if (NS_FAILED(rv))
+ return rv;
+
+ EnableSynthesizedProgressEvents(true);
+
+ // fixup content length and type
+ if (mContentLength < 0) {
+ int64_t size;
+ rv = file->GetFileSize(&size);
+ if (NS_FAILED(rv)) {
+ if (async &&
+ (NS_ERROR_FILE_NOT_FOUND == rv ||
+ NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv)) {
+ size = 0;
+ } else {
+ return rv;
+ }
+ }
+ mContentLength = size;
+ }
+ if (!contentType.IsEmpty())
+ SetContentType(contentType);
+ }
+
+ *result = nullptr;
+ stream.swap(*result);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsFileChannel::nsISupports
+
+NS_IMPL_ISUPPORTS_INHERITED(nsFileChannel,
+ nsBaseChannel,
+ nsIUploadChannel,
+ nsIFileChannel)
+
+//-----------------------------------------------------------------------------
+// nsFileChannel::nsIFileChannel
+
+NS_IMETHODIMP
+nsFileChannel::GetFile(nsIFile **file)
+{
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(URI());
+ NS_ENSURE_STATE(fileURL);
+
+ // This returns a cloned nsIFile
+ return fileURL->GetFile(file);
+}
+
+//-----------------------------------------------------------------------------
+// nsFileChannel::nsIUploadChannel
+
+NS_IMETHODIMP
+nsFileChannel::SetUploadStream(nsIInputStream *stream,
+ const nsACString &contentType,
+ int64_t contentLength)
+{
+ NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS);
+
+ if ((mUploadStream = stream)) {
+ mUploadLength = contentLength;
+ if (mUploadLength < 0) {
+ // Make sure we know how much data we are uploading.
+ uint64_t avail;
+ nsresult rv = mUploadStream->Available(&avail);
+ if (NS_FAILED(rv))
+ return rv;
+ // if this doesn't fit in the javascript MAX_SAFE_INTEGER
+ // pretend we don't know the size
+ mUploadLength = InScriptableRange(avail) ? avail : -1;
+ }
+ } else {
+ mUploadLength = -1;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFileChannel::GetUploadStream(nsIInputStream **result)
+{
+ NS_IF_ADDREF(*result = mUploadStream);
+ return NS_OK;
+}
diff --git a/netwerk/protocol/file/nsFileChannel.h b/netwerk/protocol/file/nsFileChannel.h
new file mode 100644
index 0000000000..2f1f71ba3f
--- /dev/null
+++ b/netwerk/protocol/file/nsFileChannel.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cin: */
+/* 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/. */
+
+#ifndef nsFileChannel_h__
+#define nsFileChannel_h__
+
+#include "nsBaseChannel.h"
+#include "nsIFileChannel.h"
+#include "nsIUploadChannel.h"
+
+class nsFileChannel : public nsBaseChannel
+ , public nsIFileChannel
+ , public nsIUploadChannel
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIFILECHANNEL
+ NS_DECL_NSIUPLOADCHANNEL
+
+ explicit nsFileChannel(nsIURI *uri);
+
+protected:
+ ~nsFileChannel();
+
+ // Called to construct a blocking file input stream for the given file. This
+ // method also returns a best guess at the content-type for the data stream.
+ // NOTE: If the channel has a type hint set, contentType will be left
+ // untouched. The caller should not use it in that case.
+ MOZ_MUST_USE nsresult MakeFileInputStream(nsIFile *file,
+ nsCOMPtr<nsIInputStream> &stream,
+ nsCString &contentType, bool async);
+
+ virtual MOZ_MUST_USE nsresult OpenContentStream(bool async,
+ nsIInputStream **result,
+ nsIChannel** channel) override;
+
+private:
+ nsCOMPtr<nsIInputStream> mUploadStream;
+ int64_t mUploadLength;
+};
+
+#endif // !nsFileChannel_h__
diff --git a/netwerk/protocol/file/nsFileProtocolHandler.cpp b/netwerk/protocol/file/nsFileProtocolHandler.cpp
new file mode 100644
index 0000000000..e55cb9d474
--- /dev/null
+++ b/netwerk/protocol/file/nsFileProtocolHandler.cpp
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+// vim:ts=4 sw=4 sts=4 et cin:
+/* 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/. */
+
+#include "nsIFile.h"
+#include "nsFileProtocolHandler.h"
+#include "nsFileChannel.h"
+#include "nsStandardURL.h"
+#include "nsURLHelper.h"
+
+#include "nsNetUtil.h"
+
+// URL file handling, copied and modified from xpfe/components/bookmarks/src/nsBookmarksService.cpp
+#ifdef XP_WIN
+#include <shlobj.h>
+#include <intshcut.h>
+#include "nsIFileURL.h"
+#ifdef CompareString
+#undef CompareString
+#endif
+#endif
+
+// URL file handling for freedesktop.org
+#ifdef XP_UNIX
+#include "nsINIParser.h"
+#define DESKTOP_ENTRY_SECTION "Desktop Entry"
+#endif
+
+//-----------------------------------------------------------------------------
+
+nsFileProtocolHandler::nsFileProtocolHandler()
+{
+}
+
+nsresult
+nsFileProtocolHandler::Init()
+{
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsFileProtocolHandler,
+ nsIFileProtocolHandler,
+ nsIProtocolHandler,
+ nsISupportsWeakReference)
+
+//-----------------------------------------------------------------------------
+// nsIProtocolHandler methods:
+
+#if defined(XP_WIN)
+NS_IMETHODIMP
+nsFileProtocolHandler::ReadURLFile(nsIFile* aFile, nsIURI** aURI)
+{
+ nsAutoString path;
+ nsresult rv = aFile->GetPath(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (path.Length() < 4)
+ return NS_ERROR_NOT_AVAILABLE;
+ if (!StringTail(path, 4).LowerCaseEqualsLiteral(".url"))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ HRESULT result;
+
+ rv = NS_ERROR_NOT_AVAILABLE;
+
+ IUniformResourceLocatorW* urlLink = nullptr;
+ result = ::CoCreateInstance(CLSID_InternetShortcut, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IUniformResourceLocatorW, (void**)&urlLink);
+ if (SUCCEEDED(result) && urlLink) {
+ IPersistFile* urlFile = nullptr;
+ result = urlLink->QueryInterface(IID_IPersistFile, (void**)&urlFile);
+ if (SUCCEEDED(result) && urlFile) {
+ result = urlFile->Load(path.get(), STGM_READ);
+ if (SUCCEEDED(result) ) {
+ LPWSTR lpTemp = nullptr;
+
+ // The URL this method will give us back seems to be already
+ // escaped. Hence, do not do escaping of our own.
+ result = urlLink->GetURL(&lpTemp);
+ if (SUCCEEDED(result) && lpTemp) {
+ rv = NS_NewURI(aURI, nsDependentString(lpTemp));
+ // free the string that GetURL alloc'd
+ CoTaskMemFree(lpTemp);
+ }
+ }
+ urlFile->Release();
+ }
+ urlLink->Release();
+ }
+ return rv;
+}
+
+#elif defined(XP_UNIX)
+NS_IMETHODIMP
+nsFileProtocolHandler::ReadURLFile(nsIFile* aFile, nsIURI** aURI)
+{
+ // We only support desktop files that end in ".desktop" like the spec says:
+ // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html
+ nsAutoCString leafName;
+ nsresult rv = aFile->GetNativeLeafName(leafName);
+ if (NS_FAILED(rv) ||
+ !StringEndsWith(leafName, NS_LITERAL_CSTRING(".desktop")))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ bool isFile = false;
+ rv = aFile->IsFile(&isFile);
+ if (NS_FAILED(rv) || !isFile) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsINIParser parser;
+ rv = parser.Init(aFile);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString type;
+ parser.GetString(DESKTOP_ENTRY_SECTION, "Type", type);
+ if (!type.EqualsLiteral("Link"))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ nsAutoCString url;
+ rv = parser.GetString(DESKTOP_ENTRY_SECTION, "URL", url);
+ if (NS_FAILED(rv) || url.IsEmpty())
+ return NS_ERROR_NOT_AVAILABLE;
+
+ return NS_NewURI(aURI, url);
+}
+
+#else // other platforms
+NS_IMETHODIMP
+nsFileProtocolHandler::ReadURLFile(nsIFile* aFile, nsIURI** aURI)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+#endif // ReadURLFile()
+
+NS_IMETHODIMP
+nsFileProtocolHandler::GetScheme(nsACString &result)
+{
+ result.AssignLiteral("file");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::GetDefaultPort(int32_t *result)
+{
+ *result = -1; // no port for file: URLs
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::GetProtocolFlags(uint32_t *result)
+{
+ *result = URI_NOAUTH | URI_IS_LOCAL_FILE | URI_IS_LOCAL_RESOURCE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::NewURI(const nsACString &spec,
+ const char *charset,
+ nsIURI *baseURI,
+ nsIURI **result)
+{
+ nsCOMPtr<nsIStandardURL> url = new nsStandardURL(true);
+ if (!url)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ const nsACString *specPtr = &spec;
+
+#if defined(XP_WIN)
+ nsAutoCString buf;
+ if (net_NormalizeFileURL(spec, buf))
+ specPtr = &buf;
+#endif
+
+ nsresult rv = url->Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1,
+ *specPtr, charset, baseURI);
+ if (NS_FAILED(rv)) return rv;
+
+ return CallQueryInterface(url, result);
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::NewChannel2(nsIURI* uri,
+ nsILoadInfo* aLoadInfo,
+ nsIChannel** result)
+{
+ nsFileChannel *chan = new nsFileChannel(uri);
+ if (!chan)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(chan);
+
+ nsresult rv = chan->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(chan);
+ return rv;
+ }
+
+ // set the loadInfo on the new channel
+ rv = chan->SetLoadInfo(aLoadInfo);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(chan);
+ return rv;
+ }
+
+ *result = chan;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::NewChannel(nsIURI *uri, nsIChannel **result)
+{
+ return NewChannel2(uri, nullptr, result);
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *result)
+{
+ // don't override anything.
+ *result = false;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsIFileProtocolHandler methods:
+
+NS_IMETHODIMP
+nsFileProtocolHandler::NewFileURI(nsIFile *file, nsIURI **result)
+{
+ NS_ENSURE_ARG_POINTER(file);
+ nsresult rv;
+
+ nsCOMPtr<nsIFileURL> url = new nsStandardURL(true);
+ if (!url)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // NOTE: the origin charset is assigned the value of the platform
+ // charset by the SetFile method.
+ rv = url->SetFile(file);
+ if (NS_FAILED(rv)) return rv;
+
+ return CallQueryInterface(url, result);
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::GetURLSpecFromFile(nsIFile *file, nsACString &result)
+{
+ NS_ENSURE_ARG_POINTER(file);
+ return net_GetURLSpecFromFile(file, result);
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::GetURLSpecFromActualFile(nsIFile *file,
+ nsACString &result)
+{
+ NS_ENSURE_ARG_POINTER(file);
+ return net_GetURLSpecFromActualFile(file, result);
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::GetURLSpecFromDir(nsIFile *file, nsACString &result)
+{
+ NS_ENSURE_ARG_POINTER(file);
+ return net_GetURLSpecFromDir(file, result);
+}
+
+NS_IMETHODIMP
+nsFileProtocolHandler::GetFileFromURLSpec(const nsACString &spec, nsIFile **result)
+{
+ return net_GetFileFromURLSpec(spec, result);
+}
diff --git a/netwerk/protocol/file/nsFileProtocolHandler.h b/netwerk/protocol/file/nsFileProtocolHandler.h
new file mode 100644
index 0000000000..211eb2873e
--- /dev/null
+++ b/netwerk/protocol/file/nsFileProtocolHandler.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef nsFileProtocolHandler_h__
+#define nsFileProtocolHandler_h__
+
+#include "nsIFileProtocolHandler.h"
+#include "nsWeakReference.h"
+
+class nsFileProtocolHandler : public nsIFileProtocolHandler
+ , public nsSupportsWeakReference
+{
+ virtual ~nsFileProtocolHandler() {}
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIPROTOCOLHANDLER
+ NS_DECL_NSIFILEPROTOCOLHANDLER
+
+ nsFileProtocolHandler();
+
+ MOZ_MUST_USE nsresult Init();
+};
+
+#endif // !nsFileProtocolHandler_h__
diff --git a/netwerk/protocol/file/nsIFileChannel.idl b/netwerk/protocol/file/nsIFileChannel.idl
new file mode 100644
index 0000000000..e5fcdecb4c
--- /dev/null
+++ b/netwerk/protocol/file/nsIFileChannel.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+
+/**
+ * nsIFileChannel
+ */
+[scriptable, uuid(06169120-136d-45a5-b535-498f1f755ab7)]
+interface nsIFileChannel : nsISupports
+{
+ readonly attribute nsIFile file;
+};
diff --git a/netwerk/protocol/file/nsIFileProtocolHandler.idl b/netwerk/protocol/file/nsIFileProtocolHandler.idl
new file mode 100644
index 0000000000..c39d54f518
--- /dev/null
+++ b/netwerk/protocol/file/nsIFileProtocolHandler.idl
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#include "nsIProtocolHandler.idl"
+
+interface nsIFile;
+
+[scriptable, uuid(1fb25bd5-4354-4dcd-8d97-621b7b3ed2e4)]
+interface nsIFileProtocolHandler : nsIProtocolHandler
+{
+ /**
+ * This method constructs a new file URI
+ *
+ * @param aFile nsIFile
+ * @return reference to a new nsIURI object
+ */
+ nsIURI newFileURI(in nsIFile aFile);
+
+ /**
+ * Converts the nsIFile to the corresponding URL string. NOTE: under
+ * some platforms this is a lossy conversion (e.g., Mac Carbon build).
+ * If the nsIFile is a local file, then the result will be a file://
+ * URL string.
+ *
+ * The resulting string may contain URL-escaped characters.
+ * NOTE: Callers should use getURLSpecFromActualFile or
+ * getURLSpecFromDirFile if possible, for performance reasons.
+ */
+ AUTF8String getURLSpecFromFile(in nsIFile file);
+
+ /**
+ * Converts the nsIFile to the corresponding URL string. Should
+ * only be called on files which are not directories. Otherwise
+ * identical to getURLSpecFromFile, but is usually more efficient.
+ * WARNING: This restriction may not be enforced at runtime!
+ */
+ AUTF8String getURLSpecFromActualFile(in nsIFile file);
+
+ /**
+ * Converts the nsIFile to the corresponding URL string. Should
+ * only be called on files which are directories. Otherwise
+ * identical to getURLSpecFromFile, but is usually more efficient.
+ * WARNING: This restriction may not be enforced at runtime!
+ */
+ AUTF8String getURLSpecFromDir(in nsIFile file);
+
+ /**
+ * Converts the URL string into the corresponding nsIFile if possible.
+ * A local file will be created if the URL string begins with file://.
+ */
+ nsIFile getFileFromURLSpec(in AUTF8String url);
+
+ /**
+ * Takes a local file and tries to interpret it as an internet shortcut
+ * (e.g. .url files on windows).
+ * @param file The local file to read
+ * @return The URI the file refers to
+ *
+ * @throw NS_ERROR_NOT_AVAILABLE if the OS does not support such files.
+ * @throw NS_ERROR_NOT_AVAILABLE if this file is not an internet shortcut.
+ */
+ nsIURI readURLFile(in nsIFile file);
+};