diff --git a/modules/libjar/nsIJARChannel.idl b/modules/libjar/nsIJARChannel.idl index 4348717d277c..29e9cd933339 100644 --- a/modules/libjar/nsIJARChannel.idl +++ b/modules/libjar/nsIJARChannel.idl @@ -21,8 +21,11 @@ interface nsIJARChannel : nsIChannel /** * Returns the JAR file. May be null if the jar is remote. + * Setting the JAR file is optional and overrides the JAR + * file used for local file JARs. Setting the JAR file after + * the channel has been opened is not permitted. */ - readonly attribute nsIFile jarFile; + attribute nsIFile jarFile; /** * Returns the zip entry if the file is synchronously accessible. diff --git a/modules/libjar/nsJARChannel.cpp b/modules/libjar/nsJARChannel.cpp index 924a89d55266..c13a3b99997c 100644 --- a/modules/libjar/nsJARChannel.cpp +++ b/modules/libjar/nsJARChannel.cpp @@ -346,6 +346,12 @@ nsJARChannel::LookupFile(bool aAllowAsync) // have e.g. spaces in their filenames. NS_UnescapeURL(mJarEntry); + if (mJarFileOverride) { + mJarFile = mJarFileOverride; + LOG(("nsJARChannel::LookupFile [this=%p] Overriding mJarFile\n", this)); + return NS_OK; + } + // try to get a nsIFile directly from the url, which will often succeed. { nsCOMPtr fileURL = do_QueryInterface(mJarBaseURI); @@ -884,6 +890,17 @@ nsJARChannel::GetJarFile(nsIFile **aFile) return NS_OK; } +NS_IMETHODIMP +nsJARChannel::SetJarFile(nsIFile *aFile) +{ + if (mOpened) { + return NS_ERROR_IN_PROGRESS; + } + mJarFileOverride = aFile; + return NS_OK; +} + + NS_IMETHODIMP nsJARChannel::GetZipEntry(nsIZipEntry **aZipEntry) { diff --git a/modules/libjar/nsJARChannel.h b/modules/libjar/nsJARChannel.h index 5328d586ab39..ace2369c2f7a 100644 --- a/modules/libjar/nsJARChannel.h +++ b/modules/libjar/nsJARChannel.h @@ -51,6 +51,8 @@ class nsJARChannel final : public nsIJARChannel nsresult Init(nsIURI *uri); + void SetFile(nsIFile *file); + private: virtual ~nsJARChannel(); @@ -98,6 +100,7 @@ class nsJARChannel final : public nsIJARChannel // to the request if we get called back via RetargetDeliveryTo. nsCOMPtr mRequest; nsCOMPtr mJarFile; + nsCOMPtr mJarFileOverride; nsCOMPtr mJarBaseURI; nsCString mJarEntry; nsCString mInnerJarEntry; diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 811f30cd46d9..993ce8979e22 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4874,6 +4874,8 @@ pref("extensions.webextensions.identity.redirectDomain", "extensions.allizom.org pref("extensions.webextensions.themes.enabled", false); pref("extensions.webextensions.themes.icons.enabled", false); pref("extensions.webextensions.remote", false); +// Whether or not the moz-extension resource loads are remoted +pref("extensions.webextensions.protocol.remote", true); pref("layers.popups.compositing.enabled", false); diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index 4bfca2580767..e7ca61f36494 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -310,7 +310,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NamedPipeService, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsResProtocolHandler, Init) namespace mozilla { -NS_GENERIC_FACTORY_CONSTRUCTOR(ExtensionProtocolHandler) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ExtensionProtocolHandler, + ExtensionProtocolHandler::GetSingleton) NS_GENERIC_FACTORY_CONSTRUCTOR(SubstitutingURL) } // namespace mozilla #endif diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index d19b741178af..de12d0a3678b 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -8,6 +8,8 @@ #include "necko-config.h" #include "nsHttp.h" #include "mozilla/BasePrincipal.h" +#include "mozilla/ipc/IPCStreamUtils.h" +#include "mozilla/net/ExtensionProtocolHandler.h" #include "mozilla/net/NeckoParent.h" #include "mozilla/net/HttpChannelParent.h" #include "mozilla/net/CookieServiceParent.h" @@ -38,6 +40,7 @@ #include "mozilla/dom/network/UDPSocketParent.h" #include "mozilla/dom/workers/ServiceWorkerManager.h" #include "mozilla/LoadContext.h" +#include "mozilla/MozPromise.h" #include "nsPrintfCString.h" #include "nsHTMLDNSPrefetch.h" #include "nsEscape.h" @@ -62,8 +65,10 @@ using mozilla::dom::TCPServerSocketParent; using mozilla::net::PUDPSocketParent; using mozilla::dom::UDPSocketParent; using mozilla::dom::workers::ServiceWorkerManager; +using mozilla::ipc::AutoIPCStream; using mozilla::ipc::OptionalPrincipalInfo; using mozilla::ipc::PrincipalInfo; +using mozilla::ipc::LoadInfoArgsToLoadInfo; using IPC::SerializedLoadContext; namespace mozilla { @@ -955,5 +960,101 @@ NeckoParent::RecvNotifyCurrentTopLevelOuterContentWindowId(const uint64_t& aWind return IPC_OK(); } +mozilla::ipc::IPCResult +NeckoParent::RecvGetExtensionStream(const URIParams& aURI, + const LoadInfoArgs& aLoadInfo, + GetExtensionStreamResolver&& aResolve) +{ + nsCOMPtr deserializedURI = DeserializeURI(aURI); + if (!deserializedURI) { + return IPC_FAIL_NO_REASON(this); + } + + nsCOMPtr deserializedLoadInfo; + nsresult rv; + rv = LoadInfoArgsToLoadInfo(aLoadInfo, getter_AddRefs(deserializedLoadInfo)); + if (NS_FAILED(rv)) { + return IPC_FAIL_NO_REASON(this); + } + + RefPtr ph(ExtensionProtocolHandler::GetSingleton()); + MOZ_ASSERT(ph); + + // Ask the ExtensionProtocolHandler to give us a new input stream for + // this URI. The request comes from an ExtensionProtocolHandler in the + // child process, but is not guaranteed to be a valid moz-extension URI, + // and not guaranteed to represent a resource that the child should be + // allowed to access. The ExtensionProtocolHandler is responsible for + // validating the request. Specifically, only URI's for local files that + // an extension is allowed to access via moz-extension URI's should be + // accepted. + AutoIPCStream autoStream; + nsCOMPtr inputStream; + bool terminateSender = true; + auto inputStreamOrReason = ph->NewStream(deserializedURI, + deserializedLoadInfo, + &terminateSender); + if (inputStreamOrReason.isOk()) { + inputStream = inputStreamOrReason.unwrap(); + ContentParent* contentParent = static_cast(Manager()); + Unused << autoStream.Serialize(inputStream, contentParent); + } + + // If NewStream failed, we send back an invalid stream to the child so + // it can handle the error. MozPromise rejection is reserved for channel + // errors/disconnects. + aResolve(autoStream.TakeOptionalValue()); + + if (terminateSender) { + return IPC_FAIL_NO_REASON(this); + } else { + return IPC_OK(); + } +} + +mozilla::ipc::IPCResult +NeckoParent::RecvGetExtensionFD(const URIParams& aURI, + const OptionalLoadInfoArgs& aLoadInfo, + GetExtensionFDResolver&& aResolve) +{ + nsCOMPtr deserializedURI = DeserializeURI(aURI); + if (!deserializedURI) { + return IPC_FAIL_NO_REASON(this); + } + + nsCOMPtr deserializedLoadInfo; + nsresult rv; + rv = LoadInfoArgsToLoadInfo(aLoadInfo, getter_AddRefs(deserializedLoadInfo)); + if (NS_FAILED(rv)) { + return IPC_FAIL_NO_REASON(this); + } + + RefPtr ph(ExtensionProtocolHandler::GetSingleton()); + MOZ_ASSERT(ph); + + // Ask the ExtensionProtocolHandler to give us a new input stream for + // this URI. The request comes from an ExtensionProtocolHandler in the + // child process, but is not guaranteed to be a valid moz-extension URI, + // and not guaranteed to represent a resource that the child should be + // allowed to access. The ExtensionProtocolHandler is responsible for + // validating the request. Specifically, only URI's for local files that + // an extension is allowed to access via moz-extension URI's should be + // accepted. + bool terminateSender = true; + auto result = ph->NewFD(deserializedURI, deserializedLoadInfo, + &terminateSender, aResolve); + + if (result.isErr() && terminateSender) { + return IPC_FAIL_NO_REASON(this); + } + + if (result.isErr()) { + FileDescriptor invalidFD; + aResolve(invalidFD); + } + + return IPC_OK(); +} + } // namespace net } // namespace mozilla diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index 4ff79b3061e6..8897085dd335 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -236,6 +236,17 @@ class NeckoParent virtual mozilla::ipc::IPCResult RecvRemoveRequestContext(const uint64_t& rcid) override; virtual mozilla::ipc::IPCResult RecvNotifyCurrentTopLevelOuterContentWindowId(const uint64_t& aWindowId) override; + + /* WebExtensions */ + virtual mozilla::ipc::IPCResult + RecvGetExtensionStream(const URIParams& aURI, + const LoadInfoArgs& aLoadInfo, + GetExtensionStreamResolver&& aResolve) override; + + virtual mozilla::ipc::IPCResult + RecvGetExtensionFD(const URIParams& aURI, + const OptionalLoadInfoArgs& aLoadInfo, + GetExtensionFDResolver&& aResolve) override; }; } // namespace net diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index 9285e5be3450..beed972b5f5c 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -28,6 +28,7 @@ include protocol PFileChannel; include protocol PRtspController; include protocol PRtspChannel; +include IPCStream; include URIParams; include NeckoChannelParams; include PBrowserOrId; @@ -127,6 +128,14 @@ parent: prio(high) async NotifyCurrentTopLevelOuterContentWindowId(uint64_t windowId); + /** + * WebExtension-specific remote resource loading + */ + async GetExtensionStream(URIParams uri, LoadInfoArgs loadInfo) returns + (OptionalIPCStream stream); + async GetExtensionFD(URIParams uri, OptionalLoadInfoArgs loadInfo) returns + (FileDescriptor fd); + child: /* * Bring up the http auth prompt for a nested remote mozbrowser. diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.cpp b/netwerk/protocol/res/ExtensionProtocolHandler.cpp index a01d9805cd03..0d9e0d7d8f28 100644 --- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp +++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp @@ -6,29 +6,349 @@ #include "ExtensionProtocolHandler.h" +#include "mozilla/AbstractThread.h" +#include "mozilla/ClearOnShutdown.h" #include "mozilla/ExtensionPolicyService.h" +#include "mozilla/FileUtils.h" +#include "mozilla/ipc/IPCStreamUtils.h" +#include "mozilla/ipc/URIParams.h" +#include "mozilla/ipc/URIUtils.h" +#include "mozilla/net/NeckoChild.h" +#include "mozilla/RefPtr.h" + +#include "FileDescriptor.h" +#include "FileDescriptorFile.h" +#include "LoadInfo.h" +#include "nsContentUtils.h" #include "nsServiceManagerUtils.h" +#include "nsIFile.h" +#include "nsIFileChannel.h" +#include "nsIFileStreams.h" +#include "nsIFileURL.h" +#include "nsIJARChannel.h" #include "nsIURL.h" #include "nsIChannel.h" +#include "nsIInputStreamPump.h" +#include "nsIJARURI.h" #include "nsIStreamListener.h" +#include "nsIThread.h" #include "nsIInputStream.h" #include "nsIOutputStream.h" #include "nsIStreamConverterService.h" #include "nsNetUtil.h" -#include "LoadInfo.h" +#include "prio.h" #include "SimpleChannel.h" +#if defined(XP_WIN) +#include "nsILocalFileWin.h" +#endif + +#define EXTENSION_SCHEME "moz-extension" +using mozilla::ipc::FileDescriptor; +using OptionalIPCStream = mozilla::ipc::OptionalIPCStream; + namespace mozilla { + +template <> +class MOZ_MUST_USE_TYPE GenericErrorResult +{ + nsresult mErrorValue; + + template friend class Result; + +public: + explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {} + + operator nsresult() { return mErrorValue; } +}; + namespace net { using extensions::URLInfo; +StaticRefPtr ExtensionProtocolHandler::sSingleton; + +static inline Result +WrapNSResult(PRStatus aRv) +{ + if (aRv != PR_SUCCESS) { + return Err(NS_ERROR_FAILURE); + } + return Ok(); +} + +static inline Result +WrapNSResult(nsresult aRv) +{ + if (NS_FAILED(aRv)) { + return Err(aRv); + } + return Ok(); +} + +#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) + +/** + * Helper class used with SimpleChannel to asynchronously obtain an input + * stream or file descriptor from the parent for a remote moz-extension load + * from the child. + */ +class ExtensionStreamGetter : public RefCounted +{ + public: + // To use when getting a remote input stream for a resource + // in an unpacked extension. + ExtensionStreamGetter(nsIURI* aURI, nsILoadInfo* aLoadInfo) + : mURI(aURI) + , mLoadInfo(aLoadInfo) + , mIsJarChannel(false) + { + MOZ_ASSERT(aURI); + MOZ_ASSERT(aLoadInfo); + } + + // To use when getting an FD for a packed extension JAR file + // in order to load a resource. + ExtensionStreamGetter(nsIURI* aURI, nsILoadInfo* aLoadInfo, + already_AddRefed&& aJarChannel, + nsIFile* aJarFile) + : mURI(aURI) + , mLoadInfo(aLoadInfo) + , mJarChannel(Move(aJarChannel)) + , mJarFile(aJarFile) + , mIsJarChannel(true) + { + MOZ_ASSERT(aURI); + MOZ_ASSERT(aLoadInfo); + MOZ_ASSERT(mJarChannel); + MOZ_ASSERT(aJarFile); + } + + ~ExtensionStreamGetter() {} + + // Get an input stream or file descriptor from the parent asynchronously. + Result GetAsync(nsIStreamListener* aListener, + nsIChannel* aChannel); + + // Handle an input stream being returned from the parent + void OnStream(nsIInputStream* aStream); + + // Handle file descriptor being returned from the parent + void OnFD(const FileDescriptor& aFD); + + MOZ_DECLARE_REFCOUNTED_TYPENAME(ExtensionStreamGetter) + + private: + nsCOMPtr mURI; + nsCOMPtr mLoadInfo; + nsCOMPtr mJarChannel; + nsCOMPtr mJarFile; + nsCOMPtr mListener; + nsCOMPtr mChannel; + bool mIsJarChannel; +}; + +class ExtensionJARFileOpener : public nsISupports +{ +public: + ExtensionJARFileOpener(nsIFile* aFile, + NeckoParent::GetExtensionFDResolver& aResolve) : + mFile(aFile), + mResolve(aResolve) + { + MOZ_ASSERT(aFile); + MOZ_ASSERT(aResolve); + } + + NS_IMETHOD OpenFile() + { + MOZ_ASSERT(!NS_IsMainThread()); + AutoFDClose prFileDesc; + +#if defined(XP_WIN) + nsresult rv; + nsCOMPtr winFile = do_QueryInterface(mFile, &rv); + MOZ_ASSERT(winFile); + if (NS_SUCCEEDED(rv)) { + rv = winFile->OpenNSPRFileDescShareDelete(PR_RDONLY, 0, + &prFileDesc.rwget()); + } +#else + nsresult rv = mFile->OpenNSPRFileDesc(PR_RDONLY, 0, &prFileDesc.rwget()); +#endif /* XP_WIN */ + + if (NS_SUCCEEDED(rv)) { + mFD = FileDescriptor(FileDescriptor::PlatformHandleType( + PR_FileDesc2NativeHandle(prFileDesc))); + } + + nsCOMPtr event = + mozilla::NewRunnableMethod("ExtensionJarFileFDResolver", + this, &ExtensionJARFileOpener::SendBackFD); + + rv = NS_DispatchToMainThread(event, nsIEventTarget::DISPATCH_NORMAL); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread"); + return NS_OK; + } + + NS_IMETHOD SendBackFD() + { + MOZ_ASSERT(NS_IsMainThread()); + mResolve(mFD); + return NS_OK; + } + + NS_DECL_THREADSAFE_ISUPPORTS + +private: + virtual ~ExtensionJARFileOpener() {} + + nsCOMPtr mFile; + NeckoParent::GetExtensionFDResolver mResolve; + FileDescriptor mFD; +}; + +NS_IMPL_ISUPPORTS(ExtensionJARFileOpener, nsISupports) + +// The amount of time, in milliseconds, that the file opener thread will remain +// allocated after it is used. This value chosen because to match other uses +// of LazyIdleThread. +#define DEFAULT_THREAD_TIMEOUT_MS 30000 + +// Request an FD or input stream from the parent. +Result +ExtensionStreamGetter::GetAsync(nsIStreamListener* aListener, + nsIChannel* aChannel) +{ + MOZ_ASSERT(IsNeckoChild()); + + mListener = aListener; + mChannel = aChannel; + + // Serialize the URI to send to parent + mozilla::ipc::URIParams uri; + SerializeURI(mURI, uri); + + // Serialize the LoadInfo to send to parent + OptionalLoadInfoArgs loadInfo; + NS_TRY(mozilla::ipc::LoadInfoToLoadInfoArgs(mLoadInfo, &loadInfo)); + + RefPtr self = this; + if (mIsJarChannel) { + // Request an FD for this moz-extension URI + gNeckoChild->SendGetExtensionFD(uri, loadInfo)->Then( + AbstractThread::MainThread(), + __func__, + [self] (const FileDescriptor& fd) { + self->OnFD(fd); + }, + [self] (const mozilla::ipc::PromiseRejectReason) { + self->OnFD(FileDescriptor()); + } + ); + return Ok(); + } + + // Request an input stream for this moz-extension URI + gNeckoChild->SendGetExtensionStream(uri, loadInfo)->Then( + AbstractThread::MainThread(), + __func__, + [self] (const OptionalIPCStream& stream) { + nsCOMPtr inputStream; + if (stream.type() == OptionalIPCStream::OptionalIPCStream::TIPCStream) { + inputStream = ipc::DeserializeIPCStream(stream); + } + self->OnStream(inputStream); + }, + [self] (const mozilla::ipc::PromiseRejectReason) { + self->OnStream(nullptr); + } + ); + return Ok(); +} + +// Handle an input stream sent from the parent. +void +ExtensionStreamGetter::OnStream(nsIInputStream* aStream) +{ + MOZ_ASSERT(IsNeckoChild()); + MOZ_ASSERT(mListener); + + // We must keep an owning reference to the listener + // until we pass it on to AsyncRead. + nsCOMPtr listener = mListener.forget(); + + MOZ_ASSERT(mChannel); + + if (!aStream) { + // The parent didn't send us back a stream. + listener->OnStartRequest(mChannel, nullptr); + listener->OnStopRequest(mChannel, nullptr, NS_ERROR_FILE_ACCESS_DENIED); + mChannel->Cancel(NS_BINDING_ABORTED); + return; + } + + nsCOMPtr pump; + nsresult rv = NS_NewInputStreamPump(getter_AddRefs(pump), aStream); + if (NS_FAILED(rv)) { + mChannel->Cancel(NS_BINDING_ABORTED); + return; + } + + rv = pump->AsyncRead(listener, nullptr); + if (NS_FAILED(rv)) { + mChannel->Cancel(NS_BINDING_ABORTED); + } +} + +// Handle an FD sent from the parent. +void +ExtensionStreamGetter::OnFD(const FileDescriptor& aFD) +{ + MOZ_ASSERT(IsNeckoChild()); + MOZ_ASSERT(mListener); + MOZ_ASSERT(mChannel); + + if (!aFD.IsValid()) { + OnStream(nullptr); + return; + } + + // We must keep an owning reference to the listener + // until we pass it on to AsyncOpen2. + nsCOMPtr listener = mListener.forget(); + + RefPtr fdFile = new FileDescriptorFile(aFD, mJarFile); + mJarChannel->SetJarFile(fdFile); + nsresult rv = mJarChannel->AsyncOpen2(listener); + if (NS_FAILED(rv)) { + mChannel->Cancel(NS_BINDING_ABORTED); + } +} + NS_IMPL_QUERY_INTERFACE(ExtensionProtocolHandler, nsISubstitutingProtocolHandler, nsIProtocolHandler, nsIProtocolHandlerWithDynamicFlags, nsISupportsWeakReference) NS_IMPL_ADDREF_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler) NS_IMPL_RELEASE_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler) +already_AddRefed +ExtensionProtocolHandler::GetSingleton() +{ + if (!sSingleton) { + sSingleton = new ExtensionProtocolHandler(); + ClearOnShutdown(&sSingleton); + } + return do_AddRef(sSingleton.get()); +} + +ExtensionProtocolHandler::ExtensionProtocolHandler() + : SubstitutingProtocolHandler(EXTENSION_SCHEME) +{ + mUseRemoteFileChannels = IsNeckoChild() && + Preferences::GetBool("extensions.webextensions.protocol.remote"); +} + static inline ExtensionPolicyService& EPS() { @@ -77,17 +397,45 @@ ExtensionProtocolHandler::ResolveSpecialCases(const nsACString& aHost, return false; } -static inline Result -WrapNSResult(nsresult aRv) +// For file or JAR URI's, substitute in a remote channel. +Result +ExtensionProtocolHandler::SubstituteRemoteChannel(nsIURI* aURI, + nsILoadInfo* aLoadInfo, + nsIChannel** aRetVal) { - if (NS_FAILED(aRv)) { - return Err(aRv); + MOZ_ASSERT(IsNeckoChild()); + NS_TRY(aURI ? NS_OK : NS_ERROR_INVALID_ARG); + NS_TRY(aLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG); + + nsAutoCString unResolvedSpec; + NS_TRY(aURI->GetSpec(unResolvedSpec)); + + nsAutoCString resolvedSpec; + NS_TRY(ResolveURI(aURI, resolvedSpec)); + + // Use the target URI scheme to determine if this is a packed or unpacked + // extension URI. For unpacked extensions, we'll request an input stream + // from the parent. For a packed extension, we'll request a file descriptor + // for the JAR file. + nsAutoCString scheme; + NS_TRY(net_ExtractURLScheme(resolvedSpec, scheme)); + + if (scheme.EqualsLiteral("file")) { + // Unpacked extension + SubstituteRemoteFileChannel(aURI, aLoadInfo, resolvedSpec, aRetVal); + return Ok(); + } + + if (scheme.EqualsLiteral("jar")) { + // Packed extension + return SubstituteRemoteJarChannel(aURI, aLoadInfo, resolvedSpec, aRetVal); } + + // Only unpacked resource files and JAR files are remoted. + // No other moz-extension loads should be reading from the filesystem. return Ok(); } -#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) - nsresult ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, @@ -97,6 +445,10 @@ ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI, nsCOMPtr url = do_QueryInterface(aURI, &rv); NS_ENSURE_SUCCESS(rv, rv); + if (mUseRemoteFileChannels) { + MOZ_TRY(SubstituteRemoteChannel(aURI, aLoadInfo, result)); + } + nsAutoCString ext; rv = url->GetFileExtension(ext); NS_ENSURE_SUCCESS(rv, rv); @@ -146,6 +498,241 @@ ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI, return NS_OK; } +Result, nsresult> +ExtensionProtocolHandler::NewStream(nsIURI* aChildURI, + nsILoadInfo* aChildLoadInfo, + bool* aTerminateSender) +{ + MOZ_ASSERT(!IsNeckoChild()); + NS_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); + NS_TRY(aChildLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG); + NS_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); + + *aTerminateSender = true; + nsresult rv; + + // We should never receive a URI that isn't for a moz-extension because + // these requests ordinarily come from the child's ExtensionProtocolHandler. + // Ensure this request is for a moz-extension URI. A rogue child process + // could send us any URI. + bool isExtScheme = false; + if (NS_FAILED(aChildURI->SchemeIs(EXTENSION_SCHEME, &isExtScheme)) || + !isExtScheme) { + return Err(NS_ERROR_UNKNOWN_PROTOCOL); + } + + // For errors after this point, we want to propagate the error to + // the child, but we don't force the child to be terminated because + // the error is likely to be due to a bug in the extension. + *aTerminateSender = false; + + /* + * Make sure there is a substitution installed for the host found + * in the child's request URI and make sure the host resolves to + * a directory. + */ + + nsAutoCString host; + NS_TRY(aChildURI->GetAsciiHost(host)); + + // Lookup the directory this host string resolves to + nsCOMPtr baseURI; + NS_TRY(GetSubstitution(host, getter_AddRefs(baseURI))); + + // The result should be a file URL for the extension base dir + nsCOMPtr fileURL = do_QueryInterface(baseURI, &rv); + NS_TRY(rv); + + nsCOMPtr extensionDir; + NS_TRY(fileURL->GetFile(getter_AddRefs(extensionDir))); + + bool isDirectory = false; + NS_TRY(extensionDir->IsDirectory(&isDirectory)); + if (!isDirectory) { + // The host should map to a directory for unpacked extensions + return Err(NS_ERROR_FILE_NOT_DIRECTORY); + } + + /* + * Now get a channel for the resolved child URI and make sure the + * channel is a file channel. + */ + + nsCOMPtr childPrincipal; + NS_TRY(aChildLoadInfo->GetLoadingPrincipal(getter_AddRefs(childPrincipal))); + if (nsContentUtils::IsSystemPrincipal(childPrincipal)) { + return Err(NS_ERROR_FILE_ACCESS_DENIED); + } + + nsCOMPtr channel; + NS_TRY(NS_NewChannelInternal(getter_AddRefs(channel), + aChildURI, + aChildLoadInfo)); + + // Channel should be a file channel. It should never be a JAR + // channel because we only request remote streams for unpacked + // extension resource loads where the URI resolves to a file. + nsCOMPtr fileChannel = do_QueryInterface(channel, &rv); + NS_TRY(rv); + + nsCOMPtr requestedFile; + NS_TRY(fileChannel->GetFile(getter_AddRefs(requestedFile))); + + /* + * Make sure the file we resolved to is within the extension directory. + */ + + // Normalize paths for sane comparisons. nsIFile::Contains depends on + // it for reliable subpath checks. + NS_TRY(extensionDir->Normalize()); + NS_TRY(requestedFile->Normalize()); + + bool isResourceFromExtensionDir = false; + NS_TRY(extensionDir->Contains(requestedFile, &isResourceFromExtensionDir)); + if (!isResourceFromExtensionDir) { + return Err(NS_ERROR_FILE_ACCESS_DENIED); + } + + nsCOMPtr inputStream; + NS_TRY(NS_NewLocalFileInputStream(getter_AddRefs(inputStream), + requestedFile, + PR_RDONLY, + -1, + nsIFileInputStream::DEFER_OPEN)); + + return inputStream; +} + +Result +ExtensionProtocolHandler::NewFD(nsIURI* aChildURI, + nsILoadInfo* aChildLoadInfo, + bool* aTerminateSender, + NeckoParent::GetExtensionFDResolver& aResolve) +{ + MOZ_ASSERT(!IsNeckoChild()); + NS_TRY(aChildURI ? NS_OK : NS_ERROR_INVALID_ARG); + NS_TRY(aChildLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG); + NS_TRY(aTerminateSender ? NS_OK : NS_ERROR_INVALID_ARG); + + *aTerminateSender = true; + nsresult rv; + + // Ensure this is a moz-extension URI + bool isExtScheme = false; + if (NS_FAILED(aChildURI->SchemeIs(EXTENSION_SCHEME, &isExtScheme)) || + !isExtScheme) { + return Err(NS_ERROR_UNKNOWN_PROTOCOL); + } + + // For errors after this point, we want to propagate the error to + // the child, but we don't force the child to be terminated. + *aTerminateSender = false; + + nsAutoCString host; + NS_TRY(aChildURI->GetAsciiHost(host)); + + // We expect the host string to map to a JAR file because the URI + // should refer to a web accessible resource for an enabled extension. + nsCOMPtr subURI; + NS_TRY(GetSubstitution(host, getter_AddRefs(subURI))); + + nsCOMPtr jarURI = do_QueryInterface(subURI, &rv); + NS_TRY(rv); + + nsCOMPtr innerFileURI; + NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); + + nsCOMPtr innerFileURL = do_QueryInterface(innerFileURI, &rv); + NS_TRY(rv); + + nsCOMPtr jarFile; + NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); + + if (!mFileOpenerThread) { + mFileOpenerThread = + new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, + NS_LITERAL_CSTRING("ExtensionProtocolHandler")); + } + + RefPtr fileOpener = + new ExtensionJARFileOpener(jarFile, aResolve); + + nsCOMPtr event = + mozilla::NewRunnableMethod("ExtensionJarFileOpener", + fileOpener, &ExtensionJARFileOpener::OpenFile); + + NS_TRY(mFileOpenerThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL)); + + return Ok(); +} + +static void +NewSimpleChannel(nsIURI* aURI, + nsILoadInfo* aLoadinfo, + ExtensionStreamGetter* aStreamGetter, + nsIChannel** aRetVal) +{ + nsCOMPtr channel = NS_NewSimpleChannel( + aURI, aLoadinfo, aStreamGetter, + [] (nsIStreamListener* listener, nsIChannel* channel, + ExtensionStreamGetter* getter) -> RequestOrReason { + MOZ_TRY(getter->GetAsync(listener, channel)); + return RequestOrReason(nullptr); + + }); + channel.swap(*aRetVal); +} + +void +ExtensionProtocolHandler::SubstituteRemoteFileChannel(nsIURI* aURI, + nsILoadInfo* aLoadinfo, + nsACString& aResolvedFileSpec, + nsIChannel** aRetVal) +{ + MOZ_ASSERT(IsNeckoChild()); + + RefPtr streamGetter = + new ExtensionStreamGetter(aURI, aLoadinfo); + + NewSimpleChannel(aURI, aLoadinfo, streamGetter, aRetVal); +} + +Result +ExtensionProtocolHandler::SubstituteRemoteJarChannel(nsIURI* aURI, + nsILoadInfo* aLoadinfo, + nsACString& aResolvedSpec, + nsIChannel** aRetVal) +{ + MOZ_ASSERT(IsNeckoChild()); + nsresult rv; + + // Build a JAR URI for this jar:file:// URI and use it to extract the + // inner file URI. + nsCOMPtr uri; + NS_TRY(NS_NewURI(getter_AddRefs(uri), aResolvedSpec)); + + nsCOMPtr jarURI = do_QueryInterface(uri, &rv); + NS_TRY(rv); + + nsCOMPtr innerFileURI; + NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI))); + + nsCOMPtr innerFileURL = do_QueryInterface(innerFileURI, &rv); + NS_TRY(rv); + + nsCOMPtr jarFile; + NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile))); + + nsCOMPtr jarChannel = do_QueryInterface(*aRetVal, &rv); + NS_TRY(rv); + + RefPtr streamGetter = + new ExtensionStreamGetter(aURI, aLoadinfo, jarChannel.forget(), jarFile); + + NewSimpleChannel(aURI, aLoadinfo, streamGetter, aRetVal); + return Ok(); +} + #undef NS_TRY } // namespace net diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.h b/netwerk/protocol/res/ExtensionProtocolHandler.h index 9bb08343ab14..2cf665a6080c 100644 --- a/netwerk/protocol/res/ExtensionProtocolHandler.h +++ b/netwerk/protocol/res/ExtensionProtocolHandler.h @@ -6,8 +6,9 @@ #ifndef ExtensionProtocolHandler_h___ #define ExtensionProtocolHandler_h___ +#include "mozilla/net/NeckoParent.h" +#include "mozilla/LazyIdleThread.h" #include "SubstitutingProtocolHandler.h" -#include "nsWeakReference.h" namespace mozilla { namespace net { @@ -23,11 +24,63 @@ class ExtensionProtocolHandler final : public nsISubstitutingProtocolHandler, NS_FORWARD_NSIPROTOCOLHANDLER(SubstitutingProtocolHandler::) NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(SubstitutingProtocolHandler::) - ExtensionProtocolHandler() : SubstitutingProtocolHandler("moz-extension") {} + static already_AddRefed GetSingleton(); + + /** + * To be called in the parent process to obtain an input stream for a + * a web accessible resource from an unpacked WebExtension dir. + * + * @param aChildURI a moz-extension URI sent from the child that refers + * to a web accessible resource file in an enabled unpacked extension + * @param aChildLoadInfo the loadinfo for the request sent from the child + * @param aTerminateSender out param set to true when the params are invalid + * and indicate the child should be terminated. If |aChildURI| is + * not a moz-extension URI, the child is in an invalid state and + * should be terminated. + * @return NS_OK with |aTerminateSender| set to false on success. On + * failure, returns an error and sets |aTerminateSender| to indicate + * whether or not the child process should be terminated. + * A moz-extension URI from the child that doesn't resolve to a + * resource file within the extension could be the result of a bug + * in the extension and doesn't result in |aTerminateSender| being + * set to true. + */ + Result, nsresult> NewStream(nsIURI* aChildURI, + nsILoadInfo* aChildLoadInfo, + bool* aTerminateSender); + + /** + * To be called in the parent process to obtain a file descriptor for an + * enabled WebExtension JAR file. + * + * @param aChildURI a moz-extension URI sent from the child that refers + * to a web accessible resource file in an enabled unpacked extension + * @param aChildLoadInfo the loadinfo for the request sent from the child + * @param aTerminateSender out param set to true when the params are invalid + * and indicate the child should be terminated. If |aChildURI| is + * not a moz-extension URI, the child is in an invalid state and + * should be terminated. + * @param aPromise a promise that will be resolved asynchronously when the + * file descriptor is available. + * @return NS_OK with |aTerminateSender| set to false on success. On + * failure, returns an error and sets |aTerminateSender| to indicate + * whether or not the child process should be terminated. + * A moz-extension URI from the child that doesn't resolve to an + * enabled WebExtension JAR could be the result of a bug in the + * extension and doesn't result in |aTerminateSender| being + * set to true. + */ + Result NewFD(nsIURI* aChildURI, + nsILoadInfo* aChildLoadInfo, + bool* aTerminateSender, + NeckoParent::GetExtensionFDResolver& aResolve); protected: ~ExtensionProtocolHandler() {} +private: + explicit ExtensionProtocolHandler(); + MOZ_MUST_USE bool ResolveSpecialCases(const nsACString& aHost, const nsACString& aPath, const nsACString& aPathname, @@ -40,6 +93,72 @@ class ExtensionProtocolHandler final : public nsISubstitutingProtocolHandler, virtual MOZ_MUST_USE nsresult SubstituteChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, nsIChannel** result) override; + + /** + * For moz-extension URI's that resolve to file or JAR URI's, replaces + * the provided channel with a channel that will proxy the load to the + * parent process. For moz-extension URI's that resolve to other types + * of URI's (not file or JAR), the provide channel is not replaced and + * NS_OK is returned. + * + * @param aURI the moz-extension URI + * @param aLoadInfo the loadinfo for the request + * @param aRetVal in/out channel param referring to the channel that + * might need to be substituted with a remote channel. + * @return NS_OK if the channel does not need to be substituted or + * or the replacement channel was created successfully. + * Otherwise returns an error. + */ + Result SubstituteRemoteChannel(nsIURI* aURI, + nsILoadInfo* aLoadInfo, + nsIChannel** aRetVal); + + /** + * Replaces a file channel with a remote file channel for loading a + * web accessible resource for an unpacked extension from the parent. + * + * @param aURI the moz-extension URI + * @param aLoadInfo the loadinfo for the request + * @param aResolvedFileSpec the resolved URI spec for the file. + * @param aRetVal in/out param referring to the new remote channel. + * The reference to the input param file channel is dropped and + * replaced with a reference to a new channel that remotes + * the file access. The new channel encapsulates a request to + * the parent for an IPCStream for the file. + */ + void SubstituteRemoteFileChannel(nsIURI* aURI, + nsILoadInfo* aLoadinfo, + nsACString& aResolvedFileSpec, + nsIChannel** aRetVal); + + /** + * Replaces a JAR channel with a remote JAR channel for loading a + * an extension JAR file from the parent. + * + * @param aURI the moz-extension URI + * @param aLoadInfo the loadinfo for the request + * @param aResolvedFileSpec the resolved URI spec for the file. + * @param aRetVal in/out param referring to the new remote channel. + * The input param JAR channel is replaced with a new channel + * that remotes the JAR file access. The new channel encapsulates + * a request to the parent for the JAR file FD. + */ + Result SubstituteRemoteJarChannel(nsIURI* aURI, + nsILoadInfo* aLoadinfo, + nsACString& aResolvedSpec, + nsIChannel** aRetVal); + + // Used for opening JAR files off the main thread when we just need to + // obtain a file descriptor to send back to the child. + RefPtr mFileOpenerThread; + + // To allow parent IPDL actors to invoke methods on this handler when + // handling moz-extension requests from the child. + static StaticRefPtr sSingleton; + + // Set to true when this instance of the handler must proxy loads of + // extension web-accessible resources to the parent process. + bool mUseRemoteFileChannels; }; } // namespace net diff --git a/netwerk/protocol/res/moz.build b/netwerk/protocol/res/moz.build index 37e2316b05d6..30972f09182e 100644 --- a/netwerk/protocol/res/moz.build +++ b/netwerk/protocol/res/moz.build @@ -11,6 +11,11 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'necko_res' +EXPORTS.mozilla.net += [ + 'ExtensionProtocolHandler.h', + 'SubstitutingProtocolHandler.h', +] + UNIFIED_SOURCES += [ 'ExtensionProtocolHandler.cpp', 'nsResProtocolHandler.cpp', diff --git a/xpcom/io/FileDescriptorFile.cpp b/xpcom/io/FileDescriptorFile.cpp new file mode 100644 index 000000000000..656e5d005bfa --- /dev/null +++ b/xpcom/io/FileDescriptorFile.cpp @@ -0,0 +1,486 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* 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 "FileDescriptorFile.h" + +#include "mozilla/ipc/FileDescriptorUtils.h" +#include "mozilla/ipc/URIUtils.h" +#include "mozilla/net/NeckoChild.h" +#include "nsIFileURL.h" +#include "nsNetUtil.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" +#include "private/pprio.h" +#include "SerializedLoadContext.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(FileDescriptorFile, nsIFile) + +LazyLogModule gFDFileLog("FDFile"); +#undef DBG +#define DBG(...) MOZ_LOG(gFDFileLog, LogLevel::Debug, (__VA_ARGS__)) + +FileDescriptorFile::FileDescriptorFile(const FileDescriptor& aFD, + nsIFile* aFile) +{ + MOZ_ASSERT(aFD.IsValid()); + auto platformHandle = aFD.ClonePlatformHandle(); + mFD = FileDescriptor(platformHandle.get()); + mFile = aFile; +} + +FileDescriptorFile::FileDescriptorFile(const FileDescriptorFile& aOther) +{ + auto platformHandle = aOther.mFD.ClonePlatformHandle(); + mFD = FileDescriptor(platformHandle.get()); + aOther.mFile->Clone(getter_AddRefs(mFile)); +} + +//----------------------------------------------------------------------------- +// FileDescriptorFile::nsIFile functions that we override logic for +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +FileDescriptorFile::Clone(nsIFile **aFileOut) +{ + RefPtr fdFile = new FileDescriptorFile(*this); + fdFile.forget(aFileOut); + return NS_OK; +} + +NS_IMETHODIMP +FileDescriptorFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode, + PRFileDesc **aRetval) +{ + // Remove optional OS_READAHEAD flag so we test against PR_RDONLY + aFlags &= ~nsIFile::OS_READAHEAD; + + // Remove optional/deprecated DELETE_ON_CLOSE flag + aFlags &= ~nsIFile::DELETE_ON_CLOSE; + + // All other flags require write access to the file and + // this implementation only provides read access. + if (aFlags != PR_RDONLY) { + DBG("OpenNSPRFileDesc flags error (%" PRIu32 ")\n", aFlags); + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mFD.IsValid()) { + DBG("OpenNSPRFileDesc error: no file descriptor\n"); + return NS_ERROR_NOT_AVAILABLE; + } + + auto platformHandle = mFD.ClonePlatformHandle(); + *aRetval = PR_ImportFile(PROsfd(platformHandle.release())); + + if (!*aRetval) { + DBG("OpenNSPRFileDesc Clone failure\n"); + return NS_ERROR_NOT_AVAILABLE; + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// FileDescriptorFile::nsIFile functions that we delegate to underlying nsIFile +//----------------------------------------------------------------------------- + +nsresult +FileDescriptorFile::GetLeafName(nsAString &aLeafName) +{ + return mFile->GetLeafName(aLeafName); +} + +NS_IMETHODIMP +FileDescriptorFile::GetNativeLeafName(nsACString &aLeafName) +{ + return mFile->GetNativeLeafName(aLeafName); +} + +nsresult +FileDescriptorFile::GetTarget(nsAString &_retval) +{ + return mFile->GetTarget(_retval); +} + +NS_IMETHODIMP +FileDescriptorFile::GetNativeTarget(nsACString &_retval) +{ + return mFile->GetNativeTarget(_retval); +} + +nsresult +FileDescriptorFile::GetPath(nsAString &_retval) +{ + return mFile->GetPath(_retval); +} + +NS_IMETHODIMP +FileDescriptorFile::GetNativePath(nsACString &_retval) +{ + return mFile->GetNativePath(_retval); +} + +NS_IMETHODIMP +FileDescriptorFile::Equals(nsIFile *inFile, bool *_retval) +{ + return mFile->Equals(inFile, _retval); +} + +NS_IMETHODIMP +FileDescriptorFile::Contains(nsIFile *inFile, bool *_retval) +{ + return mFile->Contains(inFile, _retval); +} + +NS_IMETHODIMP +FileDescriptorFile::GetParent(nsIFile **aParent) +{ + return mFile->GetParent(aParent); +} + +NS_IMETHODIMP +FileDescriptorFile::GetFollowLinks(bool *aFollowLinks) +{ + return mFile->GetFollowLinks(aFollowLinks); +} + +//----------------------------------------------------------------------------- +// FileDescriptorFile::nsIFile functions that are not currently supported +//----------------------------------------------------------------------------- + +nsresult +FileDescriptorFile::Append(const nsAString &node) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::AppendNative(const nsACString &fragment) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::Normalize() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::Create(uint32_t type, uint32_t permissions) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +FileDescriptorFile::SetLeafName(const nsAString &aLeafName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetNativeLeafName(const nsACString &aLeafName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +FileDescriptorFile::InitWithPath(const nsAString &filePath) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::InitWithNativePath(const nsACString &filePath) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::InitWithFile(nsIFile *aFile) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetFollowLinks(bool aFollowLinks) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +FileDescriptorFile::AppendRelativePath(const nsAString &node) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::AppendRelativeNativePath(const nsACString &fragment) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetRelativeDescriptor(nsIFile *fromFile, nsACString& _retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetRelativeDescriptor(nsIFile *fromFile, + const nsACString& relativeDesc) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetRelativePath(nsIFile *fromFile, nsACString& _retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetRelativePath(nsIFile *fromFile, + const nsACString& relativePath) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +FileDescriptorFile::CopyTo(nsIFile *newParentDir, const nsAString &newName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::CopyToNative(nsIFile *newParent, const nsACString &newName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +FileDescriptorFile::CopyToFollowingLinks(nsIFile *newParentDir, + const nsAString &newName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::CopyToFollowingLinksNative(nsIFile *newParent, + const nsACString &newName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +FileDescriptorFile::MoveTo(nsIFile *newParentDir, const nsAString &newName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::MoveToNative(nsIFile *newParent, const nsACString &newName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::RenameTo(nsIFile *newParentDir, const nsAString &newName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::RenameToNative(nsIFile *newParentDir, const nsACString &newName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::Remove(bool recursive) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetPermissions(uint32_t *aPermissions) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetPermissions(uint32_t aPermissions) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetPermissionsOfLink(uint32_t *aPermissionsOfLink) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetPermissionsOfLink(uint32_t aPermissions) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetLastModifiedTime(PRTime *aLastModTime) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetLastModifiedTime(PRTime aLastModTime) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetLastModifiedTimeOfLink(PRTime *aLastModTimeOfLink) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetFileSize(int64_t *aFileSize) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::SetFileSize(int64_t aFileSize) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetFileSizeOfLink(int64_t *aFileSize) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::Exists(bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::IsWritable(bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::IsReadable(bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::IsExecutable(bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::IsHidden(bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::IsDirectory(bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::IsFile(bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::IsSymlink(bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::IsSpecial(bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::CreateUnique(uint32_t type, uint32_t attributes) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetDirectoryEntries(nsISimpleEnumerator **entries) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::OpenANSIFileDesc(const char *mode, FILE **_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::Load(PRLibrary **_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::Reveal() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorFile::Launch() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +} // namespace net +} // namespace mozilla diff --git a/xpcom/io/FileDescriptorFile.h b/xpcom/io/FileDescriptorFile.h new file mode 100644 index 000000000000..fc9ae07215de --- /dev/null +++ b/xpcom/io/FileDescriptorFile.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* 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 _FileDescriptorFile_h +#define _FileDescriptorFile_h + +#include "mozilla/Attributes.h" +#include "mozilla/ipc/FileDescriptor.h" +#include "nsIFile.h" +#include "nsIFileURL.h" +#include "nsIURI.h" +#include "private/pprio.h" + +namespace mozilla { +namespace net { + +/** + * A limited implementation of nsIFile that wraps a FileDescriptor object + * allowing the file to be read from. Added to allow a child process to use + * an nsIFile object for a file it does not have access to on the filesystem + * but has been provided a FileDescriptor for from the parent. Many nsIFile + * methods are not implemented and this is not intended to be a general + * purpose file implementation. + */ +class FileDescriptorFile final : public nsIFile +{ + typedef mozilla::ipc::FileDescriptor FileDescriptor; + +public: + FileDescriptorFile(const FileDescriptor& aFD, nsIFile* aFile); + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIFILE + +private: + ~FileDescriptorFile() + {} + + FileDescriptorFile(const FileDescriptorFile& other); + + // regular nsIFile object, that we forward most calls to. + nsCOMPtr mFile; + FileDescriptor mFD; +}; + +} // namespace net +} // namespace mozilla + +#endif // _FileDescriptorFile_h diff --git a/xpcom/io/moz.build b/xpcom/io/moz.build index 6f21e0a72747..a1db081e39fa 100644 --- a/xpcom/io/moz.build +++ b/xpcom/io/moz.build @@ -61,6 +61,7 @@ else: XPIDL_MODULE = 'xpcom_io' EXPORTS += [ + 'FileDescriptorFile.h', 'nsAnonymousTemporaryFile.h', 'nsAppDirectoryServiceDefs.h', 'nsDirectoryService.h', @@ -92,6 +93,7 @@ EXPORTS.mozilla += [ UNIFIED_SOURCES += [ 'Base64.cpp', 'crc32c.c', + 'FileDescriptorFile.cpp', 'nsAnonymousTemporaryFile.cpp', 'nsAppFileLocationProvider.cpp', 'nsBinaryStream.cpp',