diff --git a/dom/ipc/Makefile.in b/dom/ipc/Makefile.in index 8dca179ca4b6..cb8ae5d91c8e 100644 --- a/dom/ipc/Makefile.in +++ b/dom/ipc/Makefile.in @@ -22,7 +22,10 @@ ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT)) TEST_DIRS += tests endif -EXPORTS = PCOMContentPermissionRequestChild.h +EXPORTS = \ + nsICachedFileDescriptorListener.h \ + PCOMContentPermissionRequestChild.h \ + $(NULL) EXPORTS_NAMESPACES = \ mozilla \ diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index a7270b93d557..0935ed30cfe9 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -31,6 +31,7 @@ using gfxSize; using mozilla::layers::LayersBackend; using mozilla::layers::FrameMetrics; using mozilla::layout::ScrollingBehavior; +using mozilla::void_t; using mozilla::WindowsHandle; using nscolor; using nsCompositionEvent; @@ -290,6 +291,8 @@ child: LoadURL(nsCString uri); + CacheFileDescriptor(nsString path, FileDescriptor fd); + UpdateDimensions(nsRect rect, nsIntSize size, ScreenOrientation orientation) compress; UpdateFrame(FrameMetrics frame) compress; diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 0897c0478330..f2ea3441d8ba 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -18,6 +18,7 @@ #include "mozilla/dom/PContentChild.h" #include "mozilla/dom/PContentDialogChild.h" #include "mozilla/ipc/DocumentRendererChild.h" +#include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/layers/AsyncPanZoomController.h" #include "mozilla/layers/CompositorChild.h" #include "mozilla/layers/PLayersChild.h" @@ -37,6 +38,7 @@ #include "mozilla/dom/Element.h" #include "nsIAppsService.h" #include "nsIBaseWindow.h" +#include "nsICachedFileDescriptorListener.h" #include "nsIComponentManager.h" #include "nsIDocumentInlines.h" #include "nsIDOMClassInfo.h" @@ -121,6 +123,98 @@ public: const InfallibleTArray& aStringParams); }; +class TabChild::CachedFileDescriptorInfo +{ + struct PathOnlyComparatorHelper + { + bool Equals(const nsAutoPtr& a, + const CachedFileDescriptorInfo& b) const + { + return a->mPath == b.mPath; + } + }; + + struct PathAndCallbackComparatorHelper + { + bool Equals(const nsAutoPtr& a, + const CachedFileDescriptorInfo& b) const + { + return a->mPath == b.mPath && + a->mCallback == b.mCallback; + } + }; + +public: + nsString mPath; + FileDescriptor mFileDescriptor; + nsCOMPtr mCallback; + bool mCanceled; + + CachedFileDescriptorInfo(const nsAString& aPath) + : mPath(aPath), mCanceled(false) + { } + + CachedFileDescriptorInfo(const nsAString& aPath, + const FileDescriptor& aFileDescriptor) + : mPath(aPath), mFileDescriptor(aFileDescriptor), mCanceled(false) + { } + + CachedFileDescriptorInfo(const nsAString& aPath, + nsICachedFileDescriptorListener* aCallback) + : mPath(aPath), mCallback(aCallback), mCanceled(false) + { } + + PathOnlyComparatorHelper PathOnlyComparator() const + { + return PathOnlyComparatorHelper(); + } + + PathAndCallbackComparatorHelper PathAndCallbackComparator() const + { + return PathAndCallbackComparatorHelper(); + } + + void FireCallback() const + { + mCallback->OnCachedFileDescriptor(mPath, mFileDescriptor); + } +}; + +class TabChild::CachedFileDescriptorCallbackRunnable : public nsRunnable +{ + typedef TabChild::CachedFileDescriptorInfo CachedFileDescriptorInfo; + + nsAutoPtr mInfo; + +public: + CachedFileDescriptorCallbackRunnable(CachedFileDescriptorInfo* aInfo) + : mInfo(aInfo) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aInfo); + MOZ_ASSERT(!aInfo->mPath.IsEmpty()); + MOZ_ASSERT(aInfo->mCallback); + } + + void Dispatch() + { + MOZ_ASSERT(NS_IsMainThread()); + + nsresult rv = NS_DispatchToCurrentThread(this); + NS_ENSURE_SUCCESS_VOID(rv); + } + +private: + NS_IMETHOD Run() + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mInfo); + + mInfo->FireCallback(); + return NS_OK; + } +}; + StaticRefPtr sPreallocatedTab; /*static*/ void @@ -1117,6 +1211,130 @@ TabChild::RecvLoadURL(const nsCString& uri) return true; } +bool +TabChild::RecvCacheFileDescriptor(const nsString& aPath, + const FileDescriptor& aFileDescriptor) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aPath.IsEmpty()); + + // aFileDescriptor may be invalid here, but the callback will choose how to + // handle it. + + // First see if we already have a request for this path. + const CachedFileDescriptorInfo search(aPath); + uint32_t index = + mCachedFileDescriptorInfos.IndexOf(search, 0, + search.PathOnlyComparator()); + if (index == mCachedFileDescriptorInfos.NoIndex) { + // We haven't had any requests for this path yet. Assume that we will + // in a little while and save the file descriptor here. + mCachedFileDescriptorInfos.AppendElement( + new CachedFileDescriptorInfo(aPath, aFileDescriptor)); + return true; + } + + nsAutoPtr& info = + mCachedFileDescriptorInfos[index]; + + MOZ_ASSERT(info); + MOZ_ASSERT(info->mPath == aPath); + MOZ_ASSERT(!info->mFileDescriptor.IsValid()); + MOZ_ASSERT(info->mCallback); + + // If this callback has been canceled then we can simply close the file + // descriptor and forget about the callback. + if (info->mCanceled) { + // Only close if this is a valid file descriptor. + if (aFileDescriptor.IsValid()) { + nsRefPtr runnable = + new CloseFileRunnable(aFileDescriptor); + runnable->Dispatch(); + } + } else { + // Not canceled so fire the callback. + info->mFileDescriptor = aFileDescriptor; + + // We don't need a runnable here because we should already be at the top + // of the event loop. Just fire immediately. + info->FireCallback(); + } + + mCachedFileDescriptorInfos.RemoveElementAt(index); + return true; +} + +bool +TabChild::GetCachedFileDescriptor(const nsAString& aPath, + nsICachedFileDescriptorListener* aCallback) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aPath.IsEmpty()); + MOZ_ASSERT(aCallback); + + // First see if we've already received a cached file descriptor for this + // path. + const CachedFileDescriptorInfo search(aPath); + uint32_t index = + mCachedFileDescriptorInfos.IndexOf(search, 0, + search.PathOnlyComparator()); + if (index == mCachedFileDescriptorInfos.NoIndex) { + // We haven't received a file descriptor for this path yet. Assume that + // we will in a little while and save the request here. + mCachedFileDescriptorInfos.AppendElement( + new CachedFileDescriptorInfo(aPath, aCallback)); + return false; + } + + nsAutoPtr& info = + mCachedFileDescriptorInfos[index]; + + MOZ_ASSERT(info); + MOZ_ASSERT(info->mPath == aPath); + MOZ_ASSERT(!info->mCallback); + MOZ_ASSERT(!info->mCanceled); + + info->mCallback = aCallback; + + nsRefPtr runnable = + new CachedFileDescriptorCallbackRunnable(info.forget()); + runnable->Dispatch(); + + mCachedFileDescriptorInfos.RemoveElementAt(index); + return true; +} + +void +TabChild::CancelCachedFileDescriptorCallback( + const nsAString& aPath, + nsICachedFileDescriptorListener* aCallback) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aPath.IsEmpty()); + MOZ_ASSERT(aCallback); + + const CachedFileDescriptorInfo search(aPath, aCallback); + uint32_t index = + mCachedFileDescriptorInfos.IndexOf(search, 0, + search.PathAndCallbackComparator()); + if (index == mCachedFileDescriptorInfos.NoIndex) { + // Nothing to do here. + return; + } + + nsAutoPtr& info = + mCachedFileDescriptorInfos[index]; + + MOZ_ASSERT(info); + MOZ_ASSERT(info->mPath == aPath); + MOZ_ASSERT(!info->mFileDescriptor.IsValid()); + MOZ_ASSERT(info->mCallback == aCallback); + MOZ_ASSERT(!info->mCanceled); + + // Set this flag so that we will close the file descriptor when it arrives. + info->mCanceled = true; +} + void TabChild::DoFakeShow() { diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 07701437fa68..cb388f6c3a03 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -54,6 +54,7 @@ #include "mozilla/dom/TabContext.h" struct gfxMatrix; +class nsICachedFileDescriptorListener; namespace mozilla { namespace layout { @@ -196,6 +197,9 @@ public: const mozilla::dom::StructuredCloneData& aData); virtual bool RecvLoadURL(const nsCString& uri); + virtual bool RecvCacheFileDescriptor(const nsString& aPath, + const FileDescriptor& aFileDescriptor) + MOZ_OVERRIDE; virtual bool RecvShow(const nsIntSize& size); virtual bool RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size, const ScreenOrientation& orientation); virtual bool RecvUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics); @@ -317,6 +321,15 @@ public: */ void GetAppType(nsAString& aAppType) const { aAppType = mAppType; } + // Returns true if the file descriptor was found in the cache, false + // otherwise. + bool GetCachedFileDescriptor(const nsAString& aPath, + nsICachedFileDescriptorListener* aCallback); + + void CancelCachedFileDescriptorCallback( + const nsAString& aPath, + nsICachedFileDescriptorListener* aCallback); + protected: virtual PRenderFrameChild* AllocPRenderFrame(ScrollingBehavior* aScrolling, LayersBackend* aBackend, @@ -412,6 +425,9 @@ private: return utils; } + class CachedFileDescriptorInfo; + class CachedFileDescriptorCallbackRunnable; + nsCOMPtr mWebNav; nsCOMPtr mWidget; nsCOMPtr mLastURI; @@ -430,6 +446,9 @@ private: // the touch we're tracking. That is, if touchend or a touchmove // that exceeds the gesture threshold doesn't happen. CancelableTask* mTapHoldTimer; + // At present only 1 of these is really expected. + nsAutoTArray, 1> + mCachedFileDescriptorInfos; float mOldViewportWidth; nscolor mLastBackgroundColor; ScrollingBehavior mScrolling; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 4404fc694bc0..e5a81a75ba1e 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -50,6 +50,7 @@ #include "nsSerializationHelper.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" +#include "private/pprio.h" #include "StructuredCloneUtils.h" #include "TabChild.h" #include @@ -66,6 +67,116 @@ using namespace mozilla::dom::indexedDB; // from the ones registered by webProgressListeners. #define NOTIFY_FLAG_SHIFT 16 +class OpenFileAndSendFDRunnable : public nsRunnable +{ + const nsString mPath; + nsRefPtr mTabParent; + nsCOMPtr mEventTarget; + PRFileDesc* mFD; + +public: + OpenFileAndSendFDRunnable(const nsAString& aPath, TabParent* aTabParent) + : mPath(aPath), mTabParent(aTabParent), mFD(nullptr) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aPath.IsEmpty()); + MOZ_ASSERT(aTabParent); + } + + void Dispatch() + { + MOZ_ASSERT(NS_IsMainThread()); + + mEventTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + NS_ENSURE_TRUE_VOID(mEventTarget); + + nsresult rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS_VOID(rv); + } + +private: + ~OpenFileAndSendFDRunnable() + { + MOZ_ASSERT(!mFD); + } + + // This shouldn't be called directly except by the event loop. Use Dispatch + // to start the sequence. + NS_IMETHOD Run() + { + if (NS_IsMainThread()) { + SendResponse(); + } else if (mFD) { + CloseFile(); + } else { + OpenFile(); + } + + return NS_OK; + } + + void SendResponse() + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mTabParent); + MOZ_ASSERT(mEventTarget); + MOZ_ASSERT(mFD); + + nsRefPtr tabParent; + mTabParent.swap(tabParent); + + FileDescriptor::PlatformHandleType handle = + FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFD)); + + mozilla::unused << tabParent->SendCacheFileDescriptor(mPath, handle); + + nsCOMPtr eventTarget; + mEventTarget.swap(eventTarget); + + if (NS_FAILED(eventTarget->Dispatch(this, NS_DISPATCH_NORMAL))) { + NS_WARNING("Failed to dispatch to stream transport service!"); + + // It's probably safer to take the main thread IO hit here rather + // than leak a file descriptor. + CloseFile(); + } + } + + void OpenFile() + { + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!mFD); + + nsCOMPtr file; + nsresult rv = NS_NewLocalFile(mPath, false, getter_AddRefs(file)); + NS_ENSURE_SUCCESS_VOID(rv); + + PRFileDesc* fd; + rv = file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); + NS_ENSURE_SUCCESS_VOID(rv); + + mFD = fd; + + if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { + NS_WARNING("Failed to dispatch to main thread!"); + + CloseFile(); + } + } + + void CloseFile() + { + // It's possible for this to happen on the main thread if the dispatch + // to the stream service fails after we've already opened the file so + // we can't assert the thread we're running on. + + MOZ_ASSERT(mFD); + + PR_Close(mFD); + mFD = nullptr; + } +}; + namespace mozilla { namespace dom { @@ -93,6 +204,7 @@ TabParent::TabParent(const TabContext& aContext) , mUpdatedDimensions(false) , mMarkedDestroying(false) , mIsDestroyed(false) + , mAppPackageFileDescriptorSent(false) { } @@ -230,23 +342,66 @@ TabParent::AnswerCreateWindow(PBrowserParent** retval) void TabParent::LoadURL(nsIURI* aURI) { + MOZ_ASSERT(aURI); + if (mIsDestroyed) { - return; - } - if (!mShown) { - nsAutoCString spec; - if (aURI) { - aURI->GetSpec(spec); - } - NS_WARNING(nsPrintfCString("TabParent::LoadURL(%s) called before " - "Show(). Ignoring LoadURL.\n", spec.get()).get()); - return; + return; } nsCString spec; aURI->GetSpec(spec); + if (!mShown) { + NS_WARNING(nsPrintfCString("TabParent::LoadURL(%s) called before " + "Show(). Ignoring LoadURL.\n", + spec.get()).get()); + return; + } + unused << SendLoadURL(spec); + + // If this app is a packaged app then we can speed startup by sending over + // the file descriptor for the "application.zip" file that it will + // invariably request. Only do this once. + if (!mAppPackageFileDescriptorSent) { + mAppPackageFileDescriptorSent = true; + + nsCOMPtr app = GetOwnOrContainingApp(); + if (app) { + nsString manifestURL; + nsresult rv = app->GetManifestURL(manifestURL); + NS_ENSURE_SUCCESS_VOID(rv); + + if (StringBeginsWith(manifestURL, NS_LITERAL_STRING("app:"))) { + nsString basePath; + rv = app->GetBasePath(basePath); + NS_ENSURE_SUCCESS_VOID(rv); + + nsString appId; + rv = app->GetId(appId); + NS_ENSURE_SUCCESS_VOID(rv); + + nsCOMPtr packageFile; + rv = NS_NewLocalFile(basePath, false, + getter_AddRefs(packageFile)); + NS_ENSURE_SUCCESS_VOID(rv); + + rv = packageFile->Append(appId); + NS_ENSURE_SUCCESS_VOID(rv); + + rv = packageFile->Append(NS_LITERAL_STRING("application.zip")); + NS_ENSURE_SUCCESS_VOID(rv); + + nsString path; + rv = packageFile->GetPath(path); + NS_ENSURE_SUCCESS_VOID(rv); + + nsRefPtr openFileRunnable = + new OpenFileAndSendFDRunnable(path, this); + openFileRunnable->Dispatch(); + } + } + } } void diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index e0144b1b4c83..f2e809ab2370 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -304,6 +304,8 @@ private: // When true, the TabParent is invalid and we should not send IPC messages // anymore. bool mIsDestroyed; + // Whether we have already sent a FileDescriptor for the app package. + bool mAppPackageFileDescriptorSent; }; } // namespace dom diff --git a/dom/ipc/nsICachedFileDescriptorListener.h b/dom/ipc/nsICachedFileDescriptorListener.h new file mode 100644 index 000000000000..d71cc36ba82c --- /dev/null +++ b/dom/ipc/nsICachedFileDescriptorListener.h @@ -0,0 +1,38 @@ +/* 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 mozilla_dom_ipc_nsICachedFileDescriptorListener_h +#define mozilla_dom_ipc_nsICachedFileDescriptorListener_h + +#include "nsISupports.h" + +#ifndef NS_NO_VTABLE +#define NS_NO_VTABLE +#endif + +#define NS_ICACHEDFILEDESCRIPTORLISTENER_IID \ + {0x2cedaee0, 0x6ef2, 0x4f60, {0x9a, 0x6c, 0xdf, 0x4e, 0x4d, 0x65, 0x6a, 0xf7}} + +class nsAString; + +namespace mozilla { +namespace ipc { +class FileDescriptor; +} +} + +class NS_NO_VTABLE nsICachedFileDescriptorListener : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICACHEDFILEDESCRIPTORLISTENER_IID) + + virtual void + OnCachedFileDescriptor(const nsAString& aPath, + const mozilla::ipc::FileDescriptor& aFD) = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsICachedFileDescriptorListener, + NS_ICACHEDFILEDESCRIPTORLISTENER_IID) + +#endif // mozilla_dom_ipc_nsICachedFileDescriptorListener_h diff --git a/ipc/glue/FileDescriptor.cpp b/ipc/glue/FileDescriptor.cpp index c5dce360e9aa..ba55e61859d0 100644 --- a/ipc/glue/FileDescriptor.cpp +++ b/ipc/glue/FileDescriptor.cpp @@ -42,7 +42,8 @@ FileDescriptor::FileDescriptor(PlatformHandleType aHandle) void FileDescriptor::DuplicateInCurrentProcess(PlatformHandleType aHandle) { - MOZ_ASSERT(!mHandleCreatedByOtherProcess); + MOZ_ASSERT_IF(mHandleCreatedByOtherProcess, + mHandleCreatedByOtherProcessWasUsed); if (IsValid(aHandle)) { PlatformHandleType newHandle; diff --git a/ipc/glue/FileDescriptor.h b/ipc/glue/FileDescriptor.h index 879b9920ee73..0c78974180da 100644 --- a/ipc/glue/FileDescriptor.h +++ b/ipc/glue/FileDescriptor.h @@ -53,7 +53,9 @@ public: FileDescriptor(const FileDescriptor& aOther) { - *this = aOther; + // Don't use operator= here because that will call + // CloseCurrentProcessHandle() on this (uninitialized) object. + Assign(aOther); } FileDescriptor(PlatformHandleType aHandle); @@ -77,18 +79,7 @@ public: operator=(const FileDescriptor& aOther) { CloseCurrentProcessHandle(); - - if (aOther.mHandleCreatedByOtherProcess) { - mHandleCreatedByOtherProcess = true; - mHandleCreatedByOtherProcessWasUsed = - aOther.mHandleCreatedByOtherProcessWasUsed; - mHandle = aOther.PlatformHandle(); - } else { - DuplicateInCurrentProcess(aOther.PlatformHandle()); - mHandleCreatedByOtherProcess = false; - mHandleCreatedByOtherProcessWasUsed = false; - } - + Assign(aOther); return *this; } @@ -122,6 +113,21 @@ public: } private: + void + Assign(const FileDescriptor& aOther) + { + if (aOther.mHandleCreatedByOtherProcess) { + mHandleCreatedByOtherProcess = true; + mHandleCreatedByOtherProcessWasUsed = + aOther.mHandleCreatedByOtherProcessWasUsed; + mHandle = aOther.PlatformHandle(); + } else { + DuplicateInCurrentProcess(aOther.PlatformHandle()); + mHandleCreatedByOtherProcess = false; + mHandleCreatedByOtherProcessWasUsed = false; + } + } + static bool IsValid(PlatformHandleType aHandle); diff --git a/ipc/glue/FileDescriptorUtils.cpp b/ipc/glue/FileDescriptorUtils.cpp new file mode 100644 index 000000000000..150354c2ba43 --- /dev/null +++ b/ipc/glue/FileDescriptorUtils.cpp @@ -0,0 +1,81 @@ +/* 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 "FileDescriptorUtils.h" + +#include "nsIEventTarget.h" + +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "prio.h" +#include "private/pprio.h" + +using mozilla::ipc::CloseFileRunnable; + +#ifdef DEBUG + +CloseFileRunnable::CloseFileRunnable(const FileDescriptor& aFileDescriptor) +: mFileDescriptor(aFileDescriptor) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aFileDescriptor.IsValid()); +} + +#endif // DEBUG + +CloseFileRunnable::~CloseFileRunnable() +{ + if (mFileDescriptor.IsValid()) { + // It's probably safer to take the main thread IO hit here rather than leak + // the file descriptor. + CloseFile(); + } +} + +NS_IMPL_THREADSAFE_ISUPPORTS1(CloseFileRunnable, nsIRunnable) + +void +CloseFileRunnable::Dispatch() +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr eventTarget = + do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + NS_ENSURE_TRUE_VOID(eventTarget); + + nsresult rv = eventTarget->Dispatch(this, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS_VOID(rv); +} + +void +CloseFileRunnable::CloseFile() +{ + // It's possible for this to happen on the main thread if the dispatch to the + // stream service fails so we can't assert the thread on which we're running. + + MOZ_ASSERT(mFileDescriptor.IsValid()); + + PRFileDesc* fd = + PR_ImportFile(PROsfd(mFileDescriptor.PlatformHandle())); + NS_WARN_IF_FALSE(fd, "Failed to import file handle!"); + + mFileDescriptor = FileDescriptor(); + + if (fd) { + PR_Close(fd); + fd = nullptr; + } +} + +NS_IMETHODIMP +CloseFileRunnable::Run() +{ + MOZ_ASSERT(!NS_IsMainThread()); + + CloseFile(); + return NS_OK; +} diff --git a/ipc/glue/FileDescriptorUtils.h b/ipc/glue/FileDescriptorUtils.h new file mode 100644 index 000000000000..9ec6efce5a30 --- /dev/null +++ b/ipc/glue/FileDescriptorUtils.h @@ -0,0 +1,47 @@ + +/* 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 mozilla_ipc_FileDescriptorUtils_h +#define mozilla_ipc_FileDescriptorUtils_h + +#include "mozilla/ipc/FileDescriptor.h" +#include "nsIRunnable.h" + +namespace mozilla { +namespace ipc { + +// When Dispatch() is called (from main thread) this class arranges to close the +// provided FileDescriptor on one of the socket transport service threads (to +// avoid main thread I/O). +class CloseFileRunnable : public nsIRunnable +{ + typedef mozilla::ipc::FileDescriptor FileDescriptor; + + FileDescriptor mFileDescriptor; + +public: + CloseFileRunnable(const FileDescriptor& aFileDescriptor) +#ifdef DEBUG + ; +#else + : mFileDescriptor(aFileDescriptor) + { } +#endif + + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + + void Dispatch(); + +private: + ~CloseFileRunnable(); + + void CloseFile(); +}; + +} // namespace ipc +} // namespace mozilla + +#endif // mozilla_ipc_FileDescriptorUtils_h diff --git a/ipc/glue/Makefile.in b/ipc/glue/Makefile.in index cfb4418c63c4..542ccc0ffe90 100644 --- a/ipc/glue/Makefile.in +++ b/ipc/glue/Makefile.in @@ -26,6 +26,7 @@ EXPORTS_mozilla/ipc = \ BrowserProcessSubThread.h \ CrossProcessMutex.h \ FileDescriptor.h \ + FileDescriptorUtils.h \ GeckoChildProcessHost.h \ InputStreamUtils.h \ IOThreadChild.h \ @@ -71,6 +72,7 @@ CPPSRCS += \ AsyncChannel.cpp \ BrowserProcessSubThread.cpp \ FileDescriptor.cpp \ + FileDescriptorUtils.cpp \ GeckoChildProcessHost.cpp \ InputStreamUtils.cpp \ MessagePump.cpp \ diff --git a/modules/libjar/nsJARChannel.cpp b/modules/libjar/nsJARChannel.cpp index c6eb053e71c1..22b483a8f6cf 100644 --- a/modules/libjar/nsJARChannel.cpp +++ b/modules/libjar/nsJARChannel.cpp @@ -18,7 +18,6 @@ #include "nsIScriptSecurityManager.h" #include "nsIPrincipal.h" #include "nsIFileURL.h" -#include "nsXULAppAPI.h" #include "mozilla/Preferences.h" #include "mozilla/net/RemoteOpenFileChild.h" @@ -349,9 +348,9 @@ nsJARChannel::LookupFile() } // if we're in child process and have special "remoteopenfile:://" scheme, // create special nsIFile that gets file handle from parent when opened. - if (!mJarFile && XRE_GetProcessType() != GeckoProcessType_Default) { + if (!mJarFile && !gJarHandler->IsMainProcess()) { nsAutoCString scheme; - nsresult rv = mJarBaseURI->GetScheme(scheme); + rv = mJarBaseURI->GetScheme(scheme); if (NS_SUCCEEDED(rv) && scheme.EqualsLiteral("remoteopenfile")) { nsRefPtr remoteFile = new RemoteOpenFileChild(); rv = remoteFile->Init(mJarBaseURI); @@ -369,13 +368,20 @@ nsJARChannel::LookupFile() } } + mOpeningRemote = true; + + if (gJarHandler->RemoteOpenFileInProgress(remoteFile, this)) { + // JarHandler will trigger OnRemoteFileOpen() after the first + // request for this file completes and we'll get a JAR cache + // hit. + return NS_OK; + } + // Open file on parent: OnRemoteFileOpenComplete called when done nsCOMPtr tabChild; NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, tabChild); rv = remoteFile->AsyncRemoteFileOpen(PR_RDONLY, this, tabChild.get()); NS_ENSURE_SUCCESS(rv, rv); - - mOpeningRemote = true; } } // try to handle a nested jar @@ -397,6 +403,38 @@ nsJARChannel::LookupFile() return rv; } +nsresult +nsJARChannel::OpenLocalFile() +{ + MOZ_ASSERT(mIsPending); + + // Local files are always considered safe. + mIsUnsafe = false; + + nsRefPtr input; + nsresult rv = CreateJarInput(gJarHandler->JarCache(), + getter_AddRefs(input)); + if (NS_SUCCEEDED(rv)) { + // Create input stream pump and call AsyncRead as a block. + rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input); + if (NS_SUCCEEDED(rv)) + rv = mPump->AsyncRead(this, nullptr); + } + + return rv; +} + +void +nsJARChannel::NotifyError(nsresult aError) +{ + MOZ_ASSERT(NS_FAILED(aError)); + + mStatus = aError; + + OnStartRequest(nullptr, nullptr); + OnStopRequest(nullptr, nullptr, aError); +} + //----------------------------------------------------------------------------- // nsIRequest //----------------------------------------------------------------------------- @@ -749,17 +787,7 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) } else if (mOpeningRemote) { // nothing to do: already asked parent to open file. } else { - // local files are always considered safe - mIsUnsafe = false; - - nsRefPtr input; - rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input)); - if (NS_SUCCEEDED(rv)) { - // create input stream pump and call AsyncRead as a block - rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input); - if (NS_SUCCEEDED(rv)) - rv = mPump->AsyncRead(this, nullptr); - } + rv = OpenLocalFile(); } if (NS_FAILED(rv)) { @@ -902,9 +930,7 @@ nsJARChannel::OnDownloadComplete(nsIDownloader *downloader, } if (NS_FAILED(status)) { - mStatus = status; - OnStartRequest(nullptr, nullptr); - OnStopRequest(nullptr, nullptr, status); + NotifyError(status); } return NS_OK; @@ -918,30 +944,19 @@ nsJARChannel::OnRemoteFileOpenComplete(nsresult aOpenStatus) { nsresult rv = aOpenStatus; - if (NS_SUCCEEDED(rv)) { - // files on parent are always considered safe - mIsUnsafe = false; - - nsRefPtr input; - rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input)); - if (NS_SUCCEEDED(rv)) { - // create input stream pump and call AsyncRead as a block - rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input); - if (NS_SUCCEEDED(rv)) - rv = mPump->AsyncRead(this, nullptr); - } + // NS_ERROR_ALREADY_OPENED here means we'll hit JAR cache in + // OpenLocalFile(). + if (NS_SUCCEEDED(rv) || rv == NS_ERROR_ALREADY_OPENED) { + rv = OpenLocalFile(); } if (NS_FAILED(rv)) { - mStatus = rv; - OnStartRequest(nullptr, nullptr); - OnStopRequest(nullptr, nullptr, mStatus); + NotifyError(rv); } return NS_OK; } - //----------------------------------------------------------------------------- // nsIStreamListener //----------------------------------------------------------------------------- diff --git a/modules/libjar/nsJARChannel.h b/modules/libjar/nsJARChannel.h index baddcd54f77e..d155ccbde43e 100644 --- a/modules/libjar/nsJARChannel.h +++ b/modules/libjar/nsJARChannel.h @@ -51,6 +51,8 @@ public: private: nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **); nsresult LookupFile(); + nsresult OpenLocalFile(); + void NotifyError(nsresult aError); #if defined(PR_LOGGING) nsCString mSpec; diff --git a/modules/libjar/nsJARProtocolHandler.cpp b/modules/libjar/nsJARProtocolHandler.cpp index 4df46f7b335f..c7649998b53f 100644 --- a/modules/libjar/nsJARProtocolHandler.cpp +++ b/modules/libjar/nsJARProtocolHandler.cpp @@ -17,6 +17,10 @@ #include "nsNetCID.h" #include "nsIMIMEService.h" #include "nsMimeTypes.h" +#include "nsIRemoteOpenFileListener.h" +#include "nsIHashable.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" static NS_DEFINE_CID(kZipReaderCacheCID, NS_ZIPREADERCACHE_CID); @@ -27,7 +31,13 @@ static NS_DEFINE_CID(kZipReaderCacheCID, NS_ZIPREADERCACHE_CID); nsJARProtocolHandler *gJarHandler = nullptr; nsJARProtocolHandler::nsJARProtocolHandler() +: mIsMainProcess(XRE_GetProcessType() == GeckoProcessType_Default) { + MOZ_ASSERT(NS_IsMainThread()); + + if (!mIsMainProcess) { + mRemoteFileListeners.Init(); + } } nsJARProtocolHandler::~nsJARProtocolHandler() @@ -55,6 +65,67 @@ nsJARProtocolHandler::MimeService() return mMimeService.get(); } +bool +nsJARProtocolHandler::RemoteOpenFileInProgress( + nsIHashable *aRemoteFile, + nsIRemoteOpenFileListener *aListener) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aRemoteFile); + MOZ_ASSERT(aListener); + + if (IsMainProcess()) { + MOZ_NOT_REACHED("Shouldn't be called in the main process!"); + return false; + } + + RemoteFileListenerArray *listeners; + if (mRemoteFileListeners.Get(aRemoteFile, &listeners)) { + listeners->AppendElement(aListener); + return true; + } + + // We deliberately don't put the listener in the new array since the first + // load is handled differently. + mRemoteFileListeners.Put(aRemoteFile, new RemoteFileListenerArray()); + return false; +} + +void +nsJARProtocolHandler::RemoteOpenFileComplete(nsIHashable *aRemoteFile, + nsresult aStatus) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aRemoteFile); + + if (IsMainProcess()) { + MOZ_NOT_REACHED("Shouldn't be called in the main process!"); + return; + } + + RemoteFileListenerArray *tempListeners; + if (!mRemoteFileListeners.Get(aRemoteFile, &tempListeners)) { + return; + } + + // Save the listeners in a stack array. The call to Remove() below will + // delete the tempListeners array. + RemoteFileListenerArray listeners; + tempListeners->SwapElements(listeners); + + mRemoteFileListeners.Remove(aRemoteFile); + + // Technically we must fail OnRemoteFileComplete() since OpenNSPRFileDesc() + // won't succeed here. We've trained nsJARChannel to recognize + // NS_ERROR_ALREADY_OPENED in this case as "proceed to JAR cache hit." + nsresult status = NS_SUCCEEDED(aStatus) ? NS_ERROR_ALREADY_OPENED : aStatus; + + uint32_t count = listeners.Length(); + for (uint32_t index = 0; index < count; index++) { + listeners[index]->OnRemoteFileOpenComplete(status); + } +} + NS_IMPL_THREADSAFE_ISUPPORTS3(nsJARProtocolHandler, nsIJARProtocolHandler, nsIProtocolHandler, diff --git a/modules/libjar/nsJARProtocolHandler.h b/modules/libjar/nsJARProtocolHandler.h index 83d65db6396d..1b55f8f05bb9 100644 --- a/modules/libjar/nsJARProtocolHandler.h +++ b/modules/libjar/nsJARProtocolHandler.h @@ -13,10 +13,18 @@ #include "nsIMIMEService.h" #include "nsWeakReference.h" #include "nsCOMPtr.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" + +class nsIHashable; +class nsIRemoteOpenFileListener; class nsJARProtocolHandler : public nsIJARProtocolHandler , public nsSupportsWeakReference { + typedef nsAutoTArray, 5> + RemoteFileListenerArray; + public: NS_DECL_ISUPPORTS NS_DECL_NSIPROTOCOLHANDLER @@ -34,9 +42,22 @@ public: nsIMIMEService *MimeService(); nsIZipReaderCache *JarCache() { return mJARCache; } + bool IsMainProcess() const { return mIsMainProcess; } + + bool RemoteOpenFileInProgress(nsIHashable *aRemoteFile, + nsIRemoteOpenFileListener *aListener); + void RemoteOpenFileComplete(nsIHashable *aRemoteFile, nsresult aStatus); + protected: nsCOMPtr mJARCache; nsCOMPtr mMimeService; + + // Holds lists of RemoteOpenFileChild (not including the 1st) that have + // requested the same file from parent. + nsClassHashtable + mRemoteFileListeners; + + bool mIsMainProcess; }; extern nsJARProtocolHandler *gJarHandler; diff --git a/netwerk/ipc/Makefile.in b/netwerk/ipc/Makefile.in index bac2526e2429..6dcd3c253331 100644 --- a/netwerk/ipc/Makefile.in +++ b/netwerk/ipc/Makefile.in @@ -46,6 +46,7 @@ CPPSRCS = \ LOCAL_INCLUDES += \ -I$(srcdir)/../protocol/http \ -I$(srcdir)/../base/src \ + -I$(topsrcdir)/modules/libjar \ $(NULL) include $(topsrcdir)/config/config.mk diff --git a/netwerk/ipc/RemoteOpenFileChild.cpp b/netwerk/ipc/RemoteOpenFileChild.cpp index 98644ace5322..f54e0ec616e9 100644 --- a/netwerk/ipc/RemoteOpenFileChild.cpp +++ b/netwerk/ipc/RemoteOpenFileChild.cpp @@ -4,10 +4,15 @@ * 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 "mozilla/net/NeckoChild.h" #include "mozilla/net/RemoteOpenFileChild.h" -#include "nsIRemoteOpenFileListener.h" + +#include "mozilla/ipc/FileDescriptor.h" +#include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/ipc/URIUtils.h" +#include "mozilla/net/NeckoChild.h" +#include "nsThreadUtils.h" +#include "nsJARProtocolHandler.h" +#include "nsIRemoteOpenFileListener.h" // needed to alloc/free NSPR file descriptors #include "private/pprio.h" @@ -17,13 +22,54 @@ using namespace mozilla::ipc; namespace mozilla { namespace net { -NS_IMPL_THREADSAFE_ISUPPORTS2(RemoteOpenFileChild, - nsIFile, - nsIHashable) +//----------------------------------------------------------------------------- +// Helper class to dispatch events async on windows/OSX +//----------------------------------------------------------------------------- +class CallsListenerInNewEvent : public nsRunnable +{ +public: + CallsListenerInNewEvent(nsIRemoteOpenFileListener *aListener, nsresult aRv) + : mListener(aListener), mRV(aRv) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aListener); + } + + void Dispatch() + { + MOZ_ASSERT(NS_IsMainThread()); + + nsresult rv = NS_DispatchToCurrentThread(this); + NS_ENSURE_SUCCESS_VOID(rv); + } + +private: + NS_IMETHOD Run() + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mListener); + + mListener->OnRemoteFileOpenComplete(mRV); + return NS_OK; + } + + nsCOMPtr mListener; + nsresult mRV; +}; + +//----------------------------------------------------------------------------- +// RemoteOpenFileChild +//----------------------------------------------------------------------------- + +NS_IMPL_THREADSAFE_ISUPPORTS3(RemoteOpenFileChild, + nsIFile, + nsIHashable, + nsICachedFileDescriptorListener) RemoteOpenFileChild::RemoteOpenFileChild(const RemoteOpenFileChild& other) - : mNSPRFileDesc(other.mNSPRFileDesc) + : mTabChild(other.mTabChild) + , mNSPRFileDesc(other.mNSPRFileDesc) , mAsyncOpenCalled(other.mAsyncOpenCalled) , mNSPROpenCalled(other.mNSPROpenCalled) { @@ -34,6 +80,10 @@ RemoteOpenFileChild::RemoteOpenFileChild(const RemoteOpenFileChild& other) RemoteOpenFileChild::~RemoteOpenFileChild() { + if (mListener) { + NotifyListener(NS_ERROR_UNEXPECTED); + } + if (mNSPRFileDesc) { // If we handed out fd we shouldn't have pointer to it any more. MOZ_ASSERT(!mNSPROpenCalled); @@ -102,25 +152,39 @@ RemoteOpenFileChild::AsyncRemoteFileOpen(int32_t aFlags, return NS_ERROR_NOT_AVAILABLE; } - mozilla::dom::TabChild* tabChild = nullptr; - if (aTabChild) { - tabChild = static_cast(aTabChild); - } - if (MissingRequiredTabChild(tabChild, "remoteopenfile")) { + mTabChild = static_cast(aTabChild); + + if (MissingRequiredTabChild(mTabChild, "remoteopenfile")) { return NS_ERROR_ILLEGAL_VALUE; } #if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA) // Windows/OSX desktop builds skip remoting, and just open file in child // process when asked for NSPR handle - aListener->OnRemoteFileOpenComplete(NS_OK); + nsRefPtr runnable = + new CallsListenerInNewEvent(aListener, NS_OK); + runnable->Dispatch(); + mAsyncOpenCalled = true; return NS_OK; #else + nsString path; + if (NS_FAILED(mFile->GetPath(path))) { + MOZ_NOT_REACHED("Couldn't get path from file!"); + } + + if (mTabChild) { + if (mTabChild->GetCachedFileDescriptor(path, this)) { + // The file descriptor was found in the cache and OnCachedFileDescriptor() + // will be called with it. + return NS_OK; + } + } + URIParams uri; SerializeURI(mURI, uri); - gNeckoChild->SendPRemoteOpenFileConstructor(this, uri, tabChild); + gNeckoChild->SendPRemoteOpenFileConstructor(this, uri, mTabChild); // Can't seem to reply from within IPDL Parent constructor, so send open as // separate message @@ -135,6 +199,86 @@ RemoteOpenFileChild::AsyncRemoteFileOpen(int32_t aFlags, #endif } +void +RemoteOpenFileChild::OnCachedFileDescriptor(const nsAString& aPath, + const FileDescriptor& aFD) +{ +#ifdef DEBUG + if (!aPath.IsEmpty()) { + MOZ_ASSERT(mFile); + + nsString path; + if (NS_FAILED(mFile->GetPath(path))) { + MOZ_NOT_REACHED("Couldn't get path from file!"); + } + + MOZ_ASSERT(path == aPath, "Paths don't match!"); + } +#endif + + HandleFileDescriptorAndNotifyListener(aFD, /* aFromRecvFileOpened */ false); +} + +void +RemoteOpenFileChild::HandleFileDescriptorAndNotifyListener( + const FileDescriptor& aFD, + bool aFromRecvFileOpened) +{ +#if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA) + MOZ_NOT_REACHED("OS X and Windows shouldn't be doing IPDL here"); +#else + if (!mListener) { + // We already notified our listener (either in response to a cached file + // descriptor callback or through the normal messaging mechanism). Close the + // file descriptor if it is valid. + if (aFD.IsValid()) { + nsRefPtr runnable = new CloseFileRunnable(aFD); + runnable->Dispatch(); + } + return; + } + + MOZ_ASSERT(!mNSPRFileDesc); + + nsRefPtr tabChild; + mTabChild.swap(tabChild); + + // If there is a pending callback and we're being called from IPDL then we + // need to cancel it. + if (tabChild && aFromRecvFileOpened) { + nsString path; + if (NS_FAILED(mFile->GetPath(path))) { + MOZ_NOT_REACHED("Couldn't get path from file!"); + } + + tabChild->CancelCachedFileDescriptorCallback(path, this); + } + + if (aFD.IsValid()) { + mNSPRFileDesc = PR_ImportFile(aFD.PlatformHandle()); + if (!mNSPRFileDesc) { + NS_WARNING("Failed to import file handle!"); + } + } + + NotifyListener(mNSPRFileDesc ? NS_OK : NS_ERROR_FILE_NOT_FOUND); +#endif +} + +void +RemoteOpenFileChild::NotifyListener(nsresult aResult) +{ + MOZ_ASSERT(mListener); + mListener->OnRemoteFileOpenComplete(aResult); + mListener = nullptr; // release ref to listener + + nsRefPtr handler(gJarHandler); + NS_WARN_IF_FALSE(handler, "nsJARProtocolHandler is already gone!"); + + if (handler) { + handler->RemoteOpenFileComplete(this, aResult); + } +} //----------------------------------------------------------------------------- // RemoteOpenFileChild::PRemoteOpenFileChild @@ -144,18 +288,9 @@ bool RemoteOpenFileChild::RecvFileOpened(const FileDescriptor& aFD) { #if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA) - NS_NOTREACHED("osX and Windows shouldn't be doing IPDL here"); + NS_NOTREACHED("OS X and Windows shouldn't be doing IPDL here"); #else - if (!aFD.IsValid()) { - return RecvFileDidNotOpen(); - } - - MOZ_ASSERT(!mNSPRFileDesc); - mNSPRFileDesc = PR_AllocFileDesc(aFD.PlatformHandle(), PR_GetFileMethods()); - - MOZ_ASSERT(mListener); - mListener->OnRemoteFileOpenComplete(NS_OK); - mListener = nullptr; // release ref to listener + HandleFileDescriptorAndNotifyListener(aFD, /* aFromRecvFileOpened */ true); // This calls NeckoChild::DeallocPRemoteOpenFile(), which deletes |this| if // IPDL holds the last reference. Don't rely on |this| existing after here! @@ -169,14 +304,10 @@ bool RemoteOpenFileChild::RecvFileDidNotOpen() { #if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA) - NS_NOTREACHED("osX and Windows shouldn't be doing IPDL here"); + NS_NOTREACHED("OS X and Windows shouldn't be doing IPDL here"); #else - MOZ_ASSERT(!mNSPRFileDesc); - printf_stderr("RemoteOpenFileChild: file was not opened!\n"); - - MOZ_ASSERT(mListener); - mListener->OnRemoteFileOpenComplete(NS_ERROR_FILE_NOT_FOUND); - mListener = nullptr; // release ref to listener + HandleFileDescriptorAndNotifyListener(FileDescriptor(), + /* aFromRecvFileOpened */ true); // This calls NeckoChild::DeallocPRemoteOpenFile(), which deletes |this| if // IPDL holds the last reference. Don't rely on |this| existing after here! @@ -186,24 +317,6 @@ RemoteOpenFileChild::RecvFileDidNotOpen() return true; } -void -RemoteOpenFileChild::AddIPDLReference() -{ - AddRef(); -} - -void -RemoteOpenFileChild::ReleaseIPDLReference() -{ - // if we haven't gotten fd from parent yet, we're not going to. - if (mListener) { - mListener->OnRemoteFileOpenComplete(NS_ERROR_UNEXPECTED); - mListener = nullptr; - } - - Release(); -} - //----------------------------------------------------------------------------- // RemoteOpenFileChild::nsIFile functions that we override logic for //----------------------------------------------------------------------------- @@ -662,4 +775,3 @@ RemoteOpenFileChild::GetHashCode(uint32_t *aResult) } // namespace net } // namespace mozilla - diff --git a/netwerk/ipc/RemoteOpenFileChild.h b/netwerk/ipc/RemoteOpenFileChild.h index 44877d6d14ff..3a383b83135d 100644 --- a/netwerk/ipc/RemoteOpenFileChild.h +++ b/netwerk/ipc/RemoteOpenFileChild.h @@ -9,10 +9,16 @@ #include "mozilla/dom/TabChild.h" #include "mozilla/net/PRemoteOpenFileChild.h" +#include "nsICachedFileDescriptorListener.h" #include "nsILocalFile.h" #include "nsIRemoteOpenFileListener.h" namespace mozilla { + +namespace ipc { +class FileDescriptor; +} + namespace net { /** @@ -38,7 +44,11 @@ class RemoteOpenFileChild MOZ_FINAL : public PRemoteOpenFileChild , public nsIFile , public nsIHashable + , public nsICachedFileDescriptorListener { + typedef mozilla::dom::TabChild TabChild; + typedef mozilla::ipc::FileDescriptor FileDescriptor; + public: RemoteOpenFileChild() : mNSPRFileDesc(nullptr) @@ -55,26 +65,43 @@ public: // URI must be scheme 'remoteopenfile://': otherwise looks like a file:// uri. nsresult Init(nsIURI* aRemoteOpenUri); - void AddIPDLReference(); - void ReleaseIPDLReference(); - // Send message to parent to tell it to open file handle for file. // TabChild is required, for IPC security. // Note: currently only PR_RDONLY is supported for 'flags' nsresult AsyncRemoteFileOpen(int32_t aFlags, nsIRemoteOpenFileListener* aListener, nsITabChild* aTabChild); + + void ReleaseIPDLReference() + { + Release(); + } + private: RemoteOpenFileChild(const RemoteOpenFileChild& other); protected: + void AddIPDLReference() + { + AddRef(); + } + virtual bool RecvFileOpened(const FileDescriptor&); virtual bool RecvFileDidNotOpen(); + virtual void OnCachedFileDescriptor(const nsAString& aPath, + const FileDescriptor& aFD) MOZ_OVERRIDE; + + void HandleFileDescriptorAndNotifyListener(const FileDescriptor&, + bool aFromRecvFileOpened); + + void NotifyListener(nsresult aResult); + // regular nsIFile object, that we forward most calls to. nsCOMPtr mFile; nsCOMPtr mURI; nsCOMPtr mListener; + nsRefPtr mTabChild; PRFileDesc* mNSPRFileDesc; bool mAsyncOpenCalled; bool mNSPROpenCalled; @@ -84,4 +111,3 @@ protected: } // namespace mozilla #endif // _RemoteOpenFileChild_h -