--- a/dom/apps/src/AppsService.js
+++ b/dom/apps/src/AppsService.js
@@ -53,13 +53,23 @@ AppsService.prototype = {
return DOMApplicationRegistry.getManifestURLByLocalId(aLocalId);
},
getAppFromObserverMessage: function getAppFromObserverMessage(aMessage) {
debug("getAppFromObserverMessage( " + aMessage + " )");
return DOMApplicationRegistry.getAppFromObserverMessage(aMessage);
},
+ getCoreAppsBasePath: function getCoreAppsBasePath() {
+ debug("getCoreAppsBasePath()");
+ return DOMApplicationRegistry.getCoreAppsBasePath();
+ },
+
+ getWebAppsBasePath: function getWebAppsBasePath() {
+ debug("getWebAppsBasePath()");
+ return DOMApplicationRegistry.getWebAppsBasePath();
+ },
+
classID : APPS_SERVICE_CID,
QueryInterface : XPCOMUtils.generateQI([Ci.nsIAppsService])
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AppsService])
--- a/dom/apps/src/AppsServiceChild.jsm
+++ b/dom/apps/src/AppsServiceChild.jsm
@@ -81,12 +81,21 @@ this.DOMApplicationRegistry = {
getManifestURLByLocalId: function getManifestURLByLocalId(aLocalId) {
debug("getManifestURLByLocalId " + aLocalId);
return AppsUtils.getManifestURLByLocalId(this.webapps, aLocalId);
},
getAppFromObserverMessage: function getAppFromObserverMessage(aMessage) {
debug("getAppFromObserverMessage " + aMessage);
return AppsUtils.getAppFromObserverMessage(this.webapps. aMessage);
+ },
+ getCoreAppsBasePath: function getCoreAppsBasePath() {
+ debug("getCoreAppsBasePath() not yet supported on child!");
+ return null;
+ },
+
+ getWebAppsBasePath: function getWebAppsBasePath() {
+ debug("getWebAppsBasePath() not yet supported on child!");
+ return null;
}
}
DOMApplicationRegistry.init();
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -33,17 +33,19 @@ this.AppsUtils = {
csp: aApp.csp,
installOrigin: aApp.installOrigin,
origin: aApp.origin,
receipts: aApp.receipts ? JSON.parse(JSON.stringify(aApp.receipts)) : null,
installTime: aApp.installTime,
manifestURL: aApp.manifestURL,
appStatus: aApp.appStatus,
removable: aApp.removable,
+ id: aApp.id,
localId: aApp.localId,
+ basePath: aApp.basePath,
progress: aApp.progress || 0.0,
installState: aApp.installState || "installed",
downloadAvailable: aApp.downloadAvailable,
downloading: aApp.downloading,
readyToApplyDownload: aApp.readyToApplyDownload,
downloadSize: aApp.downloadSize || 0,
lastUpdateCheck: aApp.lastUpdateCheck,
updateTime: aApp.updateTime,
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -70,17 +70,17 @@ this.DOMApplicationRegistry = {
children: [ ],
allAppsLaunchable: false,
init: function() {
this.messages = ["Webapps:Install", "Webapps:Uninstall",
"Webapps:GetSelf", "Webapps:CheckInstalled",
"Webapps:GetInstalled", "Webapps:GetNotInstalled",
"Webapps:Launch", "Webapps:GetAll",
- "Webapps:InstallPackage", "Webapps:GetBasePath",
+ "Webapps:InstallPackage", "Webapps:GetAppInfo",
"Webapps:GetList", "Webapps:RegisterForMessages",
"Webapps:UnregisterForMessages",
"Webapps:CancelDownload", "Webapps:CheckForUpdate",
"Webapps:Download", "Webapps:ApplyDownload",
"child-process-shutdown"];
this.frameMessages = ["Webapps:ClearBrowserData"];
@@ -105,16 +105,19 @@ this.DOMApplicationRegistry = {
loadCurrentRegistry: function loadCurrentRegistry(aNext) {
let file = FileUtils.getFile(DIRECTORY_NAME, ["webapps", "webapps.json"], false);
if (file && file.exists()) {
this._loadJSONAsync(file, (function loadRegistry(aData) {
if (aData) {
this.webapps = aData;
let appDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps"], false);
for (let id in this.webapps) {
+
+ this.webapps[id].id = id;
+
// Make sure we have a localId
if (this.webapps[id].localId === undefined) {
this.webapps[id].localId = this._nextLocalId();
}
if (this.webapps[id].basePath === undefined) {
this.webapps[id].basePath = appDir.path;
}
@@ -210,17 +213,17 @@ this.DOMApplicationRegistry = {
},
// Installs a 3rd party packaged app.
installPreinstalledPackage: function installPreinstalledPackage(aId) {
#ifdef MOZ_WIDGET_GONK
let app = this.webapps[aId];
let baseDir;
try {
- baseDir = FileUtils.getDir("coreAppsDir", ["webapps", aId], true, true);
+ baseDir = FileUtils.getDir("coreAppsDir", ["webapps", aId], false);
} catch(e) {
// In ENG builds, we don't have apps in coreAppsDir.
return;
}
let updateFile = baseDir.clone();
updateFile.append("update.webapp");
if (!updateFile.exists()) {
@@ -315,16 +318,18 @@ this.DOMApplicationRegistry = {
// c
for (let id in aData) {
// Core apps have ids matching their domain name (eg: dialer.gaiamobile.org)
// Use that property to check if they are new or not.
if (!(id in this.webapps)) {
this.webapps[id] = aData[id];
this.webapps[id].basePath = appDir.path;
+ this.webapps[id].id = id;
+
// Create a new localId.
this.webapps[id].localId = this._nextLocalId();
// Core apps are not removable.
if (this.webapps[id].removable === undefined) {
this.webapps[id].removable = false;
}
}
@@ -774,22 +779,23 @@ this.DOMApplicationRegistry = {
if (msg.hasPrivileges)
this.getAll(msg, mm);
else
mm.sendAsyncMessage("Webapps:GetAll:Return:KO", msg);
break;
case "Webapps:InstallPackage":
this.doInstallPackage(msg, mm);
break;
- case "Webapps:GetBasePath":
+ case "Webapps:GetAppInfo":
if (!this.webapps[msg.id]) {
debug("No webapp for " + msg.id);
return null;
}
- return this.webapps[msg.id].basePath;
+ return { "basePath": this.webapps[msg.id].basePath + "/",
+ "isCoreApp": !this.webapps[msg.id].removable };
break;
case "Webapps:RegisterForMessages":
this.addMessageListener(msg, mm);
break;
case "Webapps:UnregisterForMessages":
this.removeMessageListener(msg, mm);
break;
case "child-process-shutdown":
@@ -1474,19 +1480,19 @@ this.DOMApplicationRegistry = {
isReinstall = true;
let dir = this._getAppDir(id);
try {
dir.remove(true);
} catch(e) {
}
} else {
id = this.makeAppId();
- app.id = id;
localId = this._nextLocalId();
}
+ app.id = id;
let manifestName = "manifest.webapp";
if (aData.isPackage) {
// Override the origin with the correct id.
app.origin = "app://" + id;
// For packaged apps, keep the update manifest distinct from the app
// manifest.
@@ -1501,16 +1507,17 @@ this.DOMApplicationRegistry = {
appObject.appStatus = AppsUtils.getAppManifestStatus(app.manifest);
}
appObject.installTime = app.installTime = Date.now();
appObject.lastUpdateCheck = app.lastUpdateCheck = Date.now();
let appNote = JSON.stringify(appObject);
appNote.id = id;
+ appObject.id = id;
appObject.localId = localId;
appObject.basePath = FileUtils.getDir(DIRECTORY_NAME, ["webapps"], true, true).path;
let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
dir.permissions = FileUtils.PERMS_DIRECTORY;
let manFile = dir.clone();
manFile.append(manifestName);
let jsonManifest = aData.isPackage ? app.updateManifest : app.manifest;
this._writeFile(manFile, JSON.stringify(jsonManifest), function() { });
@@ -2184,16 +2191,24 @@ this.DOMApplicationRegistry = {
getAppLocalIdByManifestURL: function(aManifestURL) {
return AppsUtils.getAppLocalIdByManifestURL(this.webapps, aManifestURL);
},
getAppFromObserverMessage: function(aMessage) {
return AppsUtils.getAppFromObserverMessage(this.webapps, aMessage);
},
+ getCoreAppsBasePath: function() {
+ return FileUtils.getDir("coreAppsDir", ["webapps"], false).path;
+ },
+
+ getWebAppsBasePath: function getWebAppsBasePath() {
+ return FileUtils.getDir(DIRECTORY_NAME, ["webapps"], false).path;
+ },
+
getAllWithoutManifests: function(aCallback) {
let result = {};
for (let id in this.webapps) {
let app = AppsUtils.cloneAppObject(this.webapps[id]);
result[id] = app;
}
aCallback(result);
},
--- a/dom/interfaces/apps/mozIApplication.idl
+++ b/dom/interfaces/apps/mozIApplication.idl
@@ -6,26 +6,32 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIDOMApplicationRegistry.idl"
/**
* We expose Gecko-internal helpers related to "web apps" through this
* sub-interface.
*/
-[scriptable, uuid(8ac7827f-f982-40fb-be11-ba16dd665635)]
+[scriptable, uuid(cfa75628-4d31-481f-b51e-fe0ce18fa98f)]
interface mozIApplication: mozIDOMApplication
{
/* Return true if this app has |permission|. */
boolean hasPermission(in string permission);
/* Application status as defined in nsIPrincipal. */
readonly attribute unsigned short appStatus;
- /* Returns the local id of the app (not the uuid used for sync). */
+ /* Returns the uuid of the app. */
+ readonly attribute DOMString id;
+
+ /* Returns the local id of the app. */
readonly attribute unsigned long localId;
+ /* Returns the base directory for the app */
+ readonly attribute DOMString basePath;
+
/* Name copied from the manifest */
readonly attribute DOMString name;
/* CSP copied from the manifest */
readonly attribute DOMString csp;
};
--- a/dom/interfaces/apps/nsIAppsService.idl
+++ b/dom/interfaces/apps/nsIAppsService.idl
@@ -11,17 +11,17 @@ interface mozIApplication;
#define APPS_SERVICE_CID { 0x05072afa, 0x92fe, 0x45bf, { 0xae, 0x22, 0x39, 0xb6, 0x9c, 0x11, 0x70, 0x58 } }
#define APPS_SERVICE_CONTRACTID "@mozilla.org/AppsService;1"
%}
/*
* This service allows accessing some DOMApplicationRegistry methods from
* non-javascript code.
*/
-[scriptable, uuid(4a182c18-dbdf-4f9c-93a0-0f0cffb88ed0)]
+[scriptable, uuid(e65f9397-e191-4273-aa5f-f13c185ce63b)]
interface nsIAppsService : nsISupports
{
mozIDOMApplication getAppByManifestURL(in DOMString manifestURL);
/**
* Returns the |localId| of the app associated with the |manifestURL| passed
* in parameter.
* Returns nsIScriptSecurityManager::NO_APP_ID if |manifestURL| isn't a valid
@@ -45,9 +45,19 @@ interface nsIAppsService : nsISupports
* of the message when listening to one.
*/
mozIApplication getAppFromObserverMessage(in DOMString message);
/**
* Returns the CSP associated to this localId.
*/
DOMString getCSPByLocalId(in unsigned long localId);
+
+ /**
+ * Returns the basepath for core apps
+ */
+ DOMString getCoreAppsBasePath();
+
+ /**
+ * Returns the basepath for regular packaged apps
+ */
+ DOMString getWebAppsBasePath();
};
--- a/modules/libjar/nsIZipReader.idl
+++ b/modules/libjar/nsIZipReader.idl
@@ -180,17 +180,17 @@ interface nsIZipReader : nsISupports
nsICertificatePrincipal getCertificatePrincipal(in AUTF8String aEntryName);
readonly attribute uint32_t manifestEntriesCount;
};
////////////////////////////////////////////////////////////////////////////////
// nsIZipReaderCache
-[scriptable, uuid(72fc56e5-3e6e-4d11-8967-26ab96071032)]
+[scriptable, uuid(748050ac-3ab6-4472-bc2a-cb1564ac6a81)]
interface nsIZipReaderCache : nsISupports
{
/**
* Initializes a new zip reader cache.
* @param cacheSize - the number of released entries to maintain before
* beginning to throw some out (note that the number of outstanding
* entries can be much greater than this number -- this is the count
* for those otherwise unused entries)
@@ -206,16 +206,21 @@ interface nsIZipReaderCache : nsISupport
* returned.
*
* @note If someone called close() on the shared nsIZipReader, this method
* will return the closed zip reader.
*/
nsIZipReader getZip(in nsIFile zipFile);
/**
+ * returns true if this zipreader already has this file cached
+ */
+ bool isCached(in nsIFile zipFile);
+
+ /**
* Returns a (possibly shared) nsIZipReader for a zip inside another zip
*
* See getZip
*/
nsIZipReader getInnerZip(in nsIFile zipFile, in AUTF8String zipEntry);
};
////////////////////////////////////////////////////////////////////////////////
--- a/modules/libjar/nsJAR.cpp
+++ b/modules/libjar/nsJAR.cpp
@@ -1058,16 +1058,37 @@ nsZipReaderCache::~nsZipReaderCache()
printf("nsZipReaderCache size=%d hits=%d lookups=%d rate=%f%% flushes=%d missed %d\n",
mCacheSize, mZipCacheHits, mZipCacheLookups,
(float)mZipCacheHits / mZipCacheLookups,
mZipCacheFlushes, mZipSyncMisses);
#endif
}
NS_IMETHODIMP
+nsZipReaderCache::IsCached(nsIFile* zipFile, bool* aResult)
+{
+ NS_ENSURE_ARG_POINTER(zipFile);
+ nsresult rv;
+ nsCOMPtr<nsIZipReader> antiLockZipGrip;
+ MutexAutoLock lock(mLock);
+
+ nsAutoCString uri;
+ rv = zipFile->GetNativePath(uri);
+ if (NS_FAILED(rv))
+ return rv;
+
+ uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
+
+ nsCStringKey key(uri);
+
+ *aResult = mZips.Exists(&key);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader* *result)
{
NS_ENSURE_ARG_POINTER(zipFile);
nsresult rv;
nsCOMPtr<nsIZipReader> antiLockZipGrip;
MutexAutoLock lock(mLock);
#ifdef ZIP_CACHE_HIT_RATE
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -1,11 +1,11 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 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 "nsJAR.h"
#include "nsJARChannel.h"
#include "nsJARProtocolHandler.h"
#include "nsMimeTypes.h"
#include "nsNetUtil.h"
@@ -13,20 +13,24 @@
#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;
static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
// the entry for a directory will either be empty (in the case of the
// top-level directory) or will end with a slash
#define ENTRY_IS_DIRECTORY(_entry) \
((_entry).IsEmpty() || '/' == (_entry).Last())
@@ -183,16 +187,17 @@ nsJARInputThunk::IsNonBlocking(bool *non
nsJARChannel::nsJARChannel()
: mOpened(false)
, mAppURI(nullptr)
, mContentLength(-1)
, mLoadFlags(LOAD_NORMAL)
, mStatus(NS_OK)
, mIsPending(false)
, mIsUnsafe(true)
+ , mOpeningRemote(false)
{
#if defined(PR_LOGGING)
if (!gJarProtocolLog)
gJarProtocolLog = PR_NewLogModule("nsJarProtocol");
#endif
// hold an owning reference to the jar handler
NS_ADDREF(gJarHandler);
@@ -200,23 +205,24 @@ nsJARChannel::nsJARChannel()
nsJARChannel::~nsJARChannel()
{
// release owning reference to the jar handler
nsJARProtocolHandler *handler = gJarHandler;
NS_RELEASE(handler); // NULL parameter
}
-NS_IMPL_ISUPPORTS_INHERITED6(nsJARChannel,
+NS_IMPL_ISUPPORTS_INHERITED7(nsJARChannel,
nsHashPropertyBag,
nsIRequest,
nsIChannel,
nsIStreamListener,
nsIRequestObserver,
nsIDownloadObserver,
+ nsIRemoteOpenFileListener,
nsIJARChannel)
nsresult
nsJARChannel::Init(nsIURI *uri)
{
nsresult rv;
rv = nsHashPropertyBag::Init();
if (NS_FAILED(rv))
@@ -258,27 +264,27 @@ nsJARChannel::CreateJarInput(nsIZipReade
nsCOMPtr<nsIFile> clonedFile;
nsresult rv = mJarFile->Clone(getter_AddRefs(clonedFile));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIZipReader> reader;
if (jarCache) {
if (mInnerJarEntry.IsEmpty())
- rv = jarCache->GetZip(mJarFile, getter_AddRefs(reader));
+ rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader));
else
- rv = jarCache->GetInnerZip(mJarFile, mInnerJarEntry,
+ rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry,
getter_AddRefs(reader));
} else {
// create an uncached jar reader
nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv);
if (NS_FAILED(rv))
return rv;
- rv = outerReader->Open(mJarFile);
+ rv = outerReader->Open(clonedFile);
if (NS_FAILED(rv))
return rv;
if (mInnerJarEntry.IsEmpty())
reader = outerReader;
else {
reader = do_CreateInstance(kZipReaderCID, &rv);
if (NS_FAILED(rv))
@@ -329,16 +335,47 @@ nsJARChannel::LookupFile()
NS_UnescapeURL(mJarEntry);
// 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) {
+ nsAutoCString scheme;
+ nsresult 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) {
+ 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;
+ }
+ }
+
+ // 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;
rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI));
if (NS_SUCCEEDED(rv))
@@ -650,17 +687,17 @@ nsJARChannel::Open(nsIInputStream **stre
return rv;
// If mJarInput was not set by LookupFile, the JAR is a remote jar.
if (!mJarFile) {
NS_NOTREACHED("need sync downloader");
return NS_ERROR_NOT_IMPLEMENTED;
}
- nsCOMPtr<nsJARInputThunk> input;
+ nsRefPtr<nsJARInputThunk> input;
rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
if (NS_FAILED(rv))
return rv;
input.forget(stream);
mOpened = true;
// local files are always considered safe
mIsUnsafe = false;
@@ -697,22 +734,23 @@ nsJARChannel::AsyncOpen(nsIStreamListene
if (!mJarFile) {
// Not a local file...
// kick off an async download of the base URI...
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 {
+ } else if (mOpeningRemote) {
+ // nothing to do: already asked parent to open file.
+ } else {
// local files are always considered safe
mIsUnsafe = false;
- nsCOMPtr<nsJARInputThunk> input;
+ 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);
}
}
@@ -840,17 +878,17 @@ nsJARChannel::OnDownloadComplete(nsIDown
if (viewSource) {
status = NS_ERROR_UNSAFE_CONTENT_TYPE;
}
}
if (NS_SUCCEEDED(status)) {
mJarFile = file;
- nsCOMPtr<nsJARInputThunk> input;
+ nsRefPtr<nsJARInputThunk> input;
rv = CreateJarInput(nullptr, getter_AddRefs(input));
if (NS_SUCCEEDED(rv)) {
// create input stream pump
rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input);
if (NS_SUCCEEDED(rv))
rv = mPump->AsyncRead(this, nullptr);
}
status = rv;
@@ -861,16 +899,48 @@ nsJARChannel::OnDownloadComplete(nsIDown
OnStartRequest(nullptr, nullptr);
OnStopRequest(nullptr, nullptr, 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);
+ }
+ }
+
+ if (NS_FAILED(rv)) {
+ mStatus = rv;
+ OnStartRequest(nullptr, nullptr);
+ OnStopRequest(nullptr, nullptr, mStatus);
+ }
+
+ 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
@@ -7,16 +7,17 @@
#define nsJARChannel_h__
#include "nsIJARChannel.h"
#include "nsIJARURI.h"
#include "nsIInputStreamPump.h"
#include "nsIInterfaceRequestor.h"
#include "nsIProgressEventSink.h"
#include "nsIStreamListener.h"
+#include "nsIRemoteOpenFileListener.h"
#include "nsIZipReader.h"
#include "nsIDownloader.h"
#include "nsILoadGroup.h"
#include "nsHashPropertyBag.h"
#include "nsIFile.h"
#include "nsIURI.h"
#include "nsCOMPtr.h"
#include "nsString.h"
@@ -24,26 +25,28 @@
class nsJARInputThunk;
//-----------------------------------------------------------------------------
class nsJARChannel : public nsIJARChannel
, public nsIDownloadObserver
, public nsIStreamListener
+ , public nsIRemoteOpenFileListener
, public nsHashPropertyBag
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUEST
NS_DECL_NSICHANNEL
NS_DECL_NSIJARCHANNEL
NS_DECL_NSIDOWNLOADOBSERVER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIREMOTEOPENFILELISTENER
nsJARChannel();
virtual ~nsJARChannel();
nsresult Init(nsIURI *uri);
private:
nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **);
@@ -71,16 +74,17 @@ private:
/* mContentDisposition is uninitialized if mContentDispositionHeader is
* empty */
uint32_t mContentDisposition;
int64_t mContentLength;
uint32_t mLoadFlags;
nsresult mStatus;
bool mIsPending;
bool mIsUnsafe;
+ bool mOpeningRemote;
nsCOMPtr<nsIStreamListener> mDownloader;
nsCOMPtr<nsIInputStreamPump> mPump;
nsCOMPtr<nsIFile> mJarFile;
nsCOMPtr<nsIURI> mJarBaseURI;
nsCString mJarEntry;
nsCString mInnerJarEntry;
};
new file mode 100644
--- /dev/null
+++ b/modules/libjar/test/unit/head_ipc.js
@@ -0,0 +1,17 @@
+/*
+ * If we're running in e10s, determines whether we're in child directory or
+ * not.
+ */
+
+var inChild = false;
+var filePrefix = "";
+try {
+ inChild = Components.classes["@mozilla.org/xre/runtime;1"].
+ getService(Components.interfaces.nsIXULRuntime).processType
+ != Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+ if (inChild) {
+ // use "jar:remoteopenfile://" in child instead of "jar:file://"
+ filePrefix = "remoteopen";
+ }
+}
+catch (e) { }
--- a/modules/libjar/test/unit/test_jarchannel.js
+++ b/modules/libjar/test/unit/test_jarchannel.js
@@ -22,17 +22,18 @@ const obs = Cc["@mozilla.org/observer-se
const nsIBinaryInputStream = ctor("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream"
);
const fileBase = "test_bug637286.zip";
const file = do_get_file("data/" + fileBase);
-const jarBase = "jar:" + ios.newFileURI(file).spec + "!";
+// on child we'll test with jar:remoteopenfile:// instead of jar:file://
+const jarBase = "jar:" + filePrefix + ios.newFileURI(file).spec + "!";
const tmpDir = dirSvc.get("TmpD", Ci.nsIFile);
function Listener(callback) {
this._callback = callback;
}
Listener.prototype = {
gotStartRequest: false,
available: -1,
@@ -61,127 +62,141 @@ Listener.prototype = {
this.gotStopRequest = true;
if (this._callback) {
this._callback.call(null, this);
}
}
};
/**
- * Basic reading test for synchronously opened jar channels
- */
-add_test(function testSync() {
- var uri = jarBase + "/inner40.zip";
- var chan = ios.newChannel(uri, null, null);
- var stream = chan.open();
- do_check_true(chan.contentLength > 0);
- do_check_eq(stream.available(), chan.contentLength);
- stream.close();
- stream.close(); // should still not throw
-
- run_next_test();
-});
-
-/**
* Basic reading test for asynchronously opened jar channel
*/
-add_test(function testAsync() {
+function testAsync() {
var uri = jarBase + "/inner40.zip";
var chan = ios.newChannel(uri, null, null);
do_check_true(chan.contentLength < 0);
chan.asyncOpen(new Listener(function(l) {
do_check_true(chan.contentLength > 0);
do_check_true(l.gotStartRequest);
do_check_true(l.gotStopRequest);
do_check_eq(l.available, chan.contentLength);
run_next_test();
}), null);
-});
+}
+
+add_test(testAsync);
+// Run same test again so we test the codepath for a zipcache hit
+add_test(testAsync);
+
+
+// In e10s child processes we don't currently support
+// 1) synchronously opening jar files on parent
+// 2) nested jar channels in e10s: (app:// doesn't use them).
+// 3) we can't do file lock checks on android, so skip those tests too.
+if (!inChild) {
-/**
- * Basic reading test for synchronously opened, nested jar channels
- */
-add_test(function testSyncNested() {
- var uri = "jar:" + jarBase + "/inner40.zip!/foo";
- var chan = ios.newChannel(uri, null, null);
- var stream = chan.open();
- do_check_true(chan.contentLength > 0);
- do_check_eq(stream.available(), chan.contentLength);
- stream.close();
- stream.close(); // should still not throw
+ /**
+ * Basic reading test for synchronously opened jar channels
+ */
+ add_test(function testSync() {
+ var uri = jarBase + "/inner40.zip";
+ var chan = ios.newChannel(uri, null, null);
+ var stream = chan.open();
+ do_check_true(chan.contentLength > 0);
+ do_check_eq(stream.available(), chan.contentLength);
+ stream.close();
+ stream.close(); // should still not throw
- run_next_test();
-});
+ run_next_test();
+ });
-/**
- * Basic reading test for asynchronously opened, nested jar channels
- */
-add_test(function testAsyncNested(next) {
- var uri = "jar:" + jarBase + "/inner40.zip!/foo";
- var chan = ios.newChannel(uri, null, null);
- chan.asyncOpen(new Listener(function(l) {
- do_check_true(chan.contentLength > 0);
- do_check_true(l.gotStartRequest);
- do_check_true(l.gotStopRequest);
- do_check_eq(l.available, chan.contentLength);
+
+ /**
+ * Basic reading test for synchronously opened, nested jar channels
+ */
+ add_test(function testSyncNested() {
+ var uri = "jar:" + jarBase + "/inner40.zip!/foo";
+ var chan = ios.newChannel(uri, null, null);
+ var stream = chan.open();
+ do_check_true(chan.contentLength > 0);
+ do_check_eq(stream.available(), chan.contentLength);
+ stream.close();
+ stream.close(); // should still not throw
- run_next_test();
- }), null);
-});
+ run_next_test();
+ });
-/**
- * Verify that file locks are released when closing a synchronously
- * opened jar channel stream
- */
-add_test(function testSyncCloseUnlocks() {
- var copy = tmpDir.clone();
- copy.append(fileBase);
- file.copyTo(copy.parent, copy.leafName);
+ /**
+ * Basic reading test for asynchronously opened, nested jar channels
+ */
+ add_test(function testAsyncNested(next) {
+ var uri = "jar:" + jarBase + "/inner40.zip!/foo";
+ var chan = ios.newChannel(uri, null, null);
+ chan.asyncOpen(new Listener(function(l) {
+ do_check_true(chan.contentLength > 0);
+ do_check_true(l.gotStartRequest);
+ do_check_true(l.gotStopRequest);
+ do_check_eq(l.available, chan.contentLength);
- var uri = "jar:" + ios.newFileURI(copy).spec + "!/inner40.zip";
- var chan = ios.newChannel(uri, null, null);
- var stream = chan.open();
- do_check_true(chan.contentLength > 0);
- stream.close();
+ run_next_test();
+ }), null);
+ });
- // Drop any jar caches
- obs.notifyObservers(null, "chrome-flush-caches", null);
+ /**
+ * Verify that file locks are released when closing a synchronously
+ * opened jar channel stream
+ */
+ add_test(function testSyncCloseUnlocks() {
+ var copy = tmpDir.clone();
+ copy.append(fileBase);
+ file.copyTo(copy.parent, copy.leafName);
- try {
- copy.remove(false);
- }
- catch (ex) {
- do_throw(ex);
- }
+ var uri = "jar:" + ios.newFileURI(copy).spec + "!/inner40.zip";
+ var chan = ios.newChannel(uri, null, null);
+ var stream = chan.open();
+ do_check_true(chan.contentLength > 0);
+ stream.close();
- run_next_test();
-});
+ // Drop any jar caches
+ obs.notifyObservers(null, "chrome-flush-caches", null);
+
+ try {
+ copy.remove(false);
+ }
+ catch (ex) {
+ do_throw(ex);
+ }
-/**
- * Verify that file locks are released when closing an asynchronously
- * opened jar channel stream
- */
-add_test(function testAsyncCloseUnlocks() {
- var copy = tmpDir.clone();
- copy.append(fileBase);
- file.copyTo(copy.parent, copy.leafName);
+ run_next_test();
+ });
- var uri = "jar:" + ios.newFileURI(copy).spec + "!/inner40.zip";
- var chan = ios.newChannel(uri, null, null);
- chan.asyncOpen(new Listener(function (l) {
- do_check_true(chan.contentLength > 0);
+ /**
+ * Verify that file locks are released when closing an asynchronously
+ * opened jar channel stream
+ */
+ add_test(function testAsyncCloseUnlocks() {
+ var copy = tmpDir.clone();
+ copy.append(fileBase);
+ file.copyTo(copy.parent, copy.leafName);
- // Drop any jar caches
- obs.notifyObservers(null, "chrome-flush-caches", null);
+ var uri = "jar:" + ios.newFileURI(copy).spec + "!/inner40.zip";
+ var chan = ios.newChannel(uri, null, null);
+ chan.asyncOpen(new Listener(function (l) {
+ do_check_true(chan.contentLength > 0);
+
+ // Drop any jar caches
+ obs.notifyObservers(null, "chrome-flush-caches", null);
- try {
- copy.remove(false);
- }
- catch (ex) {
- do_throw(ex);
- }
+ try {
+ copy.remove(false);
+ }
+ catch (ex) {
+ do_throw(ex);
+ }
- run_next_test();
- }), null);
-});
+ run_next_test();
+ }), null);
+ });
+
+} // if !inChild
function run_test() run_next_test();
new file mode 100644
--- /dev/null
+++ b/modules/libjar/test/unit/test_jarchannel_e10s.js
@@ -0,0 +1,7 @@
+//
+// Run test script in content process instead of chrome (xpcshell's default)
+//
+
+function run_test() {
+ run_test_in_child("../unit/test_jarchannel.js");
+}
--- a/modules/libjar/test/unit/xpcshell.ini
+++ b/modules/libjar/test/unit/xpcshell.ini
@@ -1,13 +1,15 @@
[DEFAULT]
-head =
+head = head_ipc.js
tail =
[test_jarchannel.js]
+[test_jarchannel_e10s.js]
+skip-if = os == "mac" || os == "windows"
[test_bug278262.js]
[test_bug333423.js]
[test_bug336691.js]
[test_bug370103.js]
[test_bug379841.js]
[test_bug407303.js]
[test_bug453254.js]
[test_bug458158.js]
--- a/netwerk/ipc/Makefile.in
+++ b/netwerk/ipc/Makefile.in
@@ -11,31 +11,40 @@ FAIL_ON_WARNINGS := 1
include $(DEPTH)/config/autoconf.mk
MODULE = necko
LIBRARY_NAME = neckoipc_s
LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
EXPORT_LIBRARY = 1
+XPIDL_MODULE = necko_ipc
+
+XPIDLSRCS = \
+ nsIRemoteOpenFileListener.idl \
+ $(NULL)
EXPORTS_NAMESPACES = mozilla/net
EXPORTS_mozilla/net = \
NeckoParent.h \
NeckoChild.h \
NeckoCommon.h \
NeckoMessageUtils.h \
ChannelEventQueue.h \
+ RemoteOpenFileParent.h \
+ RemoteOpenFileChild.h \
$(NULL)
CPPSRCS = \
NeckoChild.cpp \
NeckoParent.cpp \
ChannelEventQueue.cpp \
+ RemoteOpenFileParent.cpp \
+ RemoteOpenFileChild.cpp \
$(NULL)
LOCAL_INCLUDES += \
-I$(srcdir)/../protocol/http \
-I$(srcdir)/../base/src \
$(NULL)
include $(topsrcdir)/config/config.mk
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -8,16 +8,17 @@
#include "nsHttp.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/net/HttpChannelChild.h"
#include "mozilla/net/CookieServiceChild.h"
#include "mozilla/net/WyciwygChannelChild.h"
#include "mozilla/net/FTPChannelChild.h"
#include "mozilla/net/WebSocketChannelChild.h"
+#include "mozilla/net/RemoteOpenFileChild.h"
#include "mozilla/dom/network/TCPSocketChild.h"
#include "mozilla/Preferences.h"
using mozilla::dom::TCPSocketChild;
namespace mozilla {
namespace net {
@@ -167,10 +168,27 @@ NeckoChild::AllocPTCPSocket(const nsStri
bool
NeckoChild::DeallocPTCPSocket(PTCPSocketChild* child)
{
TCPSocketChild* p = static_cast<TCPSocketChild*>(child);
p->ReleaseIPDLReference();
return true;
}
+PRemoteOpenFileChild*
+NeckoChild::AllocPRemoteOpenFile(const URIParams&, PBrowserChild*)
+{
+ // We don't allocate here: instead we always use IPDL constructor that takes
+ // an existing RemoteOpenFileChild
+ NS_NOTREACHED("AllocPRemoteOpenFile should not be called on child");
+ return nullptr;
+}
+
+bool
+NeckoChild::DeallocPRemoteOpenFile(PRemoteOpenFileChild* aChild)
+{
+ RemoteOpenFileChild *p = static_cast<RemoteOpenFileChild*>(aChild);
+ p->ReleaseIPDLReference();
+ return true;
+}
+
}} // mozilla::net
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -38,16 +38,19 @@ protected:
virtual PWebSocketChild* AllocPWebSocket(PBrowserChild*);
virtual bool DeallocPWebSocket(PWebSocketChild*);
virtual PTCPSocketChild* AllocPTCPSocket(const nsString& aHost,
const uint16_t& aPort,
const bool& useSSL,
const nsString& aBinaryType,
PBrowserChild* aBrowser);
virtual bool DeallocPTCPSocket(PTCPSocketChild*);
+ virtual PRemoteOpenFileChild* AllocPRemoteOpenFile(const URIParams&,
+ PBrowserChild*);
+ virtual bool DeallocPRemoteOpenFile(PRemoteOpenFileChild*);
};
/**
* Reference to the PNecko Child protocol.
* Null if this is not a content process.
*/
extern PNeckoChild *gNeckoChild;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -7,36 +7,55 @@
#include "nsHttp.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/net/HttpChannelParent.h"
#include "mozilla/net/CookieServiceParent.h"
#include "mozilla/net/WyciwygChannelParent.h"
#include "mozilla/net/FTPChannelParent.h"
#include "mozilla/net/WebSocketChannelParent.h"
+#include "mozilla/net/RemoteOpenFileParent.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/dom/network/TCPSocketParent.h"
+#include "mozilla/ipc/URIUtils.h"
#include "mozilla/Preferences.h"
#include "nsHTMLDNSPrefetch.h"
+#include "nsIAppsService.h"
+#include "nsEscape.h"
using mozilla::dom::TabParent;
using mozilla::net::PTCPSocketParent;
using mozilla::dom::TCPSocketParent;
namespace mozilla {
namespace net {
static bool gDisableIPCSecurity = false;
static const char kPrefDisableIPCSecurity[] = "network.disable.ipc.security";
// C++ file contents
NeckoParent::NeckoParent()
{
Preferences::AddBoolVarCache(&gDisableIPCSecurity, kPrefDisableIPCSecurity);
+
+ if (!gDisableIPCSecurity) {
+ // cache values for core/packaged apps basepaths
+ nsAutoString corePath, webPath;
+ nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+ if (appsService) {
+ appsService->GetCoreAppsBasePath(corePath);
+ appsService->GetWebAppsBasePath(webPath);
+ }
+ // corePath may be empty: we don't use it for all build types
+ MOZ_ASSERT(!webPath.IsEmpty());
+
+ LossyCopyUTF16toASCII(corePath, mCoreAppsBasePath);
+ LossyCopyUTF16toASCII(webPath, mWebAppsBasePath);
+ }
}
NeckoParent::~NeckoParent()
{
}
PHttpChannelParent*
NeckoParent::AllocPHttpChannel(PBrowserParent* browser,
@@ -144,16 +163,126 @@ NeckoParent::RecvPTCPSocketConstructor(P
bool
NeckoParent::DeallocPTCPSocket(PTCPSocketParent* actor)
{
TCPSocketParent* p = static_cast<TCPSocketParent*>(actor);
p->Release();
return true;
}
+PRemoteOpenFileParent*
+NeckoParent::AllocPRemoteOpenFile(const URIParams& aURI,
+ PBrowserParent* aBrowser)
+{
+ nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
+ if (!fileURL) {
+ return nullptr;
+ }
+
+ // security checks
+ if (!gDisableIPCSecurity) {
+ if (!aBrowser) {
+ NS_WARNING("NeckoParent::AllocPRemoteOpenFile: "
+ "FATAL error: missing TabParent: KILLING CHILD PROCESS\n");
+ return nullptr;
+ }
+ nsRefPtr<TabParent> tabParent = static_cast<TabParent*>(aBrowser);
+ uint32_t appId = tabParent->OwnOrContainingAppId();
+ nsCOMPtr<nsIAppsService> appsService =
+ do_GetService(APPS_SERVICE_CONTRACTID);
+ if (!appsService) {
+ return nullptr;
+ }
+ nsCOMPtr<mozIDOMApplication> domApp;
+ nsresult rv = appsService->GetAppByLocalId(appId, getter_AddRefs(domApp));
+ if (!domApp) {
+ return nullptr;
+ }
+ nsCOMPtr<mozIApplication> mozApp = do_QueryInterface(domApp);
+ if (!mozApp) {
+ return nullptr;
+ }
+ bool hasManage = false;
+ rv = mozApp->HasPermission("webapps-manage", &hasManage);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ nsAutoCString requestedPath;
+ fileURL->GetPath(requestedPath);
+ NS_UnescapeURL(requestedPath);
+
+ if (hasManage) {
+ // webapps-manage permission means allow reading any application.zip file
+ // in either the regular webapps directory, or the core apps directory (if
+ // we're using one).
+ NS_NAMED_LITERAL_CSTRING(appzip, "/application.zip");
+ nsAutoCString pathEnd;
+ requestedPath.Right(pathEnd, appzip.Length());
+ if (!pathEnd.Equals(appzip)) {
+ return nullptr;
+ }
+ nsAutoCString pathStart;
+ requestedPath.Left(pathStart, mWebAppsBasePath.Length());
+ if (!pathStart.Equals(mWebAppsBasePath)) {
+ if (mCoreAppsBasePath.IsEmpty()) {
+ return nullptr;
+ }
+ requestedPath.Left(pathStart, mCoreAppsBasePath.Length());
+ if (!pathStart.Equals(mCoreAppsBasePath)) {
+ return nullptr;
+ }
+ }
+ // Finally: make sure there are no "../" in URI.
+ // Note: not checking for symlinks (would cause I/O for each path
+ // component). So it's up to us to avoid creating symlinks that could
+ // provide attack vectors.
+ if (PL_strnstr(requestedPath.BeginReading(), "/../",
+ requestedPath.Length())) {
+ NS_WARNING("NeckoParent::AllocPRemoteOpenFile: "
+ "FATAL error: requested file URI contains '/../' "
+ "KILLING CHILD PROCESS\n");
+ return nullptr;
+ }
+ } else {
+ // regular packaged apps can only access their own application.zip file
+ nsAutoString basePath;
+ rv = mozApp->GetBasePath(basePath);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+ nsAutoString uuid;
+ rv = mozApp->GetId(uuid);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+ nsPrintfCString mustMatch("%s/%s/application.zip",
+ NS_LossyConvertUTF16toASCII(basePath).get(),
+ NS_LossyConvertUTF16toASCII(uuid).get());
+ if (!requestedPath.Equals(mustMatch)) {
+ NS_WARNING("NeckoParent::AllocPRemoteOpenFile: "
+ "FATAL error: requesting file other than application.zip: "
+ "KILLING CHILD PROCESS\n");
+ return nullptr;
+ }
+ }
+ }
+
+ RemoteOpenFileParent* parent = new RemoteOpenFileParent(fileURL);
+ return parent;
+}
+
+bool
+NeckoParent::DeallocPRemoteOpenFile(PRemoteOpenFileParent* actor)
+{
+ delete actor;
+ return true;
+}
+
bool
NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname,
const uint16_t& flags)
{
nsHTMLDNSPrefetch::Prefetch(hostname, flags);
return true;
}
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -34,27 +34,35 @@ protected:
virtual bool DeallocPFTPChannel(PFTPChannelParent*);
virtual PWebSocketParent* AllocPWebSocket(PBrowserParent* browser);
virtual bool DeallocPWebSocket(PWebSocketParent*);
virtual PTCPSocketParent* AllocPTCPSocket(const nsString& aHost,
const uint16_t& aPort,
const bool& useSSL,
const nsString& aBinaryType,
PBrowserParent* aBrowser);
+ virtual PRemoteOpenFileParent* AllocPRemoteOpenFile(
+ const URIParams& fileuri,
+ PBrowserParent* browser);
+ virtual bool DeallocPRemoteOpenFile(PRemoteOpenFileParent* actor);
+
virtual bool RecvPTCPSocketConstructor(PTCPSocketParent*,
const nsString& aHost,
const uint16_t& aPort,
const bool& useSSL,
const nsString& aBinaryType,
PBrowserParent* aBrowser);
virtual bool DeallocPTCPSocket(PTCPSocketParent*);
virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
const uint16_t& flags);
virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
const uint16_t& flags,
const nsresult& reason);
+private:
+ nsCString mCoreAppsBasePath;
+ nsCString mWebAppsBasePath;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_NeckoParent_h
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -8,16 +8,18 @@
include protocol PContent;
include protocol PHttpChannel;
include protocol PCookieService;
include protocol PBrowser;
include protocol PWyciwygChannel;
include protocol PFTPChannel;
include protocol PWebSocket;
include protocol PTCPSocket;
+include protocol PRemoteOpenFile;
+include URIParams;
include "SerializedLoadContext.h";
using IPC::SerializedLoadContext;
namespace mozilla {
namespace net {
@@ -27,28 +29,30 @@ sync protocol PNecko
{
manager PContent;
manages PHttpChannel;
manages PCookieService;
manages PWyciwygChannel;
manages PFTPChannel;
manages PWebSocket;
manages PTCPSocket;
+ manages PRemoteOpenFile;
parent:
__delete__();
PCookieService();
PHttpChannel(nullable PBrowser browser,
SerializedLoadContext loadContext);
PWyciwygChannel();
PFTPChannel();
PWebSocket(PBrowser browser);
PTCPSocket(nsString host, uint16_t port, bool useSSL, nsString binaryType,
nullable PBrowser browser);
+ PRemoteOpenFile(URIParams fileuri, nullable PBrowser browser);
HTMLDNSPrefetch(nsString hostname, uint16_t flags);
CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason);
};
} // namespace net
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/PRemoteOpenFile.ipdl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 protocol PNecko;
+
+namespace mozilla {
+namespace net {
+
+/**
+ * Protocol to support RemoteOpenFile, an nsIFile that opens it's file handle on
+ * the parent instead of the child (since child lacks permission to do so).
+ */
+protocol PRemoteOpenFile
+{
+ manager PNecko;
+
+parent:
+ // Tell parent to open file. URI to open was passed and vetted for security in
+ // IPDL constructor: see NeckoParent::AllocPRemoteOpenFile()
+ AsyncOpenFile();
+
+ __delete__();
+
+child:
+ // success/failure code, and if NS_SUCCEEDED(rv), an open file descriptor
+ FileOpened(FileDescriptor fd, nsresult rv);
+};
+
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/RemoteOpenFileChild.cpp
@@ -0,0 +1,644 @@
+/* -*- 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/NeckoChild.h"
+#include "mozilla/net/RemoteOpenFileChild.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,
+ nsIFile,
+ nsIHashable)
+
+
+RemoteOpenFileChild::RemoteOpenFileChild(const RemoteOpenFileChild& other)
+ : 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 (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)
+{
+ if (!aRemoteOpenUri) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsAutoCString scheme;
+ nsresult rv = aRemoteOpenUri->GetScheme(scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!scheme.EqualsLiteral("remoteopenfile")) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // scheme of URI is not file:// so this is not a nsIFileURL. Convert to one.
+ nsCOMPtr<nsIURI> clonedURI;
+ rv = aRemoteOpenUri->Clone(getter_AddRefs(clonedURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ clonedURI->SetScheme(NS_LITERAL_CSTRING("file"));
+ nsAutoCString spec;
+ clonedURI->GetSpec(spec);
+
+ rv = NS_NewURI(getter_AddRefs(mURI), spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get nsIFile
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mURI);
+ if (!fileURL) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = fileURL->GetFile(getter_AddRefs(mFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult
+RemoteOpenFileChild::AsyncRemoteFileOpen(int32_t aFlags,
+ nsIRemoteOpenFileListener* aListener,
+ nsITabChild* aTabChild)
+{
+ if (!mFile) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ if (!aListener) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ 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 defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
+ // we do nothing on these platforms: we'll just open file locally when asked
+ // for NSPR handle
+ mListener->OnRemoteFileOpenComplete(NS_OK);
+ mListener = nullptr;
+ mAsyncOpenCalled = true;
+ return NS_OK;
+
+#else
+ URIParams uri;
+ SerializeURI(mURI, uri);
+
+ gNeckoChild->SendPRemoteOpenFileConstructor(this, uri, tabChild);
+
+ // 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
+}
+
+
+//-----------------------------------------------------------------------------
+// RemoteOpenFileChild::PRemoteOpenFileChild
+//-----------------------------------------------------------------------------
+
+bool
+RemoteOpenFileChild::RecvFileOpened(const FileDescriptor& aFD,
+ const nsresult& aRV)
+{
+#if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
+ NS_NOTREACHED("osX and Windows shouldn't be doing IPDL here");
+#else
+ if (NS_SUCCEEDED(aRV)) {
+ mNSPRFileDesc = PR_AllocFileDesc(aFD.PlatformHandle(), PR_GetFileMethods());
+ }
+
+ MOZ_ASSERT(mListener);
+
+ mListener->OnRemoteFileOpenComplete(aRV);
+
+ mListener = nullptr; // release ref to listener
+
+ // 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);
+ 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)
+{
+#if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
+ // 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
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // hand off ownership (i.e responsibility to PR_Close() file handle) to caller
+ *aRetval = mNSPRFileDesc;
+ mNSPRFileDesc = nullptr;
+ mNSPROpenCalled = true;
+
+ return NS_OK;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// RemoteOpenFileChild::nsIFile functions that we delegate to underlying nsIFile
+//-----------------------------------------------------------------------------
+
+nsresult
+RemoteOpenFileChild::GetLeafName(nsAString &aLeafName)
+{
+ return mFile->GetLeafName(aLeafName);
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetNativeLeafName(nsACString &aLeafName)
+{
+ return mFile->GetNativeLeafName(aLeafName);
+}
+
+nsresult
+RemoteOpenFileChild::GetTarget(nsAString &_retval)
+{
+ return mFile->GetTarget(_retval);
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetNativeTarget(nsACString &_retval)
+{
+ return mFile->GetNativeTarget(_retval);
+}
+
+nsresult
+RemoteOpenFileChild::GetPath(nsAString &_retval)
+{
+ return mFile->GetPath(_retval);
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetNativePath(nsACString &_retval)
+{
+ return mFile->GetNativePath(_retval);
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Equals(nsIFile *inFile, bool *_retval)
+{
+ return mFile->Equals(inFile, _retval);
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Contains(nsIFile *inFile, bool recur, bool *_retval)
+{
+ return mFile->Contains(inFile, recur, _retval);
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetParent(nsIFile **aParent)
+{
+ return mFile->GetParent(aParent);
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetFollowLinks(bool *aFollowLinks)
+{
+ return mFile->GetFollowLinks(aFollowLinks);
+}
+
+//-----------------------------------------------------------------------------
+// RemoteOpenFileChild::nsIFile functions that are not currently supported
+//-----------------------------------------------------------------------------
+
+nsresult
+RemoteOpenFileChild::Append(const nsAString &node)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::AppendNative(const nsACString &fragment)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Normalize()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Create(uint32_t type, uint32_t permissions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+RemoteOpenFileChild::SetLeafName(const nsAString &aLeafName)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::SetNativeLeafName(const nsACString &aLeafName)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+RemoteOpenFileChild::InitWithPath(const nsAString &filePath)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::InitWithNativePath(const nsACString &filePath)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::InitWithFile(nsIFile *aFile)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::SetFollowLinks(bool aFollowLinks)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+RemoteOpenFileChild::AppendRelativePath(const nsAString &node)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::AppendRelativeNativePath(const nsACString &fragment)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetRelativeDescriptor(nsIFile *fromFile, nsACString& _retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::SetRelativeDescriptor(nsIFile *fromFile,
+ const nsACString& relativeDesc)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+RemoteOpenFileChild::CopyTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::CopyToNative(nsIFile *newParent, const nsACString &newName)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+RemoteOpenFileChild::CopyToFollowingLinks(nsIFile *newParentDir,
+ const nsAString &newName)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::CopyToFollowingLinksNative(nsIFile *newParent,
+ const nsACString &newName)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+RemoteOpenFileChild::MoveTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::MoveToNative(nsIFile *newParent, const nsACString &newName)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Remove(bool recursive)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetPermissions(uint32_t *aPermissions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::SetPermissions(uint32_t aPermissions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetPermissionsOfLink(uint32_t *aPermissionsOfLink)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::SetPermissionsOfLink(uint32_t aPermissions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetLastModifiedTime(PRTime *aLastModTime)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::SetLastModifiedTime(PRTime aLastModTime)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetLastModifiedTimeOfLink(PRTime *aLastModTimeOfLink)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetFileSize(int64_t *aFileSize)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::SetFileSize(int64_t aFileSize)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetFileSizeOfLink(int64_t *aFileSize)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Exists(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::IsWritable(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::IsReadable(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::IsExecutable(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::IsHidden(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::IsDirectory(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::IsFile(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::IsSymlink(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::IsSpecial(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::CreateUnique(uint32_t type, uint32_t attributes)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetDirectoryEntries(nsISimpleEnumerator **entries)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::OpenANSIFileDesc(const char *mode, FILE **_retval)
+{
+ // TODO: can implement using fdopen()?
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Load(PRLibrary **_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Reveal()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Launch()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//-----------------------------------------------------------------------------
+// RemoteOpenFileChild::nsIHashable functions that we delegate to underlying nsIFile
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+RemoteOpenFileChild::Equals(nsIHashable* aOther, bool *aResult)
+{
+ nsCOMPtr<nsIHashable> hashable = do_QueryInterface(mFile);
+
+ MOZ_ASSERT(hashable);
+
+ if (hashable) {
+ return hashable->Equals(aOther, aResult);
+ }
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+RemoteOpenFileChild::GetHashCode(uint32_t *aResult)
+{
+ nsCOMPtr<nsIHashable> hashable = do_QueryInterface(mFile);
+
+ MOZ_ASSERT(hashable);
+
+ if (hashable) {
+ return hashable->GetHashCode(aResult);
+ }
+ return NS_ERROR_UNEXPECTED;
+}
+
+} // namespace net
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/RemoteOpenFileChild.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RemoteOpenFileChild_h
+#define _RemoteOpenFileChild_h
+
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/net/PRemoteOpenFileChild.h"
+#include "nsILocalFile.h"
+#include "nsIRemoteOpenFileListener.h"
+
+namespace mozilla {
+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.
+ *
+ * To open a file handle with this class, AsyncRemoteFileOpen() must be called
+ * first. After the listener's OnRemoteFileOpenComplete() is called, if the
+ * result is NS_OK, nsIFile.OpenNSPRFileDesc() may be called--once--to get the
+ * file handle.
+ *
+ * Note that calling Clone() on this class results in the filehandle ownership
+ * being passed on to the new RemoteOpenFileChild. I.e. if
+ * OnRemoteFileOpenComplete is called and then Clone(), OpenNSPRFileDesc() will
+ * work in the cloned object, but not in the original.
+ *
+ * This class should only be instantiated in a child process.
+ *
+ */
+class RemoteOpenFileChild MOZ_FINAL
+ : public PRemoteOpenFileChild
+ , public nsIFile
+ , public nsIHashable
+{
+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);
+private:
+ RemoteOpenFileChild(const RemoteOpenFileChild& other);
+
+protected:
+ virtual bool RecvFileOpened(const FileDescriptor&, const nsresult&);
+
+ // regular nsIFile object, that we forward most calls to.
+ nsCOMPtr<nsIFile> mFile;
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIRemoteOpenFileListener> mListener;
+ PRFileDesc* mNSPRFileDesc;
+ bool mAsyncOpenCalled;
+ bool mNSPROpenCalled;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // _RemoteOpenFileChild_h
+
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/RemoteOpenFileParent.cpp
@@ -0,0 +1,69 @@
+/* -*- 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/RemoteOpenFileParent.h"
+#include "mozilla/unused.h"
+#include "nsEscape.h"
+
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_COCOA)
+#include <fcntl.h>
+#endif
+
+namespace mozilla {
+namespace net {
+
+RemoteOpenFileParent::RemoteOpenFileParent(nsIFileURL *aURI)
+ : mURI(aURI)
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_COCOA)
+ , mFd(-1)
+#endif
+{}
+
+RemoteOpenFileParent::~RemoteOpenFileParent()
+{
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_COCOA)
+ if (mFd != -1) {
+ // close file handle now that other process has it open, else we'll leak
+ // file handles in parent process
+ close(mFd);
+ }
+#endif
+}
+
+bool
+RemoteOpenFileParent::RecvAsyncOpenFile()
+{
+#if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
+ NS_NOTREACHED("osX and Windows shouldn't be doing IPDL here");
+#else
+
+ // TODO: make this async!
+
+ nsAutoCString path;
+ nsresult rv = mURI->GetFilePath(path);
+ NS_UnescapeURL(path);
+ if (NS_SUCCEEDED(rv)) {
+ int fd = open(path.get(), O_RDONLY);
+ if (fd != -1) {
+ unused << SendFileOpened(FileDescriptor(fd), NS_OK);
+ // file handle needs to stay open until it's shared with child (and IPDL
+ // is async, so hasn't happened yet). Close in destructor.
+ mFd = fd;
+ return true;
+ }
+ }
+
+ // Note: sending an invalid file descriptor currently kills the child process:
+ // but that's ok for our use case (failing to open application.jar).
+ unused << SendFileOpened(FileDescriptor(mFd), NS_ERROR_NOT_AVAILABLE);
+#endif // OS_TYPE
+
+ return true;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/RemoteOpenFileParent.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_RemoteOpenFileParent_h
+#define mozilla_net_RemoteOpenFileParent_h
+
+#include "mozilla/net/PRemoteOpenFileParent.h"
+#include "mozilla/net/NeckoCommon.h"
+#include "nsIFileURL.h"
+
+namespace mozilla {
+namespace net {
+
+class RemoteOpenFileParent : public PRemoteOpenFileParent
+{
+public:
+ RemoteOpenFileParent(nsIFileURL* aURI);
+
+ ~RemoteOpenFileParent();
+
+ virtual bool RecvAsyncOpenFile();
+
+private:
+ nsCOMPtr<nsIFileURL> mURI;
+
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_COCOA)
+ int mFd;
+#endif
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_RemoteOpenFileParent_h
--- a/netwerk/ipc/ipdl.mk
+++ b/netwerk/ipc/ipdl.mk
@@ -1,8 +1,9 @@
# 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/.
IPDLSRCS = \
PNecko.ipdl \
+ PRemoteOpenFile.ipdl \
$(NULL)
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/nsIRemoteOpenFileListener.idl
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=4 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 "nsISupports.idl"
+
+/**
+ * nsIRemoteOpenFileListener: passed to RemoteOpenFileChild::AsyncRemoteFileOpen.
+ *
+ * Interface for notifying when the file has been opened and is available in
+ * child.
+ */
+[uuid(5c89208c-fe2b-4e04-9783-93bcf5c3b783)]
+interface nsIRemoteOpenFileListener : nsISupports
+{
+ /**
+ * Called when result of opening RemoteOpenFileChild:AsyncRemoteFileOpen()
+ * is available in child.
+ *
+ * @param aOpenStatus: nsresult from opening file in parent. If NS_OK,
+ * then a following call to RemoteOpenFileChild::OpenNSPRFileDesc that
+ * passes the same flags as were passed to
+ * RemoteOpenFileChild::AsyncRemoteFileOpen is guaranteed to succeed. If
+ * !NS_OK or if different flags were passed, the call will fail.
+ */
+ void onRemoteFileOpenComplete(in nsresult aOpenStatus);
+};
+
--- a/netwerk/protocol/app/AppProtocolHandler.js
+++ b/netwerk/protocol/app/AppProtocolHandler.js
@@ -11,38 +11,40 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
function AppProtocolHandler() {
- this._basePath = [];
+ this._appInfo = [];
+ this._runningInParent = Cc["@mozilla.org/xre/runtime;1"]
+ .getService(Ci.nsIXULRuntime)
+ .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
}
AppProtocolHandler.prototype = {
classID: Components.ID("{b7ad6144-d344-4687-b2d0-b6b9dce1f07f}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
scheme: "app",
defaultPort: -1,
// Don't allow loading from other protocols, and only from app:// if webapps is granted
protocolFlags: Ci.nsIProtocolHandler.URI_NOAUTH |
Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
Ci.nsIProtocolHandler.URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM,
- getBasePath: function app_phGetBasePath(aId) {
+ getAppInfo: function app_phGetAppInfo(aId) {
- if (!this._basePath[aId]) {
- this._basePath[aId] = cpmm.sendSyncMessage("Webapps:GetBasePath",
- { id: aId })[0] + "/";
+ if (!this._appInfo[aId]) {
+ let reply = cpmm.sendSyncMessage("Webapps:GetAppInfo", { id: aId });
+ this._appInfo[aId] = reply[0];
}
-
- return this._basePath[aId];
+ return this._appInfo[aId];
},
newURI: function app_phNewURI(aSpec, aOriginCharset, aBaseURI) {
let uri = Cc["@mozilla.org/network/standard-url;1"]
.createInstance(Ci.nsIStandardURL);
uri.init(Ci.nsIStandardURL.URLTYPE_STANDARD, -1, aSpec, aOriginCharset,
aBaseURI);
return uri.QueryInterface(Ci.nsIURI);
@@ -57,17 +59,25 @@ AppProtocolHandler.prototype = {
let appId = noScheme;
let fileSpec = aURI.path;
if (firstSlash) {
appId = noScheme.substring(0, firstSlash);
}
// Build a jar channel and masquerade as an app:// URI.
- let uri = "jar:file://" + this.getBasePath(appId) + appId + "/application.zip!" + fileSpec;
+ let appInfo = this.getAppInfo(appId);
+ let uri;
+ if (this._runningInParent || appInfo.isCoreApp) {
+ // In-parent and CoreApps can directly access files, so use jar:file://
+ uri = "jar:file://" + appInfo.basePath + appId + "/application.zip!" + fileSpec;
+ } else {
+ // non-CoreApps in child need to ask parent for file handle, use jar:ipcfile://
+ uri = "jar:remoteopenfile://" + appInfo.basePath + appId + "/application.zip!" + fileSpec;
+ }
let channel = Services.io.newChannel(uri, null, null);
channel.QueryInterface(Ci.nsIJARChannel).setAppURI(aURI);
channel.QueryInterface(Ci.nsIChannel).originalURI = aURI;
return channel;
},
allowPort: function app_phAllowPort(aPort, aScheme) {