mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 835698 - 'Pre-open() and send the fd for app process's application.zip'. r=jduell.
This commit is contained in:
parent
5c728f13b8
commit
b66d3a5418
@ -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 \
|
||||
|
@ -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;
|
||||
|
@ -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<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
|
||||
@ -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<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()
|
||||
{
|
||||
|
@ -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<nsIWebNavigation> mWebNav;
|
||||
nsCOMPtr<nsIWidget> mWidget;
|
||||
nsCOMPtr<nsIURI> 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<nsAutoPtr<CachedFileDescriptorInfo>, 1>
|
||||
mCachedFileDescriptorInfos;
|
||||
float mOldViewportWidth;
|
||||
nscolor mLastBackgroundColor;
|
||||
ScrollingBehavior mScrolling;
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "nsSerializationHelper.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "private/pprio.h"
|
||||
#include "StructuredCloneUtils.h"
|
||||
#include "TabChild.h"
|
||||
#include <algorithm>
|
||||
@ -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<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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
38
dom/ipc/nsICachedFileDescriptorListener.h
Normal file
38
dom/ipc/nsICachedFileDescriptorListener.h
Normal file
@ -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
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
81
ipc/glue/FileDescriptorUtils.cpp
Normal file
81
ipc/glue/FileDescriptorUtils.cpp
Normal file
@ -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;
|
||||
}
|
47
ipc/glue/FileDescriptorUtils.h
Normal file
47
ipc/glue/FileDescriptorUtils.h
Normal file
@ -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
|
@ -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 \
|
||||
|
@ -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<RemoteOpenFileChild> 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<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
|
||||
@ -397,6 +403,38 @@ nsJARChannel::LookupFile()
|
||||
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
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -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<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)) {
|
||||
@ -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<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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -51,6 +51,8 @@ public:
|
||||
private:
|
||||
nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **);
|
||||
nsresult LookupFile();
|
||||
nsresult OpenLocalFile();
|
||||
void NotifyError(nsresult aError);
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
nsCString mSpec;
|
||||
|
@ -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,
|
||||
|
@ -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<nsCOMPtr<nsIRemoteOpenFileListener>, 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<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;
|
||||
|
@ -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
|
||||
|
@ -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<nsIRemoteOpenFileListener> 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<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
|
||||
@ -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<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
|
||||
@ -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
|
||||
|
||||
|
@ -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<nsIFile> mFile;
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIRemoteOpenFileListener> mListener;
|
||||
nsRefPtr<TabChild> mTabChild;
|
||||
PRFileDesc* mNSPRFileDesc;
|
||||
bool mAsyncOpenCalled;
|
||||
bool mNSPROpenCalled;
|
||||
@ -84,4 +111,3 @@ protected:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _RemoteOpenFileChild_h
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user