--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -17,17 +17,20 @@ EXPORT_LIBRARY = 1
ifndef _MSC_VER
FAIL_ON_WARNINGS := 1
endif # !_MSC_VER
ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
TEST_DIRS += tests
endif
-EXPORTS = PCOMContentPermissionRequestChild.h
+EXPORTS = \
+ nsICachedFileDescriptorListener.h \
+ PCOMContentPermissionRequestChild.h \
+ $(NULL)
EXPORTS_NAMESPACES = \
mozilla \
mozilla/dom \
mozilla/dom/ipc \
$(NULL)
EXPORTS_mozilla = \
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -26,16 +26,17 @@ include "mozilla/layout/RenderFrameUtils
using IPC::Principal;
using gfxMatrix;
using gfxRect;
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;
using nsIMEUpdatePreference;
using nsIntPoint;
using nsIntRect;
using nsIntSize;
using nsKeyEvent;
@@ -285,16 +286,18 @@ child:
* |Show()| and |Move()| take IntSizes rather than Rects because
* content processes always render to a virtual <0, 0> top-left
* point.
*/
Show(nsIntSize size);
LoadURL(nsCString uri);
+ CacheFileDescriptor(nsString path, FileDescriptor fd);
+
UpdateDimensions(nsRect rect, nsIntSize size, ScreenOrientation orientation) compress;
UpdateFrame(FrameMetrics frame) compress;
/**
* Requests handling of a double tap. |point| is in CSS pixels, relative to
* the scroll offset. This message is expected to round-trip back to
* ZoomToRect() with a rect indicating where we should zoom to.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -13,16 +13,17 @@
#include "ContentChild.h"
#include "IndexedDBChild.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/IntentionalCrash.h"
#include "mozilla/docshell/OfflineCacheUpdateChild.h"
#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"
#include "mozilla/layout/RenderFrameChild.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/unused.h"
#include "mozIApplication.h"
#include "nsComponentManagerUtils.h"
@@ -32,16 +33,17 @@
#include "nsEventListenerManager.h"
#include <algorithm>
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif
#include "mozilla/dom/Element.h"
#include "nsIAppsService.h"
#include "nsIBaseWindow.h"
+#include "nsICachedFileDescriptorListener.h"
#include "nsIComponentManager.h"
#include "nsIDocumentInlines.h"
#include "nsIDOMClassInfo.h"
#include "nsIDOMElement.h"
#include "nsIDOMEvent.h"
#include "nsIDOMWindow.h"
#include "nsIDOMWindowUtils.h"
#include "nsIDocShell.h"
@@ -116,16 +118,108 @@ ContentListener::HandleEvent(nsIDOMEvent
class ContentDialogChild : public PContentDialogChild
{
public:
virtual bool Recv__delete__(const InfallibleTArray<int>& aIntParams,
const InfallibleTArray<nsString>& aStringParams);
};
+class TabChild::CachedFileDescriptorInfo
+{
+ struct PathOnlyComparatorHelper
+ {
+ bool Equals(const nsAutoPtr<CachedFileDescriptorInfo>& a,
+ const CachedFileDescriptorInfo& b) const
+ {
+ return a->mPath == b.mPath;
+ }
+ };
+
+ struct PathAndCallbackComparatorHelper
+ {
+ bool Equals(const nsAutoPtr<CachedFileDescriptorInfo>& a,
+ const CachedFileDescriptorInfo& b) const
+ {
+ return a->mPath == b.mPath &&
+ a->mCallback == b.mCallback;
+ }
+ };
+
+public:
+ nsString mPath;
+ FileDescriptor mFileDescriptor;
+ nsCOMPtr<nsICachedFileDescriptorListener> 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<CachedFileDescriptorInfo> 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<TabChild> sPreallocatedTab;
/*static*/ void
TabChild::PreloadSlowThings()
{
MOZ_ASSERT(!sPreallocatedTab);
nsRefPtr<TabChild> tab(new TabChild(TabContext(), /* chromeFlags */ 0));
@@ -1112,16 +1206,140 @@ TabChild::RecvLoadURL(const nsCString& u
#ifdef MOZ_CRASHREPORTER
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), uri);
#endif
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<CachedFileDescriptorInfo>& 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<CloseFileRunnable> 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<CachedFileDescriptorInfo>& info =
+ mCachedFileDescriptorInfos[index];
+
+ MOZ_ASSERT(info);
+ MOZ_ASSERT(info->mPath == aPath);
+ MOZ_ASSERT(!info->mCallback);
+ MOZ_ASSERT(!info->mCanceled);
+
+ info->mCallback = aCallback;
+
+ nsRefPtr<CachedFileDescriptorCallbackRunnable> 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<CachedFileDescriptorInfo>& 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()
{
RecvShow(nsIntSize(0, 0));
mDidFakeShow = true;
}
bool
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -49,16 +49,17 @@
#include "nsWeakReference.h"
#include "nsITabChild.h"
#include "mozilla/Attributes.h"
#include "FrameMetrics.h"
#include "ProcessUtils.h"
#include "mozilla/dom/TabContext.h"
struct gfxMatrix;
+class nsICachedFileDescriptorListener;
namespace mozilla {
namespace layout {
class RenderFrameChild;
}
namespace dom {
@@ -191,16 +192,19 @@ public:
*/
virtual bool DoSendSyncMessage(const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
InfallibleTArray<nsString>* aJSONRetVal);
virtual bool DoSendAsyncMessage(const nsAString& aMessage,
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);
virtual bool RecvHandleDoubleTap(const nsIntPoint& aPoint);
virtual bool RecvHandleSingleTap(const nsIntPoint& aPoint);
virtual bool RecvHandleLongTap(const nsIntPoint& aPoint);
virtual bool RecvActivate();
virtual bool RecvDeactivate();
@@ -312,16 +316,25 @@ public:
/**
* Get this object's app type.
*
* A TabChild's app type corresponds to the value of its frame element's
* "mozapptype" attribute.
*/
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,
int32_t* aMaxTextureSize,
uint64_t* aLayersId) MOZ_OVERRIDE;
virtual bool DeallocPRenderFrame(PRenderFrameChild* aFrame) MOZ_OVERRIDE;
virtual bool RecvDestroy() MOZ_OVERRIDE;
@@ -407,16 +420,19 @@ private:
nsIDOMWindowUtils* GetDOMWindowUtils()
{
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
return utils;
}
+ class CachedFileDescriptorInfo;
+ class CachedFileDescriptorCallbackRunnable;
+
nsCOMPtr<nsIWebNavigation> mWebNav;
nsCOMPtr<nsIWidget> mWidget;
nsCOMPtr<nsIURI> mLastURI;
FrameMetrics mLastMetrics;
RenderFrameChild* mRemoteFrame;
nsRefPtr<TabChildGlobal> mTabChildGlobal;
uint32_t mChromeFlags;
nsIntRect mOuterRect;
@@ -425,16 +441,19 @@ private:
// point of the touchstart.
nsIntPoint mGestureDownPoint;
// The touch identifier of the active gesture.
int32_t mActivePointerId;
// A timer task that fires if the tap-hold timeout is exceeded by
// 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<nsAutoPtr<CachedFileDescriptorInfo>, 1>
+ mCachedFileDescriptorInfos;
float mOldViewportWidth;
nscolor mLastBackgroundColor;
ScrollingBehavior mScrolling;
bool mDidFakeShow;
bool mNotified;
bool mContentDocumentIsDisplayed;
bool mTriedBrowserInit;
nsString mAppType;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -45,32 +45,143 @@
#include "nsIWidget.h"
#include "nsIWindowWatcher.h"
#include "nsNetUtil.h"
#include "nsPIDOMWindow.h"
#include "nsPrintfCString.h"
#include "nsSerializationHelper.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
+#include "private/pprio.h"
#include "StructuredCloneUtils.h"
#include "TabChild.h"
#include <algorithm>
using namespace mozilla::dom;
using namespace mozilla::ipc;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::services;
using namespace mozilla::widget;
using namespace mozilla::dom::indexedDB;
// The flags passed by the webProgress notifications are 16 bits shifted
// from the ones registered by webProgressListeners.
#define NOTIFY_FLAG_SHIFT 16
+class OpenFileAndSendFDRunnable : public nsRunnable
+{
+ const nsString mPath;
+ nsRefPtr<TabParent> mTabParent;
+ nsCOMPtr<nsIEventTarget> 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> tabParent;
+ mTabParent.swap(tabParent);
+
+ FileDescriptor::PlatformHandleType handle =
+ FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFD));
+
+ mozilla::unused << tabParent->SendCacheFileDescriptor(mPath, handle);
+
+ nsCOMPtr<nsIEventTarget> 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<nsIFile> 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 {
TabParent* sEventCapturer;
TabParent *TabParent::mIMETabParent = nullptr;
NS_IMPL_ISUPPORTS3(TabParent, nsITabParent, nsIAuthPromptProvider, nsISecureBrowserUI)
@@ -88,16 +199,17 @@ TabParent::TabParent(const TabContext& a
, mRect(0, 0, 0, 0)
, mDimensions(0, 0)
, mOrientation(0)
, mDPI(0)
, mShown(false)
, mUpdatedDimensions(false)
, mMarkedDestroying(false)
, mIsDestroyed(false)
+ , mAppPackageFileDescriptorSent(false)
{
}
TabParent::~TabParent()
{
}
void
@@ -225,33 +337,76 @@ TabParent::AnswerCreateWindow(PBrowserPa
*retval = frameLoader->GetRemoteBrowser();
return true;
}
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<mozIApplication> 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<nsIFile> 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<OpenFileAndSendFDRunnable> openFileRunnable =
+ new OpenFileAndSendFDRunnable(path, this);
+ openFileRunnable->Dispatch();
+ }
+ }
+ }
}
void
TabParent::Show(const nsIntSize& size)
{
// sigh
mShown = true;
mDimensions = size;
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -299,14 +299,16 @@ private:
void MaybeForwardEventToRenderFrame(const nsInputEvent& aEvent,
nsInputEvent* aOutEvent);
// When true, we've initiated normal shutdown and notified our
// managing PContent.
bool mMarkedDestroying;
// 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
} // namespace mozilla
#endif
new file mode 100644
--- /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
--- a/ipc/glue/FileDescriptor.cpp
+++ b/ipc/glue/FileDescriptor.cpp
@@ -37,17 +37,18 @@ FileDescriptor::FileDescriptor(PlatformH
mHandleCreatedByOtherProcessWasUsed(false)
{
DuplicateInCurrentProcess(aHandle);
}
void
FileDescriptor::DuplicateInCurrentProcess(PlatformHandleType aHandle)
{
- MOZ_ASSERT(!mHandleCreatedByOtherProcess);
+ MOZ_ASSERT_IF(mHandleCreatedByOtherProcess,
+ mHandleCreatedByOtherProcessWasUsed);
if (IsValid(aHandle)) {
PlatformHandleType newHandle;
#ifdef XP_WIN
if (DuplicateHandle(GetCurrentProcess(), aHandle, GetCurrentProcess(),
&newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
#else // XP_WIN
if ((newHandle = dup(aHandle)) != INVALID_HANDLE) {
--- a/ipc/glue/FileDescriptor.h
+++ b/ipc/glue/FileDescriptor.h
@@ -48,17 +48,19 @@ public:
// This should only ever be created by IPDL.
struct IPDLPrivate
{};
FileDescriptor();
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);
FileDescriptor(const IPDLPrivate&, const PickleType& aPickle)
#ifdef XP_WIN
: mHandle(aPickle)
#else
@@ -72,28 +74,17 @@ public:
{
CloseCurrentProcessHandle();
}
FileDescriptor&
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;
}
// Performs platform-specific actions to duplicate mHandle in the other
// process (e.g. dup() on POSIX, DuplicateHandle() on Windows). Returns a
// pickled value that can be passed to the other process via IPC.
PickleType
ShareTo(const IPDLPrivate&, ProcessHandle aOtherProcess) const;
@@ -117,16 +108,31 @@ public:
bool
operator==(const FileDescriptor& aOther) const
{
return mHandle == aOther.mHandle;
}
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);
void
DuplicateInCurrentProcess(PlatformHandleType aHandle);
void
CloseCurrentProcessHandle();
new file mode 100644
--- /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<nsIEventTarget> 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;
+}
new file mode 100644
--- /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
--- a/ipc/glue/Makefile.in
+++ b/ipc/glue/Makefile.in
@@ -21,16 +21,17 @@ EXPORTS_NAMESPACES = ipc mozilla/ipc
EXPORTS_ipc = IPCMessageUtils.h
EXPORTS_mozilla/ipc = \
AsyncChannel.h \
BrowserProcessSubThread.h \
CrossProcessMutex.h \
FileDescriptor.h \
+ FileDescriptorUtils.h \
GeckoChildProcessHost.h \
InputStreamUtils.h \
IOThreadChild.h \
ProcessChild.h \
ProtocolUtils.h \
RPCChannel.h \
SharedMemory.h \
SharedMemoryBasic.h \
@@ -66,16 +67,17 @@ EXPORTS_mozilla/ipc += SharedMemoryBasic
else
EXPORTS_mozilla/ipc += SharedMemoryBasic_chromium.h
endif #}
CPPSRCS += \
AsyncChannel.cpp \
BrowserProcessSubThread.cpp \
FileDescriptor.cpp \
+ FileDescriptorUtils.cpp \
GeckoChildProcessHost.cpp \
InputStreamUtils.cpp \
MessagePump.cpp \
ProcessChild.cpp \
ProtocolUtils.cpp \
RPCChannel.cpp \
ScopedXREEmbed.cpp \
SharedMemory.cpp \
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -13,17 +13,16 @@
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIViewSourceChannel.h"
#include "nsChannelProperties.h"
#include "nsIScriptSecurityManager.h"
#include "nsIPrincipal.h"
#include "nsIFileURL.h"
-#include "nsXULAppAPI.h"
#include "mozilla/Preferences.h"
#include "mozilla/net/RemoteOpenFileChild.h"
#include "nsITabChild.h"
using namespace mozilla;
using namespace mozilla::net;
@@ -344,19 +343,19 @@ nsJARChannel::LookupFile()
// 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));
}
// 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<RemoteOpenFileChild> remoteFile = new RemoteOpenFileChild();
rv = remoteFile->Init(mJarBaseURI);
NS_ENSURE_SUCCESS(rv, rv);
mJarFile = remoteFile;
nsIZipReaderCache *jarCache = gJarHandler->JarCache();
if (jarCache) {
@@ -364,23 +363,30 @@ nsJARChannel::LookupFile()
rv = jarCache->IsCached(mJarFile, &cached);
if (NS_SUCCEEDED(rv) && cached) {
// zipcache already has file mmapped: don't open on parent,
// just return and proceed to cache hit in CreateJarInput()
return NS_OK;
}
}
+ 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<nsITabChild> 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
if (!mJarFile) {
nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI);
if (jarURI) {
nsCOMPtr<nsIFileURL> fileURL;
nsCOMPtr<nsIURI> innerJarURI;
@@ -392,16 +398,48 @@ nsJARChannel::LookupFile()
jarURI->GetJAREntry(mInnerJarEntry);
}
}
}
return rv;
}
+nsresult
+nsJARChannel::OpenLocalFile()
+{
+ MOZ_ASSERT(mIsPending);
+
+ // Local files are always considered safe.
+ mIsUnsafe = false;
+
+ nsRefPtr<nsJARInputThunk> 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
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsJARChannel::GetName(nsACString &result)
{
return mJarURI->GetSpec(result);
@@ -744,27 +782,17 @@ nsJARChannel::AsyncOpen(nsIStreamListene
rv = NS_NewDownloader(getter_AddRefs(mDownloader), this);
if (NS_SUCCEEDED(rv))
rv = NS_OpenURI(mDownloader, nullptr, mJarBaseURI, nullptr,
mLoadGroup, mCallbacks,
mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS));
} else if (mOpeningRemote) {
// nothing to do: already asked parent to open file.
} else {
- // local files are always considered safe
- mIsUnsafe = false;
-
- nsRefPtr<nsJARInputThunk> 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)) {
mIsPending = false;
mListenerContext = nullptr;
mListener = nullptr;
return rv;
}
@@ -897,56 +925,43 @@ nsJARChannel::OnDownloadComplete(nsIDown
rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input);
if (NS_SUCCEEDED(rv))
rv = mPump->AsyncRead(this, nullptr);
}
status = rv;
}
if (NS_FAILED(status)) {
- mStatus = status;
- OnStartRequest(nullptr, nullptr);
- OnStopRequest(nullptr, nullptr, status);
+ NotifyError(status);
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsIRemoteOpenFileListener
//-----------------------------------------------------------------------------
nsresult
nsJARChannel::OnRemoteFileOpenComplete(nsresult aOpenStatus)
{
nsresult rv = aOpenStatus;
- if (NS_SUCCEEDED(rv)) {
- // files on parent are always considered safe
- mIsUnsafe = false;
-
- nsRefPtr<nsJARInputThunk> 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
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
{
LOG(("nsJARChannel::OnStartRequest [this=%x %s]\n", this, mSpec.get()));
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -46,16 +46,18 @@ public:
nsJARChannel();
virtual ~nsJARChannel();
nsresult Init(nsIURI *uri);
private:
nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **);
nsresult LookupFile();
+ nsresult OpenLocalFile();
+ void NotifyError(nsresult aError);
#if defined(PR_LOGGING)
nsCString mSpec;
#endif
bool mOpened;
nsCOMPtr<nsIJARURI> mJarURI;
--- a/modules/libjar/nsJARProtocolHandler.cpp
+++ b/modules/libjar/nsJARProtocolHandler.cpp
@@ -12,27 +12,37 @@
#include "nsJARURI.h"
#include "nsIURL.h"
#include "nsJARChannel.h"
#include "nsXPIDLString.h"
#include "nsString.h"
#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);
#define NS_JAR_CACHE_SIZE 32
//-----------------------------------------------------------------------------
nsJARProtocolHandler *gJarHandler = nullptr;
nsJARProtocolHandler::nsJARProtocolHandler()
+: mIsMainProcess(XRE_GetProcessType() == GeckoProcessType_Default)
{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mIsMainProcess) {
+ mRemoteFileListeners.Init();
+ }
}
nsJARProtocolHandler::~nsJARProtocolHandler()
{
}
nsresult
nsJARProtocolHandler::Init()
@@ -50,16 +60,77 @@ nsIMIMEService *
nsJARProtocolHandler::MimeService()
{
if (!mMimeService)
mMimeService = do_GetService("@mozilla.org/mime;1");
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,
nsISupportsWeakReference)
nsJARProtocolHandler*
nsJARProtocolHandler::GetSingleton()
{
--- a/modules/libjar/nsJARProtocolHandler.h
+++ b/modules/libjar/nsJARProtocolHandler.h
@@ -8,20 +8,28 @@
#include "nsIJARProtocolHandler.h"
#include "nsIProtocolHandler.h"
#include "nsIJARURI.h"
#include "nsIZipReader.h"
#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<nsCOMPtr<nsIRemoteOpenFileListener>, 5>
+ RemoteFileListenerArray;
+
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROTOCOLHANDLER
NS_DECL_NSIJARPROTOCOLHANDLER
// nsJARProtocolHandler methods:
nsJARProtocolHandler();
virtual ~nsJARProtocolHandler();
@@ -29,19 +37,32 @@ public:
static nsJARProtocolHandler *GetSingleton();
nsresult Init();
// returns non addref'ed pointer.
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<nsIZipReaderCache> mJARCache;
nsCOMPtr<nsIMIMEService> mMimeService;
+
+ // Holds lists of RemoteOpenFileChild (not including the 1st) that have
+ // requested the same file from parent.
+ nsClassHashtable<nsHashableHashKey, RemoteFileListenerArray>
+ mRemoteFileListeners;
+
+ bool mIsMainProcess;
};
extern nsJARProtocolHandler *gJarHandler;
#define NS_JARPROTOCOLHANDLER_CLASSNAME \
"nsJarProtocolHandler"
#define NS_JARPROTOCOLHANDLER_CID \
{ /* 0xc7e410d4-0x85f2-11d3-9f63-006008a6efe9 */ \
--- a/netwerk/ipc/Makefile.in
+++ b/netwerk/ipc/Makefile.in
@@ -41,13 +41,14 @@ CPPSRCS = \
ChannelEventQueue.cpp \
RemoteOpenFileParent.cpp \
RemoteOpenFileChild.cpp \
$(NULL)
LOCAL_INCLUDES += \
-I$(srcdir)/../protocol/http \
-I$(srcdir)/../base/src \
+ -I$(topsrcdir)/modules/libjar \
$(NULL)
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk
include $(topsrcdir)/config/rules.mk
--- a/netwerk/ipc/RemoteOpenFileChild.cpp
+++ b/netwerk/ipc/RemoteOpenFileChild.cpp
@@ -1,44 +1,94 @@
/* -*- 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 "mozilla/net/RemoteOpenFileChild.h"
+
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/ipc/URIUtils.h"
#include "mozilla/net/NeckoChild.h"
-#include "mozilla/net/RemoteOpenFileChild.h"
+#include "nsThreadUtils.h"
+#include "nsJARProtocolHandler.h"
#include "nsIRemoteOpenFileListener.h"
-#include "mozilla/ipc/URIUtils.h"
// needed to alloc/free NSPR file descriptors
#include "private/pprio.h"
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
-NS_IMPL_THREADSAFE_ISUPPORTS2(RemoteOpenFileChild,
+//-----------------------------------------------------------------------------
+// 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<nsIRemoteOpenFileListener> mListener;
+ nsresult mRV;
+};
+
+//-----------------------------------------------------------------------------
+// RemoteOpenFileChild
+//-----------------------------------------------------------------------------
+
+NS_IMPL_THREADSAFE_ISUPPORTS3(RemoteOpenFileChild,
nsIFile,
- nsIHashable)
-
+ nsIHashable,
+ nsICachedFileDescriptorListener)
RemoteOpenFileChild::RemoteOpenFileChild(const RemoteOpenFileChild& other)
- : mNSPRFileDesc(other.mNSPRFileDesc)
+ : mTabChild(other.mTabChild)
+ , mNSPRFileDesc(other.mNSPRFileDesc)
, mAsyncOpenCalled(other.mAsyncOpenCalled)
, mNSPROpenCalled(other.mNSPROpenCalled)
{
// Note: don't clone mListener or we'll have a refcount leak.
other.mURI->Clone(getter_AddRefs(mURI));
other.mFile->Clone(getter_AddRefs(mFile));
}
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);
// PR_Close both closes the file and deallocates the PRFileDesc
PR_Close(mNSPRFileDesc);
}
}
@@ -97,118 +147,181 @@ RemoteOpenFileChild::AsyncRemoteFileOpen
if (mAsyncOpenCalled) {
return NS_ERROR_ALREADY_OPENED;
}
if (aFlags != PR_RDONLY) {
return NS_ERROR_NOT_AVAILABLE;
}
- mozilla::dom::TabChild* tabChild = nullptr;
- if (aTabChild) {
- tabChild = static_cast<mozilla::dom::TabChild*>(aTabChild);
- }
- if (MissingRequiredTabChild(tabChild, "remoteopenfile")) {
+ mTabChild = static_cast<TabChild*>(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<CallsListenerInNewEvent> 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
SendAsyncOpenFile();
// The chrome process now has a logical ref to us until we call Send__delete
AddIPDLReference();
mListener = aListener;
mAsyncOpenCalled = true;
return NS_OK;
#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<CloseFileRunnable> runnable = new CloseFileRunnable(aFD);
+ runnable->Dispatch();
+ }
+ return;
+ }
+
+ MOZ_ASSERT(!mNSPRFileDesc);
+
+ nsRefPtr<TabChild> 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<nsJARProtocolHandler> handler(gJarHandler);
+ NS_WARN_IF_FALSE(handler, "nsJARProtocolHandler is already gone!");
+
+ if (handler) {
+ handler->RemoteOpenFileComplete(this, aResult);
+ }
+}
//-----------------------------------------------------------------------------
// RemoteOpenFileChild::PRemoteOpenFileChild
//-----------------------------------------------------------------------------
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!
Send__delete__(this);
#endif
return true;
}
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!
Send__delete__(this);
#endif
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
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RemoteOpenFileChild::Clone(nsIFile **file)
{
*file = new RemoteOpenFileChild(*this);
@@ -657,9 +770,8 @@ RemoteOpenFileChild::GetHashCode(uint32_
if (hashable) {
return hashable->GetHashCode(aResult);
}
return NS_ERROR_UNEXPECTED;
}
} // namespace net
} // namespace mozilla
-
--- a/netwerk/ipc/RemoteOpenFileChild.h
+++ b/netwerk/ipc/RemoteOpenFileChild.h
@@ -4,20 +4,26 @@
* 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 _RemoteOpenFileChild_h
#define _RemoteOpenFileChild_h
#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 {
/**
* RemoteOpenFileChild: a thin wrapper around regular nsIFile classes that does
* IPC to open a file handle on parent instead of child. Used when we can't
* open file handle on child (don't have permission), but we don't want the
* overhead of shipping all I/O traffic across IPDL. Example: JAR files.
*
@@ -33,55 +39,75 @@ namespace net {
*
* This class should only be instantiated in a child process.
*
*/
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)
, mAsyncOpenCalled(false)
, mNSPROpenCalled(false)
{}
virtual ~RemoteOpenFileChild();
NS_DECL_ISUPPORTS
NS_DECL_NSIFILE
NS_DECL_NSIHASHABLE
// 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<nsIFile> mFile;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIRemoteOpenFileListener> mListener;
+ nsRefPtr<TabChild> mTabChild;
PRFileDesc* mNSPRFileDesc;
bool mAsyncOpenCalled;
bool mNSPROpenCalled;
};
} // namespace net
} // namespace mozilla
#endif // _RemoteOpenFileChild_h
-