--- a/modules/libjar/nsIJARChannel.idl
+++ b/modules/libjar/nsIJARChannel.idl
@@ -16,17 +16,20 @@ interface nsIJARChannel : nsIChannel
* by the server for a remote JAR is not of an expected type). Scripting,
* redirects, and plugins should be disabled when loading from this
* channel.
*/
[infallible] readonly attribute boolean isUnsafe;
/**
* 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.
* This will work even without opening the channel.
*/
readonly attribute nsIZipEntry zipEntry;
};
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -341,16 +341,22 @@ nsJARChannel::LookupFile(bool aAllowAsyn
return rv;
// The name of the JAR entry must not contain URL-escaped characters:
// we're moving from URL domain to a filename domain here. nsStandardURL
// does basic escaping by default, which breaks reading zipped files which
// 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<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
if (fileURL)
fileURL->GetFile(getter_AddRefs(mJarFile));
}
// try to handle a nested jar
@@ -880,16 +886,27 @@ nsJARChannel::GetIsUnsafe(bool *isUnsafe
NS_IMETHODIMP
nsJARChannel::GetJarFile(nsIFile **aFile)
{
NS_IF_ADDREF(*aFile = mJarFile);
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)
{
nsresult rv = LookupFile(false);
if (NS_FAILED(rv))
return rv;
if (!mJarFile)
return NS_ERROR_NOT_AVAILABLE;
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -46,16 +46,18 @@ public:
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSITHREADRETARGETABLEREQUEST
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
nsJARChannel();
nsresult Init(nsIURI *uri);
+ void SetFile(nsIFile *file);
+
private:
virtual ~nsJARChannel();
nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **);
nsresult LookupFile(bool aAllowAsync);
nsresult OpenLocalFile();
void NotifyError(nsresult aError);
void FireOnProgress(uint64_t aProgress);
@@ -93,16 +95,17 @@ private:
bool mIsUnsafe;
mozilla::net::MemoryDownloader::Data mTempMem;
nsCOMPtr<nsIInputStreamPump> mPump;
// mRequest is only non-null during OnStartRequest, so we'll have a pointer
// to the request if we get called back via RetargetDeliveryTo.
nsCOMPtr<nsIRequest> mRequest;
nsCOMPtr<nsIFile> mJarFile;
+ nsCOMPtr<nsIFile> mJarFileOverride;
nsCOMPtr<nsIURI> mJarBaseURI;
nsCString mJarEntry;
nsCString mInnerJarEntry;
// True if this channel should not download any remote files.
bool mBlockRemoteFiles;
};
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4858,16 +4858,18 @@ pref("extensions.allow-non-mpc-extension
pref("extensions.webextensions.keepStorageOnUninstall", false);
pref("extensions.webextensions.keepUuidOnUninstall", false);
// Redirect basedomain used by identity api
pref("extensions.webextensions.identity.redirectDomain", "extensions.allizom.org");
// Whether or not webextension themes are supported.
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);
// Report Site Issue button
pref("extensions.webcompat-reporter.newIssueEndpoint", "https://webcompat.com/issues/new");
#if defined(MOZ_DEV_EDITION) || defined(NIGHTLY_BUILD)
pref("extensions.webcompat-reporter.enabled", true);
#else
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -305,17 +305,18 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(Name
#ifdef NECKO_PROTOCOL_res
// resource
#include "nsResProtocolHandler.h"
#include "ExtensionProtocolHandler.h"
#include "SubstitutingProtocolHandler.h"
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
#ifdef NECKO_PROTOCOL_device
#include "nsDeviceProtocolHandler.h"
typedef mozilla::net::nsDeviceProtocolHandler nsDeviceProtocolHandler;
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceProtocolHandler)
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -3,16 +3,18 @@
/* 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 "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"
#include "mozilla/net/WyciwygChannelParent.h"
#include "mozilla/net/FTPChannelParent.h"
#include "mozilla/net/WebSocketChannelParent.h"
#include "mozilla/net/WebSocketEventListenerParent.h"
#include "mozilla/net/DataChannelParent.h"
@@ -33,16 +35,17 @@
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/TabContext.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/dom/network/TCPSocketParent.h"
#include "mozilla/dom/network/TCPServerSocketParent.h"
#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"
#include "SerializedLoadContext.h"
#include "nsAuthInformationHolder.h"
#include "nsIAuthPromptCallback.h"
#include "ContentPrincipal.h"
#include "nsINetworkPredictor.h"
@@ -57,18 +60,20 @@ using mozilla::dom::TabContext;
using mozilla::dom::TabParent;
using mozilla::net::PTCPSocketParent;
using mozilla::dom::TCPSocketParent;
using mozilla::net::PTCPServerSocketParent;
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 {
namespace net {
// C++ file contents
NeckoParent::NeckoParent()
{
@@ -950,10 +955,106 @@ NeckoParent::RecvNotifyCurrentTopLevelOu
{
if (NS_FAILED(NS_NotifyCurrentTopLevelOuterContentWindowId(aWindowId))) {
NS_WARNING("NS_NotifyCurrentTopLevelOuterContentWindowId failed!");
}
return IPC_OK();
}
+mozilla::ipc::IPCResult
+NeckoParent::RecvGetExtensionStream(const URIParams& aURI,
+ const LoadInfoArgs& aLoadInfo,
+ GetExtensionStreamResolver&& aResolve)
+{
+ nsCOMPtr<nsIURI> deserializedURI = DeserializeURI(aURI);
+ if (!deserializedURI) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ nsCOMPtr<nsILoadInfo> deserializedLoadInfo;
+ nsresult rv;
+ rv = LoadInfoArgsToLoadInfo(aLoadInfo, getter_AddRefs(deserializedLoadInfo));
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ RefPtr<ExtensionProtocolHandler> 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<nsIInputStream> inputStream;
+ bool terminateSender = true;
+ auto inputStreamOrReason = ph->NewStream(deserializedURI,
+ deserializedLoadInfo,
+ &terminateSender);
+ if (inputStreamOrReason.isOk()) {
+ inputStream = inputStreamOrReason.unwrap();
+ ContentParent* contentParent = static_cast<ContentParent*>(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<nsIURI> deserializedURI = DeserializeURI(aURI);
+ if (!deserializedURI) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ nsCOMPtr<nsILoadInfo> deserializedLoadInfo;
+ nsresult rv;
+ rv = LoadInfoArgsToLoadInfo(aLoadInfo, getter_AddRefs(deserializedLoadInfo));
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ RefPtr<ExtensionProtocolHandler> 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
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -231,14 +231,25 @@ protected:
const ipc::OptionalURIParams& aSourceURI,
const PredictorPredictReason& aReason,
const OriginAttributes& aOriginAttributes) override;
virtual mozilla::ipc::IPCResult RecvPredReset() override;
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
} // namespace mozilla
#endif // mozilla_net_NeckoParent_h
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -23,16 +23,17 @@ include protocol PDataChannel;
include protocol PTransportProvider;
include protocol PChildToParentStream; //FIXME: bug #792908
include protocol PParentToChildStream; //FIXME: bug #792908
include protocol PStunAddrsRequest;
include protocol PFileChannel;
include protocol PRtspController;
include protocol PRtspChannel;
+include IPCStream;
include URIParams;
include NeckoChannelParams;
include PBrowserOrId;
include protocol PAltDataOutputStream;
using class IPC::SerializedLoadContext from "SerializedLoadContext.h";
using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
@@ -122,16 +123,24 @@ parent:
async RemoveRequestContext(uint64_t rcid);
async PAltDataOutputStream(nsCString type, PHttpChannel channel);
async PStunAddrsRequest();
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.
* NestedFrameId is the id corresponding to the PBrowser. It is the same id
* that was passed to the PBrowserOrId param in to the PHttpChannel constructor
*/
async AsyncAuthPromptForNestedFrame(TabId nestedFrameId, nsCString uri,
nsString realm, uint64_t callbackId);
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -1,39 +1,359 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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 "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>
+{
+ nsresult mErrorValue;
+
+ template<typename V, typename E2> friend class Result;
+
+public:
+ explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {}
+
+ operator nsresult() { return mErrorValue; }
+};
+
namespace net {
using extensions::URLInfo;
+StaticRefPtr<ExtensionProtocolHandler> ExtensionProtocolHandler::sSingleton;
+
+static inline Result<Ok, nsresult>
+WrapNSResult(PRStatus aRv)
+{
+ if (aRv != PR_SUCCESS) {
+ return Err(NS_ERROR_FAILURE);
+ }
+ return Ok();
+}
+
+static inline Result<Ok, nsresult>
+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<ExtensionStreamGetter>
+{
+ 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<nsIJARChannel>&& 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<Ok, nsresult> 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<nsIURI> mURI;
+ nsCOMPtr<nsILoadInfo> mLoadInfo;
+ nsCOMPtr<nsIJARChannel> mJarChannel;
+ nsCOMPtr<nsIFile> mJarFile;
+ nsCOMPtr<nsIStreamListener> mListener;
+ nsCOMPtr<nsIChannel> 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<nsILocalFileWin> 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<nsIRunnable> 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<nsIFile> 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<Ok, nsresult>
+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<ExtensionStreamGetter> 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<nsIInputStream> 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<nsIStreamListener> 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<nsIInputStreamPump> 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<nsIStreamListener> listener = mListener.forget();
+
+ RefPtr<FileDescriptorFile> 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>
+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()
{
return ExtensionPolicyService::GetSingleton();
}
nsresult
ExtensionProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags)
@@ -72,36 +392,68 @@ ExtensionProtocolHandler::ResolveSpecial
if (aPathname.EqualsLiteral("/_generated_background_page.html")) {
Unused << EPS().GetGeneratedBackgroundPageUrl(aHost, aResult);
return !aResult.IsEmpty();
}
return false;
}
-static inline Result<Ok, nsresult>
-WrapNSResult(nsresult aRv)
+// For file or JAR URI's, substitute in a remote channel.
+Result<Ok, nsresult>
+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,
nsIChannel** result)
{
nsresult rv;
nsCOMPtr<nsIURL> 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);
if (!ext.LowerCaseEqualsLiteral("css")) {
return NS_OK;
}
@@ -141,12 +493,247 @@ ExtensionProtocolHandler::SubstituteChan
(*result)->SetLoadInfo(loadInfo);
}
channel.swap(*result);
return NS_OK;
}
+Result<nsCOMPtr<nsIInputStream>, 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<nsIURI> baseURI;
+ NS_TRY(GetSubstitution(host, getter_AddRefs(baseURI)));
+
+ // The result should be a file URL for the extension base dir
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(baseURI, &rv);
+ NS_TRY(rv);
+
+ nsCOMPtr<nsIFile> 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<nsIPrincipal> childPrincipal;
+ NS_TRY(aChildLoadInfo->GetLoadingPrincipal(getter_AddRefs(childPrincipal)));
+ if (nsContentUtils::IsSystemPrincipal(childPrincipal)) {
+ return Err(NS_ERROR_FILE_ACCESS_DENIED);
+ }
+
+ nsCOMPtr<nsIChannel> 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<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv);
+ NS_TRY(rv);
+
+ nsCOMPtr<nsIFile> 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<nsIInputStream> inputStream;
+ NS_TRY(NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
+ requestedFile,
+ PR_RDONLY,
+ -1,
+ nsIFileInputStream::DEFER_OPEN));
+
+ return inputStream;
+}
+
+Result<Ok, nsresult>
+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<nsIURI> subURI;
+ NS_TRY(GetSubstitution(host, getter_AddRefs(subURI)));
+
+ nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(subURI, &rv);
+ NS_TRY(rv);
+
+ nsCOMPtr<nsIURI> innerFileURI;
+ NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI)));
+
+ nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
+ NS_TRY(rv);
+
+ nsCOMPtr<nsIFile> jarFile;
+ NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile)));
+
+ if (!mFileOpenerThread) {
+ mFileOpenerThread =
+ new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
+ NS_LITERAL_CSTRING("ExtensionProtocolHandler"));
+ }
+
+ RefPtr<ExtensionJARFileOpener> fileOpener =
+ new ExtensionJARFileOpener(jarFile, aResolve);
+
+ nsCOMPtr<nsIRunnable> 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<nsIChannel> 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<ExtensionStreamGetter> streamGetter =
+ new ExtensionStreamGetter(aURI, aLoadinfo);
+
+ NewSimpleChannel(aURI, aLoadinfo, streamGetter, aRetVal);
+}
+
+Result<Ok, nsresult>
+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<nsIURI> uri;
+ NS_TRY(NS_NewURI(getter_AddRefs(uri), aResolvedSpec));
+
+ nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv);
+ NS_TRY(rv);
+
+ nsCOMPtr<nsIURI> innerFileURI;
+ NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI)));
+
+ nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
+ NS_TRY(rv);
+
+ nsCOMPtr<nsIFile> jarFile;
+ NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile)));
+
+ nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(*aRetVal, &rv);
+ NS_TRY(rv);
+
+ RefPtr<ExtensionStreamGetter> streamGetter =
+ new ExtensionStreamGetter(aURI, aLoadinfo, jarChannel.forget(), jarFile);
+
+ NewSimpleChannel(aURI, aLoadinfo, streamGetter, aRetVal);
+ return Ok();
+}
+
#undef NS_TRY
} // namespace net
} // namespace mozilla
--- a/netwerk/protocol/res/ExtensionProtocolHandler.h
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.h
@@ -1,48 +1,167 @@
/* -*- 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 ExtensionProtocolHandler_h___
#define ExtensionProtocolHandler_h___
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/LazyIdleThread.h"
#include "SubstitutingProtocolHandler.h"
-#include "nsWeakReference.h"
namespace mozilla {
namespace net {
class ExtensionProtocolHandler final : public nsISubstitutingProtocolHandler,
public nsIProtocolHandlerWithDynamicFlags,
public SubstitutingProtocolHandler,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIPROTOCOLHANDLERWITHDYNAMICFLAGS
NS_FORWARD_NSIPROTOCOLHANDLER(SubstitutingProtocolHandler::)
NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(SubstitutingProtocolHandler::)
- ExtensionProtocolHandler() : SubstitutingProtocolHandler("moz-extension") {}
+ static already_AddRefed<ExtensionProtocolHandler> 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<nsCOMPtr<nsIInputStream>, 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<Ok, nsresult> 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,
nsACString& aResult) override;
// |result| is an inout param. On entry to this function, *result
// is expected to be non-null and already addrefed. This function
// may release the object stored in *result on entry and write
// a new pointer to an already addrefed channel to *result.
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<Ok, nsresult> 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<Ok, nsresult> 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<mozilla::LazyIdleThread> mFileOpenerThread;
+
+ // To allow parent IPDL actors to invoke methods on this handler when
+ // handling moz-extension requests from the child.
+ static StaticRefPtr<ExtensionProtocolHandler> 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
} // namespace mozilla
#endif /* ExtensionProtocolHandler_h___ */
--- a/netwerk/protocol/res/moz.build
+++ b/netwerk/protocol/res/moz.build
@@ -6,16 +6,21 @@
XPIDL_SOURCES += [
'nsIResProtocolHandler.idl',
'nsISubstitutingProtocolHandler.idl',
]
XPIDL_MODULE = 'necko_res'
+EXPORTS.mozilla.net += [
+ 'ExtensionProtocolHandler.h',
+ 'SubstitutingProtocolHandler.h',
+]
+
UNIFIED_SOURCES += [
'ExtensionProtocolHandler.cpp',
'nsResProtocolHandler.cpp',
'SubstitutingProtocolHandler.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
new file mode 100644
--- /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<FileDescriptorFile> 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
new file mode 100644
--- /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<nsIFile> mFile;
+ FileDescriptor mFD;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // _FileDescriptorFile_h
--- a/xpcom/io/moz.build
+++ b/xpcom/io/moz.build
@@ -56,16 +56,17 @@ else:
EXPORTS += ['nsLocalFileUnix.h']
SOURCES += [
'nsLocalFileUnix.cpp',
]
XPIDL_MODULE = 'xpcom_io'
EXPORTS += [
+ 'FileDescriptorFile.h',
'nsAnonymousTemporaryFile.h',
'nsAppDirectoryServiceDefs.h',
'nsDirectoryService.h',
'nsDirectoryServiceAtomList.h',
'nsDirectoryServiceDefs.h',
'nsDirectoryServiceUtils.h',
'nsEscape.h',
'nsLinebreakConverter.h',
@@ -87,16 +88,17 @@ EXPORTS.mozilla += [
'SnappyCompressOutputStream.h',
'SnappyFrameUtils.h',
'SnappyUncompressInputStream.h',
]
UNIFIED_SOURCES += [
'Base64.cpp',
'crc32c.c',
+ 'FileDescriptorFile.cpp',
'nsAnonymousTemporaryFile.cpp',
'nsAppFileLocationProvider.cpp',
'nsBinaryStream.cpp',
'nsDirectoryService.cpp',
'nsEscape.cpp',
'nsInputStreamTee.cpp',
'nsIOUtil.cpp',
'nsLinebreakConverter.cpp',