Bug 988816 - Support multiple OpenNSPRFileOpen() on RemoteOpenFile. r=aklotz, jduell
authorShian-Yow Wu <swu@mozilla.com>
Fri, 18 Jul 2014 10:46:22 +0800
changeset 215626 8912d2088eee28737b418afd8cd1e246fc97c6e7
parent 215625 daa82439f77ff7bac1b577e98b7773ce04bcb3c4
child 215627 f7de657a37ed109072f19e1dffe4867f856a5047
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaklotz, jduell
bugs988816
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 988816 - Support multiple OpenNSPRFileOpen() on RemoteOpenFile. r=aklotz, jduell
content/base/src/nsXMLHttpRequest.cpp
modules/libjar/nsIJARChannel.idl
modules/libjar/nsJARChannel.cpp
modules/libjar/nsJARChannel.h
netwerk/ipc/RemoteOpenFileChild.cpp
netwerk/protocol/app/AppProtocolHandler.cpp
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2986,16 +2986,24 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
       nsAutoCString scheme;
 
       rv = mChannel->GetURI(getter_AddRefs(uri));
       if (NS_SUCCEEDED(rv)) {
         uri->GetScheme(scheme);
         if (scheme.LowerCaseEqualsLiteral("app") ||
             scheme.LowerCaseEqualsLiteral("jar")) {
           mIsMappedArrayBuffer = true;
+          if (XRE_GetProcessType() != GeckoProcessType_Default) {
+            nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(mChannel);
+            // For memory mapping from child process, we need to get file
+            // descriptor of the JAR file opened remotely on the parent proess.
+            // Set this to make sure that file descriptor can be obtained by
+            // child process.
+            jarChannel->EnsureChildFd();
+          }
         }
       }
     }
     // Start reading from the channel
     rv = mChannel->AsyncOpen(listener, nullptr);
   }
 
   if (NS_FAILED(rv)) {
--- a/modules/libjar/nsIJARChannel.idl
+++ b/modules/libjar/nsIJARChannel.idl
@@ -2,17 +2,17 @@
 /* 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 "nsIChannel.idl"
 
 interface nsIFile;
 
-[scriptable, builtinclass, uuid(063e9698-ec67-4fe2-aa19-d21505beaa61)]
+[scriptable, builtinclass, uuid(5a4f8df0-3bd9-45c2-9db9-67e74c3dd47d)]
 interface nsIJARChannel : nsIChannel
 {
     /**
      * Returns TRUE if the JAR file is not safe (if the content type reported
      * by the server for a remote JAR is not of an expected type).  Scripting,
      * redirects, and plugins should be disabled when loading from this
      * channel.
      */
@@ -22,9 +22,16 @@ interface nsIJARChannel : nsIChannel
      * Forces the uri to be a app:// uri.
      */
     void setAppURI(in nsIURI uri);
 
     /**
      * Returns the JAR file.
      */
     readonly attribute nsIFile jarFile;
+
+    /**
+     * For child process, set this to make sure that a valid file descriptor of
+     * JAR file is always provided when calling NSPRFileDesc().
+     * Must be set before Open() or AsyncOpen() to be effective.
+     */
+    void ensureChildFd();
 };
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -194,16 +194,17 @@ nsJARChannel::nsJARChannel()
     : mOpened(false)
     , mAppURI(nullptr)
     , mContentLength(-1)
     , mLoadFlags(LOAD_NORMAL)
     , mStatus(NS_OK)
     , mIsPending(false)
     , mIsUnsafe(true)
     , mOpeningRemote(false)
+    , mEnsureChildFd(false)
 {
 #if defined(PR_LOGGING)
     if (!gJarProtocolLog)
         gJarProtocolLog = PR_NewLogModule("nsJarProtocol");
 #endif
 
     // hold an owning reference to the jar handler
     NS_ADDREF(gJarHandler);
@@ -351,29 +352,30 @@ nsJARChannel::LookupFile()
         rv = mJarBaseURI->GetScheme(scheme);
         if (NS_SUCCEEDED(rv) && scheme.EqualsLiteral("remoteopenfile")) {
             nsRefPtr<RemoteOpenFileChild> remoteFile = new RemoteOpenFileChild();
             rv = remoteFile->Init(mJarBaseURI, mAppURI);
             NS_ENSURE_SUCCESS(rv, rv);
             mJarFile = remoteFile;
 
             nsIZipReaderCache *jarCache = gJarHandler->JarCache();
-            if (jarCache) {
+            if (jarCache && !mEnsureChildFd) {
                 bool cached = false;
                 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)) {
+            if (gJarHandler->RemoteOpenFileInProgress(remoteFile, this) &&
+                !mEnsureChildFd) {
                 // 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;
@@ -860,16 +862,23 @@ nsJARChannel::SetAppURI(nsIURI *aURI) {
 
 NS_IMETHODIMP
 nsJARChannel::GetJarFile(nsIFile **aFile)
 {
     NS_IF_ADDREF(*aFile = mJarFile);
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsJARChannel::EnsureChildFd()
+{
+    mEnsureChildFd = true;
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsIDownloadObserver
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
                                  nsIRequest    *request,
                                  nsISupports   *context,
@@ -1030,16 +1039,21 @@ nsJARChannel::OnStopRequest(nsIRequest *
     mPump = 0;
     mIsPending = false;
     mDownloader = 0; // this may delete the underlying jar file
 
     // Drop notification callbacks to prevent cycles.
     mCallbacks = 0;
     mProgressSink = 0;
 
+    if (mOpeningRemote) {
+      // To deallocate file descriptor by RemoteOpenFileChild destructor.
+      mJarFile = nullptr;
+    }
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
                                nsIInputStream *stream,
                                uint64_t offset, uint32_t count)
 {
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -88,16 +88,17 @@ private:
      * empty */
     uint32_t                        mContentDisposition;
     int64_t                         mContentLength;
     uint32_t                        mLoadFlags;
     nsresult                        mStatus;
     bool                            mIsPending;
     bool                            mIsUnsafe;
     bool                            mOpeningRemote;
+    bool                            mEnsureChildFd;
 
     nsCOMPtr<nsIStreamListener>     mDownloader;
     nsCOMPtr<nsIInputStreamPump>    mPump;
     // mRequest is only non-null during OnStartRequest, so we'll have a pointer
     // to the request if we get called back via RetargetDeliveryTo.
     nsCOMPtr<nsIRequest>            mRequest;
     nsCOMPtr<nsIFile>               mJarFile;
     nsCOMPtr<nsIURI>                mJarBaseURI;
--- a/netwerk/ipc/RemoteOpenFileChild.cpp
+++ b/netwerk/ipc/RemoteOpenFileChild.cpp
@@ -67,20 +67,30 @@ private:
 
 NS_IMPL_ISUPPORTS(RemoteOpenFileChild,
                   nsIFile,
                   nsIHashable,
                   nsICachedFileDescriptorListener)
 
 RemoteOpenFileChild::RemoteOpenFileChild(const RemoteOpenFileChild& other)
   : mTabChild(other.mTabChild)
-  , mNSPRFileDesc(other.mNSPRFileDesc)
+  , mNSPRFileDesc(nullptr)
   , mAsyncOpenCalled(other.mAsyncOpenCalled)
-  , mNSPROpenCalled(other.mNSPROpenCalled)
 {
+#if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
+  // Windows/OSX desktop builds skip remoting, so the file descriptor should
+  // be nullptr here.
+  MOZ_ASSERT(!other.mNSPRFileDesc);
+#else
+  if (other.mNSPRFileDesc) {
+    PROsfd osfd = dup(PR_FileDesc2NativeHandle(other.mNSPRFileDesc));
+    mNSPRFileDesc = PR_ImportFile(osfd);
+  }
+#endif
+
   // Note: don't clone mListener or we'll have a refcount leak.
   other.mURI->Clone(getter_AddRefs(mURI));
   if (other.mAppURI) {
     other.mAppURI->Clone(getter_AddRefs(mAppURI));
   }
   other.mFile->Clone(getter_AddRefs(mFile));
 }
 
@@ -118,18 +128,16 @@ RemoteOpenFileChild::~RemoteOpenFileChil
       unused << mURI.forget();
       unused << mAppURI.forget();
       unused << mListener.forget();
       unused << mTabChild.forget();
     }
   }
 
   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);
   }
 }
 
 nsresult
 RemoteOpenFileChild::Init(nsIURI* aRemoteOpenUri, nsIURI* aAppUri)
 {
@@ -342,21 +350,16 @@ RemoteOpenFileChild::Recv__delete__(cons
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 RemoteOpenFileChild::Clone(nsIFile **file)
 {
   *file = new RemoteOpenFileChild(*this);
   NS_ADDREF(*file);
 
-  // if we transferred ownership of file to clone, forget our pointer.
-  if (mNSPRFileDesc) {
-    mNSPRFileDesc = nullptr;
-  }
-
   return NS_OK;
 }
 
 /* The main event: get file descriptor from parent process
  */
 NS_IMETHODIMP
 RemoteOpenFileChild::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
                                       PRFileDesc **aRetval)
@@ -365,31 +368,23 @@ RemoteOpenFileChild::OpenNSPRFileDesc(in
   // Windows and OSX builds: just open nsIFile locally.
   return mFile->OpenNSPRFileDesc(aFlags, aMode, aRetval);
 
 #else
   if (aFlags != PR_RDONLY) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  // Unlike regular nsIFile we can't (easily) support multiple open()s.
-  if (mNSPROpenCalled) {
-    return NS_ERROR_ALREADY_OPENED;
-  }
-
   if (!mNSPRFileDesc) {
-    // client skipped AsyncRemoteFileOpen() or didn't wait for result, or this
-    // object has been cloned
+    // Client skipped AsyncRemoteFileOpen() or didn't wait for result.
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  // hand off ownership (i.e responsibility to PR_Close() file handle) to caller
-  *aRetval = mNSPRFileDesc;
-  mNSPRFileDesc = nullptr;
-  mNSPROpenCalled = true;
+  PROsfd osfd = dup(PR_FileDesc2NativeHandle(mNSPRFileDesc));
+  *aRetval = PR_ImportFile(osfd);
 
   return NS_OK;
 #endif
 }
 
 
 //-----------------------------------------------------------------------------
 // RemoteOpenFileChild::nsIFile functions that we delegate to underlying nsIFile
--- a/netwerk/protocol/app/AppProtocolHandler.cpp
+++ b/netwerk/protocol/app/AppProtocolHandler.cpp
@@ -122,16 +122,21 @@ NS_IMETHODIMP DummyChannel::SetAppURI(ns
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP DummyChannel::GetJarFile(nsIFile* *aFile)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP DummyChannel::EnsureChildFd()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 NS_IMETHODIMP DummyChannel::Run()
 {
   nsresult rv = mListener->OnStartRequest(this, mListenerContext);
   NS_ENSURE_SUCCESS(rv, rv);
   mPending = false;
   rv = mListener->OnStopRequest(this, mListenerContext, NS_ERROR_FILE_NOT_FOUND);
   NS_ENSURE_SUCCESS(rv, rv);
   if (mLoadGroup) {