Merge mozilla-central with services-central

This commit is contained in:
Philipp von Weitershausen 2011-03-07 13:34:54 -08:00
commit 90b157e523
22 changed files with 682 additions and 155 deletions

View File

@ -4026,7 +4026,7 @@ SessionStoreService.prototype = {
// Initialize the file output stream. // Initialize the file output stream.
var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]. var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream); createInstance(Ci.nsIFileOutputStream);
ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, 0); ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN);
// Obtain a converter to convert our data to a UTF-8 encoded input stream. // Obtain a converter to convert our data to a UTF-8 encoded input stream.
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].

View File

@ -85,10 +85,10 @@
</receiver> </receiver>
<activity android:name="Restarter" <activity android:name="Restarter"
android:process="@ANDROID_PACKAGE_NAME@Restarter"
android:theme="@style/GreyTheme"> android:theme="@style/GreyTheme">
<intent-filter> <intent-filter>
<action android:name="org.mozilla.gecko.restart" <action android:name="org.mozilla.gecko.restart"/>
android:process="@MOZ_APP_NAME@Restarter"/>
</intent-filter> </intent-filter>
</activity> </activity>
#if MOZ_CRASHREPORTER #if MOZ_CRASHREPORTER

View File

@ -58,6 +58,7 @@ public class CrashReporter extends Activity
static final String kMiniDumpPathKey = "upload_file_minidump"; static final String kMiniDumpPathKey = "upload_file_minidump";
static final String kPageURLKey = "URL"; static final String kPageURLKey = "URL";
static final String kNotesKey = "Notes"; static final String kNotesKey = "Notes";
Handler mHandler = null;
ProgressDialog mProgressDialog; ProgressDialog mProgressDialog;
File mPendingMinidumpFile; File mPendingMinidumpFile;
File mPendingExtrasFile; File mPendingExtrasFile;
@ -88,6 +89,15 @@ public class CrashReporter extends Activity
return true; return true;
} }
void doFinish() {
if (mHandler != null) {
mHandler.post(new Runnable(){
public void run() {
finish();
}});
}
}
@Override @Override
public void finish() public void finish()
{ {
@ -99,6 +109,8 @@ public class CrashReporter extends Activity
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// mHandler is created here so runnables can be run on the main thread
mHandler = new Handler();
setContentView(R.layout.crash_reporter); setContentView(R.layout.crash_reporter);
mProgressDialog = new ProgressDialog(CrashReporter.this); mProgressDialog = new ProgressDialog(CrashReporter.this);
mProgressDialog.setMessage(getString(R.string.sending_crash_report)); mProgressDialog.setMessage(getString(R.string.sending_crash_report));
@ -125,7 +137,7 @@ public class CrashReporter extends Activity
{ {
final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report); final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report);
if (!sendReportCheckbox.isChecked()) { if (!sendReportCheckbox.isChecked()) {
finish(); doFinish();
return; return;
} }
@ -219,7 +231,7 @@ public class CrashReporter extends Activity
String spec = extras.get("ServerURL"); String spec = extras.get("ServerURL");
if (spec == null) if (spec == null)
finish(); doFinish();
Log.i("GeckoCrashReport", "server url: " + spec); Log.i("GeckoCrashReport", "server url: " + spec);
try { try {
@ -298,7 +310,7 @@ public class CrashReporter extends Activity
Log.e("GeckoCrashReporter", "exception during send: ", e); Log.e("GeckoCrashReporter", "exception during send: ", e);
} }
finish(); doFinish();
} }
void doRestart() void doRestart()

View File

@ -455,6 +455,8 @@ abstract public class GeckoApp
} }
} }
boolean haveKilledZombies = false;
private void unpackFile(ZipFile zip, byte[] buf, ZipEntry fileEntry, private void unpackFile(ZipFile zip, byte[] buf, ZipEntry fileEntry,
String name) String name)
throws IOException, FileNotFoundException throws IOException, FileNotFoundException
@ -471,7 +473,10 @@ abstract public class GeckoApp
outFile.length() == fileEntry.getSize()) outFile.length() == fileEntry.getSize())
return; return;
killAnyZombies(); if (!haveKilledZombies) {
haveKilledZombies = true;
GeckoAppShell.killAnyZombies();
}
File dir = outFile.getParentFile(); File dir = outFile.getParentFile();
if (!outFile.exists()) if (!outFile.exists())
@ -492,27 +497,6 @@ abstract public class GeckoApp
outFile.setLastModified(fileEntry.getTime()); outFile.setLastModified(fileEntry.getTime());
} }
boolean haveKilledZombies = false;
void killAnyZombies() {
if (haveKilledZombies)
return;
haveKilledZombies = true;
File proc = new File("/proc");
File[] files = proc.listFiles();
for (int i = 0; i < files.length; i++) {
File p = files[i];
File pEnv = new File(p, "environ");
if (pEnv.canRead() && !p.getName().equals("self")) {
int pid = Integer.parseInt(p.getName());
if (pid != android.os.Process.myPid()) {
Log.i("GeckoProcs", "gonna kill pid: " + p.getName());
android.os.Process.killProcess(pid);
}
}
}
}
public void addEnvToIntent(Intent intent) { public void addEnvToIntent(Intent intent) {
Map<String,String> envMap = System.getenv(); Map<String,String> envMap = System.getenv();
Set<Map.Entry<String,String>> envSet = envMap.entrySet(); Set<Map.Entry<String,String>> envSet = envMap.entrySet();
@ -537,15 +521,14 @@ abstract public class GeckoApp
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_MULTIPLE_TASK); Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
Log.i("GeckoAppJava", intent.toString()); Log.i("GeckoAppJava", intent.toString());
GeckoAppShell.killAnyZombies();
startActivity(intent); startActivity(intent);
} catch (Exception e) { } catch (Exception e) {
Log.i("GeckoAppJava", "error doing restart", e); Log.i("GeckoAppJava", "error doing restart", e);
} }
finish(); finish();
// Give the restart process time to start before we die // Give the restart process time to start before we die
try { GeckoAppShell.waitForAnotherGeckoProc();
Thread.currentThread().sleep(1000);
} catch (InterruptedException ie) {}
} }
public void handleNotification(String action, String alertName, String alertCookie) { public void handleNotification(String action, String alertName, String alertCookie) {

View File

@ -44,6 +44,7 @@ import java.nio.channels.*;
import java.text.*; import java.text.*;
import java.util.*; import java.util.*;
import java.util.zip.*; import java.util.zip.*;
import java.util.concurrent.*;
import android.os.*; import android.os.*;
import android.app.*; import android.app.*;
@ -63,7 +64,7 @@ import android.net.Uri;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
class GeckoAppShell public class GeckoAppShell
{ {
// static members only // static members only
private GeckoAppShell() { } private GeckoAppShell() { }
@ -343,6 +344,11 @@ class GeckoAppShell
} }
} }
public static void sendEventToGeckoSync(GeckoEvent e) {
sendEventToGecko(e);
geckoEventSync();
}
// Tell the Gecko event loop that an event is available. // Tell the Gecko event loop that an event is available.
public static native void notifyGeckoOfEvent(GeckoEvent event); public static native void notifyGeckoOfEvent(GeckoEvent event);
@ -471,6 +477,28 @@ class GeckoAppShell
imm, text, start, end, newEnd); imm, text, start, end, newEnd);
} }
private static CountDownLatch sGeckoPendingAcks = null;
// Block the current thread until the Gecko event loop is caught up
synchronized public static void geckoEventSync() {
sGeckoPendingAcks = new CountDownLatch(1);
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.GECKO_EVENT_SYNC));
while (sGeckoPendingAcks.getCount() != 0) {
try {
sGeckoPendingAcks.await();
} catch (InterruptedException e) {}
}
sGeckoPendingAcks = null;
}
// Signal the Java thread that it's time to wake up
public static void acknowledgeEventSync() {
CountDownLatch tmp = sGeckoPendingAcks;
if (tmp != null)
tmp.countDown();
}
public static void enableAccelerometer(boolean enable) { public static void enableAccelerometer(boolean enable) {
SensorManager sm = (SensorManager) SensorManager sm = (SensorManager)
GeckoApp.surfaceView.getContext().getSystemService(Context.SENSOR_SERVICE); GeckoApp.surfaceView.getContext().getSystemService(Context.SENSOR_SERVICE);
@ -868,4 +896,47 @@ class GeckoAppShell
config.locale = locale; config.locale = locale;
res.updateConfiguration(config, res.getDisplayMetrics()); res.updateConfiguration(config, res.getDisplayMetrics());
} }
public static void killAnyZombies() {
File proc = new File("/proc");
File[] files = proc.listFiles();
for (int i = 0; i < files.length; i++) {
File p = files[i];
File pEnv = new File(p, "environ");
if (pEnv.canRead() && !p.getName().equals("self")) {
int pid = Integer.parseInt(p.getName());
if (pid != android.os.Process.myPid()) {
Log.i("GeckoProcs", "gonna kill pid: " + p.getName());
android.os.Process.killProcess(pid);
}
}
}
}
public static boolean checkForGeckoProcs() {
File proc = new File("/proc");
File[] files = proc.listFiles();
for (int i = 0; i < files.length; i++) {
File p = files[i];
File pEnv = new File(p, "environ");
if (pEnv.canRead() && !p.getName().equals("self")) {
int pid = Integer.parseInt(p.getName());
if (pid != android.os.Process.myPid()) {
Log.i("GeckoProcs", "found pid: " + p.getName());
return true;
}
}
}
Log.i("GeckoProcs", "didn't find any other procs");
return false;
}
public static void waitForAnotherGeckoProc(){
int countdown = 40;
while (!checkForGeckoProcs() && --countdown > 0) {
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException ie) {}
}
}
} }

View File

@ -70,6 +70,7 @@ public class GeckoEvent {
public static final int SURFACE_CREATED = 12; public static final int SURFACE_CREATED = 12;
public static final int SURFACE_DESTROYED = 13; public static final int SURFACE_DESTROYED = 13;
public static final int GECKO_EVENT_SYNC = 14;
public static final int IME_COMPOSITION_END = 0; public static final int IME_COMPOSITION_END = 0;
public static final int IME_COMPOSITION_BEGIN = 1; public static final int IME_COMPOSITION_BEGIN = 1;

View File

@ -561,9 +561,10 @@ public class GeckoInputConnection
GeckoAppShell.sendEventToGecko( GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_SET_SELECTION, start + count, 0)); new GeckoEvent(GeckoEvent.IME_SET_SELECTION, start + count, 0));
} }
// Block this thread until all pending events are processed
GeckoAppShell.geckoEventSync();
} }
public void afterTextChanged(Editable s) public void afterTextChanged(Editable s)

View File

@ -43,30 +43,24 @@ import android.content.*;
import android.util.*; import android.util.*;
import android.os.*; import android.os.*;
import java.io.*; import java.io.*;
import org.mozilla.gecko.GeckoAppShell;
public class Restarter extends Activity { public class Restarter extends Activity {
void killAnyZombies() {
File proc = new File("/proc");
File[] files = proc.listFiles();
for (int i = 0; i < files.length; i++) {
File p = files[i];
File pEnv = new File(p, "environ");
if (pEnv.canRead() && !p.getName().equals("self")) {
int pid = Integer.parseInt(p.getName());
if (pid != android.os.Process.myPid()) {
Log.i("GeckoProcs", "gonna kill pid: " + p.getName());
android.os.Process.killProcess(pid);
}
}
}
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
Log.i("Restarter", "trying to restart @MOZ_APP_NAME@"); Log.i("Restarter", "trying to restart @MOZ_APP_NAME@");
try { try {
killAnyZombies(); int countdown = 40;
while (GeckoAppShell.checkForGeckoProcs() && --countdown > 0) {
// Wait for the old process to die before we continue
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException ie) {}
}
if (countdown <= 0) // if the countdown expired, something is hung
GeckoAppShell.killAnyZombies();
} catch (Exception e) { } catch (Exception e) {
Log.i("Restarter", e.toString()); Log.i("Restarter", e.toString());
} }
@ -84,6 +78,8 @@ public class Restarter extends Activity {
} catch (Exception e) { } catch (Exception e) {
Log.i("Restarter", e.toString()); Log.i("Restarter", e.toString());
} }
// Give the new process time to start before we die
GeckoAppShell.waitForAnotherGeckoProc();
System.exit(0); System.exit(0);
} }
}; };

View File

@ -2237,6 +2237,7 @@ ComputeIsJITBroken()
do { do {
if (0 == line.find("Hardware")) { if (0 == line.find("Hardware")) {
const char* blacklist[] = { const char* blacklist[] = {
"SCH-I400", // Samsung Continuum
"SGH-T959", // Samsung i9000, Vibrant device "SGH-T959", // Samsung i9000, Vibrant device
"SGH-I897", // Samsung i9000, Captivate device "SGH-I897", // Samsung i9000, Captivate device
"SCH-I500", // Samsung i9000, Fascinate device "SCH-I500", // Samsung i9000, Fascinate device

View File

@ -81,6 +81,28 @@ interface nsIFileInputStream : nsIInputStream
* (The file will only be reopened if it is closed for some reason.) * (The file will only be reopened if it is closed for some reason.)
*/ */
const long REOPEN_ON_REWIND = 1<<3; const long REOPEN_ON_REWIND = 1<<3;
/**
* If this is set, the file will be opened (i.e., a call to
* PR_Open done) only when we do an actual operation on the stream,
* or more specifically, when one of the following is called:
* - Seek
* - Tell
* - Available
* - Read
* - ReadLine
*
* DEFER_OPEN is useful if we use the stream on a background
* thread, so that the opening and possible |stat|ing of the file
* happens there as well.
*
* @note Using this flag results in the file not being opened
* during the call to Init. This means that any errors that might
* happen when this flag is not set would happen during the
* first read. Also, the file is not locked when Init is called,
* so it might be deleted before we try to read from it.
*/
const long DEFER_OPEN = 1<<4;
}; };
/** /**
@ -102,6 +124,22 @@ interface nsIFileOutputStream : nsIOutputStream
*/ */
void init(in nsIFile file, in long ioFlags, in long perm, void init(in nsIFile file, in long ioFlags, in long perm,
in long behaviorFlags); in long behaviorFlags);
/**
* See the same constant in nsIFileInputStream. The deferred open will
* be performed when one of the following is called:
* - Seek
* - Tell
* - Write
* - Flush
*
* @note Using this flag results in the file not being opened
* during the call to Init. This means that any errors that might
* happen when this flag is not set would happen during the
* first write, and if the file is to be created, then it will not
* appear on the disk until the first write.
*/
const long DEFER_OPEN = 1<<0;
}; };
/** /**

View File

@ -72,39 +72,25 @@
nsFileStream::nsFileStream() nsFileStream::nsFileStream()
: mFD(nsnull) : mFD(nsnull)
, mCloseFD(PR_TRUE) , mBehaviorFlags(0)
, mDeferredOpen(false)
{ {
} }
nsFileStream::~nsFileStream() nsFileStream::~nsFileStream()
{ {
if (mCloseFD)
Close(); Close();
} }
NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileStream, nsISeekableStream) NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileStream, nsISeekableStream)
nsresult
nsFileStream::InitWithFileDescriptor(PRFileDesc* fd, nsISupports* parent)
{
NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
//
// this file stream is dependent on its parent to keep the
// file descriptor valid. an owning reference to the parent
// prevents the file descriptor from going away prematurely.
//
mFD = fd;
mCloseFD = PR_FALSE;
mParent = parent;
return NS_OK;
}
nsresult nsresult
nsFileStream::Close() nsFileStream::Close()
{ {
CleanUpOpen();
nsresult rv = NS_OK; nsresult rv = NS_OK;
if (mFD) { if (mFD) {
if (mCloseFD)
if (PR_Close(mFD) == PR_FAILURE) if (PR_Close(mFD) == PR_FAILURE)
rv = NS_BASE_STREAM_OSERROR; rv = NS_BASE_STREAM_OSERROR;
mFD = nsnull; mFD = nsnull;
@ -115,6 +101,9 @@ nsFileStream::Close()
NS_IMETHODIMP NS_IMETHODIMP
nsFileStream::Seek(PRInt32 whence, PRInt64 offset) nsFileStream::Seek(PRInt32 whence, PRInt64 offset)
{ {
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (mFD == nsnull) if (mFD == nsnull)
return NS_BASE_STREAM_CLOSED; return NS_BASE_STREAM_CLOSED;
@ -128,6 +117,9 @@ nsFileStream::Seek(PRInt32 whence, PRInt64 offset)
NS_IMETHODIMP NS_IMETHODIMP
nsFileStream::Tell(PRInt64 *result) nsFileStream::Tell(PRInt64 *result)
{ {
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (mFD == nsnull) if (mFD == nsnull)
return NS_BASE_STREAM_CLOSED; return NS_BASE_STREAM_CLOSED;
@ -174,6 +166,62 @@ nsFileStream::SetEOF()
return NS_OK; return NS_OK;
} }
nsresult
nsFileStream::MaybeOpen(nsILocalFile* aFile, PRInt32 aIoFlags, PRInt32 aPerm,
bool aDeferred)
{
mOpenParams.ioFlags = aIoFlags;
mOpenParams.perm = aPerm;
if (aDeferred) {
// Clone the file, as it may change between now and the deferred open
nsCOMPtr<nsIFile> file;
nsresult rv = aFile->Clone(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
mOpenParams.localFile = do_QueryInterface(file);
NS_ENSURE_TRUE(mOpenParams.localFile, NS_ERROR_UNEXPECTED);
mDeferredOpen = true;
return NS_OK;
}
mOpenParams.localFile = aFile;
return DoOpen();
}
void
nsFileStream::CleanUpOpen()
{
mOpenParams.localFile = nsnull;
mDeferredOpen = false;
}
nsresult
nsFileStream::DoOpen()
{
NS_PRECONDITION(mOpenParams.localFile, "Must have a file to open");
PRFileDesc* fd;
nsresult rv = mOpenParams.localFile->OpenNSPRFileDesc(mOpenParams.ioFlags, mOpenParams.perm, &fd);
CleanUpOpen();
NS_ENSURE_SUCCESS(rv, rv);
mFD = fd;
return NS_OK;
}
nsresult
nsFileStream::DoPendingOpen()
{
if (!mDeferredOpen) {
return NS_OK;
}
return DoOpen();
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// nsFileInputStream // nsFileInputStream
@ -232,12 +280,10 @@ nsFileInputStream::Open(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm)
if (aPerm == -1) if (aPerm == -1)
aPerm = 0; aPerm = 0;
PRFileDesc* fd; rv = MaybeOpen(localFile, aIOFlags, aPerm,
rv = localFile->OpenNSPRFileDesc(aIOFlags, aPerm, &fd); mBehaviorFlags & nsIFileInputStream::DEFER_OPEN);
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
mFD = fd;
if (mBehaviorFlags & DELETE_ON_CLOSE) { if (mBehaviorFlags & DELETE_ON_CLOSE) {
// POSIX compatible filesystems allow a file to be unlinked while a // POSIX compatible filesystems allow a file to be unlinked while a
// file descriptor is still referencing the file. since we've already // file descriptor is still referencing the file. since we've already
@ -259,7 +305,7 @@ nsFileInputStream::Init(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm,
PRInt32 aBehaviorFlags) PRInt32 aBehaviorFlags)
{ {
NS_ENSURE_TRUE(!mFD, NS_ERROR_ALREADY_INITIALIZED); NS_ENSURE_TRUE(!mFD, NS_ERROR_ALREADY_INITIALIZED);
NS_ENSURE_TRUE(!mParent, NS_ERROR_ALREADY_INITIALIZED); NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED);
mBehaviorFlags = aBehaviorFlags; mBehaviorFlags = aBehaviorFlags;
@ -291,6 +337,9 @@ nsFileInputStream::Close()
NS_IMETHODIMP NS_IMETHODIMP
nsFileInputStream::Available(PRUint32* aResult) nsFileInputStream::Available(PRUint32* aResult)
{ {
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFD) { if (!mFD) {
return NS_BASE_STREAM_CLOSED; return NS_BASE_STREAM_CLOSED;
} }
@ -310,6 +359,9 @@ nsFileInputStream::Available(PRUint32* aResult)
NS_IMETHODIMP NS_IMETHODIMP
nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult) nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
{ {
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFD) { if (!mFD) {
*aResult = 0; *aResult = 0;
return NS_OK; return NS_OK;
@ -333,6 +385,9 @@ nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
NS_IMETHODIMP NS_IMETHODIMP
nsFileInputStream::ReadLine(nsACString& aLine, PRBool* aResult) nsFileInputStream::ReadLine(nsACString& aLine, PRBool* aResult)
{ {
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mLineBuffer) { if (!mLineBuffer) {
nsresult rv = NS_InitLineBuffer(&mLineBuffer); nsresult rv = NS_InitLineBuffer(&mLineBuffer);
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
@ -365,6 +420,9 @@ nsFileInputStream::IsNonBlocking(PRBool *aNonBlocking)
NS_IMETHODIMP NS_IMETHODIMP
nsFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset) nsFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
{ {
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
PR_FREEIF(mLineBuffer); // this invalidates the line buffer PR_FREEIF(mLineBuffer); // this invalidates the line buffer
if (!mFD) { if (!mFD) {
if (mBehaviorFlags & REOPEN_ON_REWIND) { if (mBehaviorFlags & REOPEN_ON_REWIND) {
@ -564,6 +622,9 @@ nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
PRInt32 behaviorFlags) PRInt32 behaviorFlags)
{ {
NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED); NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED);
mBehaviorFlags = behaviorFlags;
nsresult rv; nsresult rv;
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv); nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
@ -573,12 +634,8 @@ nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
if (perm <= 0) if (perm <= 0)
perm = 0664; perm = 0664;
PRFileDesc* fd; return MaybeOpen(localFile, ioFlags, perm,
rv = localFile->OpenNSPRFileDesc(ioFlags, perm, &fd); mBehaviorFlags & nsIFileOutputStream::DEFER_OPEN);
if (NS_FAILED(rv)) return rv;
mFD = fd;
return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -590,6 +647,9 @@ nsFileOutputStream::Close()
NS_IMETHODIMP NS_IMETHODIMP
nsFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result) nsFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
{ {
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (mFD == nsnull) if (mFD == nsnull)
return NS_BASE_STREAM_CLOSED; return NS_BASE_STREAM_CLOSED;
@ -604,6 +664,9 @@ nsFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
NS_IMETHODIMP NS_IMETHODIMP
nsFileOutputStream::Flush(void) nsFileOutputStream::Flush(void)
{ {
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (mFD == nsnull) if (mFD == nsnull)
return NS_BASE_STREAM_CLOSED; return NS_BASE_STREAM_CLOSED;
@ -653,7 +716,16 @@ NS_IMETHODIMP
nsSafeFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, nsSafeFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
PRInt32 behaviorFlags) PRInt32 behaviorFlags)
{ {
NS_ENSURE_ARG(file); return nsFileOutputStream::Init(file, ioFlags, perm, behaviorFlags);
}
nsresult
nsSafeFileOutputStream::DoOpen()
{
// Make sure mOpenParams.localFile will be empty if we bail somewhere in
// this function
nsCOMPtr<nsILocalFile> file;
file.swap(mOpenParams.localFile);
nsresult rv = file->Exists(&mTargetFileExists); nsresult rv = file->Exists(&mTargetFileExists);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
@ -680,16 +752,21 @@ nsSafeFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
PRUint32 origPerm; PRUint32 origPerm;
if (NS_FAILED(file->GetPermissions(&origPerm))) { if (NS_FAILED(file->GetPermissions(&origPerm))) {
NS_ERROR("Can't get permissions of target file"); NS_ERROR("Can't get permissions of target file");
origPerm = perm; origPerm = mOpenParams.perm;
} }
// XXX What if |perm| is more restrictive then |origPerm|? // XXX What if |perm| is more restrictive then |origPerm|?
// This leaves the user supplied permissions as they were. // This leaves the user supplied permissions as they were.
rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm); rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm);
} }
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
// nsFileOutputStream::DoOpen will work on the temporary file, so we
// prepare it and place it in mOpenParams.localFile.
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(tempResult, &rv);
NS_ENSURE_SUCCESS(rv, rv);
mOpenParams.localFile = localFile;
mTempFile = tempResult; mTempFile = tempResult;
mTargetFile = file; mTargetFile = file;
rv = nsFileOutputStream::Init(mTempFile, ioFlags, perm, behaviorFlags); rv = nsFileOutputStream::DoOpen();
} }
return rv; return rv;
} }

View File

@ -64,13 +64,57 @@ public:
virtual ~nsFileStream(); virtual ~nsFileStream();
nsresult Close(); nsresult Close();
nsresult InitWithFileDescriptor(PRFileDesc* fd, nsISupports* parent);
protected: protected:
PRFileDesc* mFD; PRFileDesc* mFD;
nsCOMPtr<nsISupports> mParent; // strong reference to parent nsFileIO,
// which ensures mFD remains valid. /**
PRBool mCloseFD; * Flags describing our behavior. See the IDL file for possible values.
*/
PRInt32 mBehaviorFlags;
/**
* Whether we have a pending open (see DEFER_OPEN in the IDL file).
*/
bool mDeferredOpen;
struct OpenParams {
nsCOMPtr<nsILocalFile> localFile;
PRInt32 ioFlags;
PRInt32 perm;
};
/**
* Data we need to do an open.
*/
OpenParams mOpenParams;
/**
* Prepares the data we need to open the file, and either does the open now
* by calling DoOpen(), or leaves it to be opened later by a call to
* DoPendingOpen().
*/
nsresult MaybeOpen(nsILocalFile* aFile, PRInt32 aIoFlags, PRInt32 aPerm,
bool aDeferred);
/**
* Cleans up data prepared in MaybeOpen.
*/
void CleanUpOpen();
/**
* Open the file. This is called either from MaybeOpen (during Init)
* or from DoPendingOpen (if DEFER_OPEN is used when initializing this
* stream). The default behavior of DoOpen is to open the file and save the
* file descriptor.
*/
virtual nsresult DoOpen();
/**
* If there is a pending open, do it now. It's important for this to be
* inline since we do it in almost every stream API call.
*/
inline nsresult DoPendingOpen();
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -93,7 +137,6 @@ public:
nsFileInputStream() : nsFileStream() nsFileInputStream() : nsFileStream()
{ {
mLineBuffer = nsnull; mLineBuffer = nsnull;
mBehaviorFlags = 0;
} }
virtual ~nsFileInputStream() virtual ~nsFileInputStream()
{ {
@ -118,10 +161,6 @@ protected:
* The permissions passed to Init() for the file open. * The permissions passed to Init() for the file open.
*/ */
PRInt32 mPerm; PRInt32 mPerm;
/**
* Flags describing our behavior. See the IDL file for possible values.
*/
PRInt32 mBehaviorFlags;
protected: protected:
/** /**
@ -194,6 +233,8 @@ public:
virtual ~nsSafeFileOutputStream() { nsSafeFileOutputStream::Close(); } virtual ~nsSafeFileOutputStream() { nsSafeFileOutputStream::Close(); }
virtual nsresult DoOpen();
NS_IMETHODIMP Close(); NS_IMETHODIMP Close();
NS_IMETHODIMP Write(const char *buf, PRUint32 count, PRUint32 *result); NS_IMETHODIMP Write(const char *buf, PRUint32 count, PRUint32 *result);
NS_IMETHODIMP Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRInt32 behaviorFlags); NS_IMETHODIMP Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRInt32 behaviorFlags);

View File

@ -49,6 +49,9 @@ Components.utils.import("resource://gre/modules/NetUtil.jsm");
// files. // files.
do_get_profile(); do_get_profile();
const OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/file-output-stream;1";
const SAFE_OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/safe-file-output-stream;1";
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
//// Helper Methods //// Helper Methods
@ -75,10 +78,15 @@ function getFileContents(aFile)
return string.value; return string.value;
} }
//////////////////////////////////////////////////////////////////////////////// /**
//// Tests * Tests asynchronously writing a file using NetUtil.asyncCopy.
*
function test_async_write_file() * @param aContractId
* The contract ID to use for the output stream
* @param aDeferOpen
* Whether to use DEFER_OPEN in the output stream.
*/
function async_write_file(aContractId, aDeferOpen)
{ {
do_test_pending(); do_test_pending();
@ -90,9 +98,8 @@ function test_async_write_file()
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
// Then, we need an output stream to our output file. // Then, we need an output stream to our output file.
let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. let ostream = Cc[aContractId].createInstance(Ci.nsIFileOutputStream);
createInstance(Ci.nsIFileOutputStream); ostream.init(file, -1, -1, aDeferOpen ? Ci.nsIFileOutputStream.DEFER_OPEN : 0);
ostream.init(file, -1, -1, 0);
// Finally, we need an input stream to take data from. // Finally, we need an input stream to take data from.
const TEST_DATA = "this is a test string"; const TEST_DATA = "this is a test string";
@ -113,39 +120,23 @@ function test_async_write_file()
}); });
} }
function test_async_write_file_nsISafeOutputStream() ////////////////////////////////////////////////////////////////////////////////
{ //// Tests
do_test_pending();
// First, we need an output file to write to. function test_async_write_file() {
let file = Cc["@mozilla.org/file/directory_service;1"]. async_write_file(OUTPUT_STREAM_CONTRACT_ID);
getService(Ci.nsIProperties). }
get("ProfD", Ci.nsIFile);
file.append("NetUtil-async-test-file.tmp");
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
// Then, we need an output stream to our output file. function test_async_write_file_deferred() {
let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]. async_write_file(OUTPUT_STREAM_CONTRACT_ID, true);
createInstance(Ci.nsIFileOutputStream); }
ostream.init(file, -1, -1, 0);
// Finally, we need an input stream to take data from. function test_async_write_file_safe() {
const TEST_DATA = "this is a test string"; async_write_file(SAFE_OUTPUT_STREAM_CONTRACT_ID);
let istream = Cc["@mozilla.org/io/string-input-stream;1"]. }
createInstance(Ci.nsIStringInputStream);
istream.setData(TEST_DATA, TEST_DATA.length);
NetUtil.asyncCopy(istream, ostream, function(aResult) { function test_async_write_file_safe_deferred() {
// Make sure the copy was successful! async_write_file(SAFE_OUTPUT_STREAM_CONTRACT_ID, true);
do_check_true(Components.isSuccessCode(aResult));
// Check the file contents.
do_check_eq(TEST_DATA, getFileContents(file));
// Finish the test.
do_test_finished();
run_next_test();
});
} }
function test_newURI_no_spec_throws() function test_newURI_no_spec_throws()
@ -529,7 +520,9 @@ function test_readInputStreamToString_too_many_bytes()
let tests = [ let tests = [
test_async_write_file, test_async_write_file,
test_async_write_file_nsISafeOutputStream, test_async_write_file_deferred,
test_async_write_file_safe,
test_async_write_file_safe_deferred,
test_newURI_no_spec_throws, test_newURI_no_spec_throws,
test_newURI, test_newURI,
test_newURI_takes_nsIFile, test_newURI_takes_nsIFile,

View File

@ -0,0 +1,231 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let Cc = Components.classes;
let Ci = Components.interfaces;
// We need the profile directory so the test harness will clean up our test
// files.
do_get_profile();
const OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/file-output-stream;1";
const SAFE_OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/safe-file-output-stream;1";
////////////////////////////////////////////////////////////////////////////////
//// Helper Methods
/**
* Generates a leafName for a file that does not exist, but does *not*
* create the file. Similar to createUnique except for the fact that createUnique
* does create the file.
*
* @param aFile
* The file to modify in order for it to have a unique leafname.
*/
function ensure_unique(aFile)
{
ensure_unique.fileIndex = ensure_unique.fileIndex || 0;
var leafName = aFile.leafName;
while (aFile.clone().exists()) {
aFile.leafName = leafName + "_" + (ensure_unique.fileIndex++);
}
}
/**
* Tests for files being accessed at the right time. Streams that use
* DEFER_OPEN should only open or create the file when an operation is
* done, and not during Init().
*
* Note that for writing, we check for actual writing in test_NetUtil (async)
* and in sync_operations in this file (sync), whereas in this function we
* just check that the file is *not* created during init.
*
* @param aContractId
* The contract ID to use for the output stream
* @param aDeferOpen
* Whether to check with DEFER_OPEN or not
* @param aTrickDeferredOpen
* Whether we try to 'trick' deferred opens by changing the file object before
* the actual open. The stream should have a clone, so changes to the file
* object after Init and before Open should not affect it.
*/
function check_access(aContractId, aDeferOpen, aTrickDeferredOpen)
{
const LEAF_NAME = "filestreams-test-file.tmp";
const TRICKY_LEAF_NAME = "BetYouDidNotExpectThat.tmp";
let file = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties).
get("ProfD", Ci.nsIFile);
file.append(LEAF_NAME);
// Writing
ensure_unique(file);
let ostream = Cc[aContractId].createInstance(Ci.nsIFileOutputStream);
ostream.init(file, -1, -1, aDeferOpen ? Ci.nsIFileOutputStream.DEFER_OPEN : 0);
do_check_eq(aDeferOpen, !file.clone().exists()); // If defer, should not exist and vice versa
if (aDeferOpen) {
// File should appear when we do write to it.
if (aTrickDeferredOpen) {
// See |@param aDeferOpen| in the JavaDoc comment for this function
file.leafName = TRICKY_LEAF_NAME;
}
ostream.write("data", 4);
if (aTrickDeferredOpen) {
file.leafName = LEAF_NAME;
}
// We did a write, so the file should now exist
do_check_true(file.clone().exists());
}
ostream.close();
// Reading
ensure_unique(file);
let istream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
var initOk, getOk;
try {
istream.init(file, -1, 0, aDeferOpen ? Ci.nsIFileInputStream.DEFER_OPEN : 0);
initOk = true;
}
catch(e) {
initOk = false;
}
try {
let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
fstream.init(aFile, -1, 0, 0);
getOk = true;
}
catch(e) {
getOk = false;
}
// If the open is deferred, then Init should succeed even though the file we
// intend to read does not exist, and then trying to read from it should
// fail. The other case is where the open is not deferred, and there we should
// get an error when we Init (and also when we try to read).
do_check_true( (aDeferOpen && initOk && !getOk) ||
(!aDeferOpen && !initOk && !getOk) );
istream.close();
}
/**
* We test async operations in test_NetUtil.js, and here test for simple sync
* operations on input streams.
*
* @param aDeferOpen
* Whether to use DEFER_OPEN in the streams.
*/
function sync_operations(aDeferOpen)
{
const TEST_DATA = "this is a test string";
const LEAF_NAME = "filestreams-test-file.tmp";
let file = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties).
get("ProfD", Ci.nsIFile);
file.append(LEAF_NAME);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
let ostream = Cc[OUTPUT_STREAM_CONTRACT_ID].
createInstance(Ci.nsIFileOutputStream);
ostream.init(file, -1, -1, aDeferOpen ? Ci.nsIFileOutputStream.DEFER_OPEN : 0);
ostream.write(TEST_DATA, TEST_DATA.length);
ostream.close();
let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
fstream.init(file, -1, 0, aDeferOpen ? Ci.nsIFileInputStream.DEFER_OPEN : 0);
let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Ci.nsIConverterInputStream);
cstream.init(fstream, "UTF-8", 0, 0);
let string = {};
cstream.readString(-1, string);
cstream.close();
fstream.close();
do_check_eq(string.value, TEST_DATA);
}
////////////////////////////////////////////////////////////////////////////////
//// Tests
function test_access()
{
check_access(OUTPUT_STREAM_CONTRACT_ID, false, false);
}
function test_access_trick()
{
check_access(OUTPUT_STREAM_CONTRACT_ID, false, true);
}
function test_access_defer()
{
check_access(OUTPUT_STREAM_CONTRACT_ID, true, false);
}
function test_access_defer_trick()
{
check_access(OUTPUT_STREAM_CONTRACT_ID, true, true);
}
function test_access_safe()
{
check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, false, false);
}
function test_access_safe_trick()
{
check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, false, true);
}
function test_access_safe_defer()
{
check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, true, false);
}
function test_access_safe_defer_trick()
{
check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, true, true);
}
function test_sync_operations()
{
sync_operations();
}
function test_sync_operations_deferred()
{
sync_operations(true);
}
////////////////////////////////////////////////////////////////////////////////
//// Test Runner
let tests = [
test_access,
test_access_trick,
test_access_defer,
test_access_defer_trick,
test_access_safe,
test_access_safe_trick,
test_access_safe_defer,
test_access_safe_defer_trick,
test_sync_operations,
test_sync_operations_deferred,
];
function run_test()
{
tests.forEach(function(test) {
test();
});
}

View File

@ -455,8 +455,8 @@ static void * mozload(const char * path, void *zip,
#ifdef DEBUG_EXTRACT_LIBS #ifdef DEBUG_EXTRACT_LIBS
if (extractLibs) { if (extractLibs) {
char fullpath[256]; char fullpath[PATH_MAX];
snprintf(fullpath, 256, "%s/%s", getenv("CACHE_PATH"), path + 4); snprintf(fullpath, PATH_MAX, "%s/%s", getenv("CACHE_PATH"), path + 4);
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "resolved %s to %s", path, fullpath); __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "resolved %s to %s", path, fullpath);
extractFile(fullpath, entry, data); extractFile(fullpath, entry, data);
handle = __wrap_dlopen(fullpath, RTLD_LAZY); handle = __wrap_dlopen(fullpath, RTLD_LAZY);
@ -474,7 +474,7 @@ static void * mozload(const char * path, void *zip,
} }
#endif #endif
size_t offset = letoh32(entry->offset) + sizeof(*file) + letoh16(file->filename_size) + letoh16(file->extra_field_size); size_t offset = letoh32(entry->offset) + sizeof(*file) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
bool skipLibCache = false;
int fd = zip_fd; int fd = zip_fd;
void * buf = NULL; void * buf = NULL;
uint32_t lib_size = letoh32(entry->uncompressed_size); uint32_t lib_size = letoh32(entry->uncompressed_size);
@ -483,8 +483,8 @@ static void * mozload(const char * path, void *zip,
cache_fd = lookupLibCacheFd(path + 4); cache_fd = lookupLibCacheFd(path + 4);
fd = cache_fd; fd = cache_fd;
if (fd < 0) { if (fd < 0) {
char fullpath[256]; char fullpath[PATH_MAX];
snprintf(fullpath, 256, "%s/%s", getenv("CACHE_PATH"), path + 4); snprintf(fullpath, PATH_MAX, "%s/%s", getenv("CACHE_PATH"), path + 4);
fd = open(fullpath, O_RDWR); fd = open(fullpath, O_RDWR);
struct stat status; struct stat status;
if (stat(fullpath, &status) || if (stat(fullpath, &status) ||
@ -504,9 +504,26 @@ static void * mozload(const char * path, void *zip,
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s from cache", path + 4); __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s from cache", path + 4);
#endif #endif
if (fd < 0) { if (fd < 0) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't open " ASHMEM_NAME_DEF ", Error %d, %s", errno, strerror(errno)); __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't open " ASHMEM_NAME_DEF ", Error %d, %s, using a file", errno, strerror(errno));
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s/%s", getenv("CACHE_PATH"), path + 4);
fd = open(fullpath, O_RDWR | O_CREAT);
if (fd < 0) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't create a file either, giving up");
return NULL; return NULL;
} }
// we'd like to use fallocate here, but it doesn't exist currently?
if (lseek(fd, lib_size - 1, SEEK_SET) == (off_t) - 1) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "seeking file failed");
return NULL;
}
if (write(fd, "", 1) != 1) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "writting one byte to the file failed");
return NULL;
}
skipLibCache = true;
addLibCacheFd(path + 4, fd);
}
buf = mmap(NULL, lib_size, buf = mmap(NULL, lib_size,
PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0); MAP_SHARED, fd, 0);
@ -520,8 +537,10 @@ static void * mozload(const char * path, void *zip,
if (cache_fd < 0) { if (cache_fd < 0) {
extractLib(entry, data, buf); extractLib(entry, data, buf);
if (!skipLibCache)
addLibCacheFd(path + 4, fd, lib_size, buf); addLibCacheFd(path + 4, fd, lib_size, buf);
} }
// preload libxul, to avoid slowly demand-paging it // preload libxul, to avoid slowly demand-paging it
if (!strcmp(path, "lib/libxul.so")) if (!strcmp(path, "lib/libxul.so"))
madvise(buf, entry->uncompressed_size, MADV_WILLNEED); madvise(buf, entry->uncompressed_size, MADV_WILLNEED);
@ -719,9 +738,9 @@ Java_org_mozilla_gecko_GeckoAppShell_loadLibs(JNIEnv *jenv, jclass jGeckoAppShel
if (!info->buffer) if (!info->buffer)
continue; continue;
char fullpath[256]; char fullpath[PATH_MAX];
snprintf(fullpath, 256, "%s/%s", getenv("CACHE_PATH"), info->name); snprintf(fullpath, PATH_MAX, "%s/%s", getenv("CACHE_PATH"), info->name);
char tmp_path[256]; char tmp_path[PATH_MAX];
sprintf(tmp_path, "%s.tmp", fullpath); sprintf(tmp_path, "%s.tmp", fullpath);
int file_fd = open(tmp_path, O_CREAT | O_WRONLY); int file_fd = open(tmp_path, O_CREAT | O_WRONLY);
// using sendfile would be preferable, but it doesn't seem to work // using sendfile would be preferable, but it doesn't seem to work

View File

@ -2560,7 +2560,7 @@ SearchService.prototype = {
try { try {
LOG("_buildCache: Writing to cache file."); LOG("_buildCache: Writing to cache file.");
ostream.init(cacheFile, (MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE), PERMS_FILE, 0); ostream.init(cacheFile, (MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE), PERMS_FILE, ostream.DEFER_OPEN);
converter.charset = "UTF-8"; converter.charset = "UTF-8";
let data = converter.convertToInputStream(JSON.stringify(cache)); let data = converter.convertToInputStream(JSON.stringify(cache));

View File

@ -929,6 +929,40 @@ function validateFileName(aFileName)
} }
else if (navigator.appVersion.indexOf("Macintosh") != -1) else if (navigator.appVersion.indexOf("Macintosh") != -1)
re = /[\:\/]+/g; re = /[\:\/]+/g;
else if (navigator.appVersion.indexOf("Android") != -1 ||
navigator.appVersion.indexOf("Maemo") != -1) {
// On mobile devices, the filesystem may be very limited in what
// it considers valid characters. To avoid errors, we sanitize
// conservatively.
const dangerousChars = "*?<>|\":/\\[];,+=";
var processed = "";
for (var i = 0; i < aFileName.length; i++)
processed += aFileName.charCodeAt(i) >= 32 &&
!(dangerousChars.indexOf(aFileName[i]) >= 0) ? aFileName[i]
: "_";
// Last character should not be a space
processed = processed.trim();
// If a large part of the filename has been sanitized, then we
// will use a default filename instead
if (processed.replace(/_/g, "").length <= processed.length/2) {
// We purposefully do not use a localized default filename,
// which we could have done using
// ContentAreaUtils.stringBundle.GetStringFromName("DefaultSaveFileName")
// since it may contain invalid characters.
var original = processed;
processed = "download";
// Preserve a suffix, if there is one
if (original.indexOf(".") >= 0) {
var suffix = original.split(".").slice(-1)[0];
if (suffix && suffix.indexOf("_") < 0)
processed += "." + suffix;
}
}
return processed;
}
return aFileName.replace(re, "_"); return aFileName.replace(re, "_");
} }

View File

@ -107,6 +107,8 @@ AndroidBridge::Init(JNIEnv *jEnv,
jNotifyIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIME", "(II)V"); jNotifyIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIME", "(II)V");
jNotifyIMEEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEEnabled", "(ILjava/lang/String;Ljava/lang/String;)V"); jNotifyIMEEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEEnabled", "(ILjava/lang/String;Ljava/lang/String;)V");
jNotifyIMEChange = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEChange", "(Ljava/lang/String;III)V"); jNotifyIMEChange = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEChange", "(Ljava/lang/String;III)V");
jAcknowledgeEventSync = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "acknowledgeEventSync", "()V");
jEnableAccelerometer = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableAccelerometer", "(Z)V"); jEnableAccelerometer = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableAccelerometer", "(Z)V");
jEnableLocation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocation", "(Z)V"); jEnableLocation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocation", "(Z)V");
jReturnIMEQueryResult = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "returnIMEQueryResult", "(Ljava/lang/String;II)V"); jReturnIMEQueryResult = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "returnIMEQueryResult", "(Ljava/lang/String;II)V");
@ -263,6 +265,13 @@ AndroidBridge::NotifyIMEChange(const PRUnichar *aText, PRUint32 aTextLen,
sBridge->jNotifyIMEChange, args); sBridge->jNotifyIMEChange, args);
} }
void
AndroidBridge::AcknowledgeEventSync()
{
ALOG_BRIDGE("AndroidBridge::AcknowledgeEventSync");
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jAcknowledgeEventSync);
}
void void
AndroidBridge::EnableAccelerometer(bool aEnable) AndroidBridge::EnableAccelerometer(bool aEnable)
{ {

View File

@ -111,6 +111,8 @@ public:
static void NotifyIMEChange(const PRUnichar *aText, PRUint32 aTextLen, int aStart, int aEnd, int aNewEnd); static void NotifyIMEChange(const PRUnichar *aText, PRUint32 aTextLen, int aStart, int aEnd, int aNewEnd);
void AcknowledgeEventSync();
void EnableAccelerometer(bool aEnable); void EnableAccelerometer(bool aEnable);
void EnableLocation(bool aEnable); void EnableLocation(bool aEnable);
@ -246,6 +248,7 @@ protected:
jmethodID jNotifyIME; jmethodID jNotifyIME;
jmethodID jNotifyIMEEnabled; jmethodID jNotifyIMEEnabled;
jmethodID jNotifyIMEChange; jmethodID jNotifyIMEChange;
jmethodID jAcknowledgeEventSync;
jmethodID jEnableAccelerometer; jmethodID jEnableAccelerometer;
jmethodID jEnableLocation; jmethodID jEnableLocation;
jmethodID jReturnIMEQueryResult; jmethodID jReturnIMEQueryResult;

View File

@ -451,6 +451,7 @@ public:
LOAD_URI = 11, LOAD_URI = 11,
SURFACE_CREATED = 12, SURFACE_CREATED = 12,
SURFACE_DESTROYED = 13, SURFACE_DESTROYED = 13,
GECKO_EVENT_SYNC = 14,
dummy_java_enum_list_end dummy_java_enum_list_end
}; };

View File

@ -363,7 +363,7 @@ nsWindow::Show(PRBool aState)
BringToFront(); BringToFront();
} else if (TopWindow() == this) { } else if (TopWindow() == this) {
// find the next visible window to show // find the next visible window to show
int i; unsigned int i;
for (i = 1; i < gTopLevelWindows.Length(); i++) { for (i = 1; i < gTopLevelWindows.Length(); i++) {
nsWindow *win = gTopLevelWindows[i]; nsWindow *win = gTopLevelWindows[i];
if (!win->mIsVisible) if (!win->mIsVisible)
@ -853,6 +853,10 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
sValidSurface = false; sValidSurface = false;
break; break;
case AndroidGeckoEvent::GECKO_EVENT_SYNC:
AndroidBridge::Bridge()->AcknowledgeEventSync();
break;
default: default:
break; break;
} }

View File

@ -119,6 +119,7 @@ static nsresult MacErrorMapper(OSErr inErr);
#ifdef ANDROID #ifdef ANDROID
#include "AndroidBridge.h" #include "AndroidBridge.h"
#include "nsIMIMEService.h" #include "nsIMIMEService.h"
#include <linux/magic.h>
#endif #endif
#include "nsNativeCharsetUtils.h" #include "nsNativeCharsetUtils.h"
@ -1103,9 +1104,20 @@ nsLocalFile::SetPermissions(PRUint32 aPermissions)
* Race condition here: we should use fchmod instead, there's no way to * Race condition here: we should use fchmod instead, there's no way to
* guarantee the name still refers to the same file. * guarantee the name still refers to the same file.
*/ */
if (chmod(mPath.get(), aPermissions) < 0) if (chmod(mPath.get(), aPermissions) >= 0)
return NSRESULT_FOR_ERRNO();
return NS_OK; return NS_OK;
#if defined(ANDROID) && defined(STATFS)
// For the time being, this is restricted for use by Android, but we
// will figure out what to do for all platforms in bug 638503
struct STATFS sfs;
if (STATFS(mPath.get(), &sfs) < 0)
return NSRESULT_FOR_ERRNO();
// if this is a FAT file system we can't set file permissions
if (sfs.f_type == MSDOS_SUPER_MAGIC )
return NS_OK;
#endif
return NSRESULT_FOR_ERRNO();
} }
NS_IMETHODIMP NS_IMETHODIMP