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.
var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
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.
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].

View File

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

View File

@ -58,6 +58,7 @@ public class CrashReporter extends Activity
static final String kMiniDumpPathKey = "upload_file_minidump";
static final String kPageURLKey = "URL";
static final String kNotesKey = "Notes";
Handler mHandler = null;
ProgressDialog mProgressDialog;
File mPendingMinidumpFile;
File mPendingExtrasFile;
@ -88,6 +89,15 @@ public class CrashReporter extends Activity
return true;
}
void doFinish() {
if (mHandler != null) {
mHandler.post(new Runnable(){
public void run() {
finish();
}});
}
}
@Override
public void finish()
{
@ -99,6 +109,8 @@ public class CrashReporter extends Activity
public void onCreate(Bundle 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);
mProgressDialog = new ProgressDialog(CrashReporter.this);
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);
if (!sendReportCheckbox.isChecked()) {
finish();
doFinish();
return;
}
@ -219,7 +231,7 @@ public class CrashReporter extends Activity
String spec = extras.get("ServerURL");
if (spec == null)
finish();
doFinish();
Log.i("GeckoCrashReport", "server url: " + spec);
try {
@ -298,7 +310,7 @@ public class CrashReporter extends Activity
Log.e("GeckoCrashReporter", "exception during send: ", e);
}
finish();
doFinish();
}
void doRestart()

View File

@ -455,6 +455,8 @@ abstract public class GeckoApp
}
}
boolean haveKilledZombies = false;
private void unpackFile(ZipFile zip, byte[] buf, ZipEntry fileEntry,
String name)
throws IOException, FileNotFoundException
@ -471,7 +473,10 @@ abstract public class GeckoApp
outFile.length() == fileEntry.getSize())
return;
killAnyZombies();
if (!haveKilledZombies) {
haveKilledZombies = true;
GeckoAppShell.killAnyZombies();
}
File dir = outFile.getParentFile();
if (!outFile.exists())
@ -492,27 +497,6 @@ abstract public class GeckoApp
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) {
Map<String,String> envMap = System.getenv();
Set<Map.Entry<String,String>> envSet = envMap.entrySet();
@ -537,15 +521,14 @@ abstract public class GeckoApp
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
Log.i("GeckoAppJava", intent.toString());
GeckoAppShell.killAnyZombies();
startActivity(intent);
} catch (Exception e) {
Log.i("GeckoAppJava", "error doing restart", e);
}
finish();
// Give the restart process time to start before we die
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException ie) {}
GeckoAppShell.waitForAnotherGeckoProc();
}
public void handleNotification(String action, String alertName, String alertCookie) {

View File

@ -44,6 +44,7 @@ import java.nio.channels.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;
import java.util.concurrent.*;
import android.os.*;
import android.app.*;
@ -63,7 +64,7 @@ import android.net.Uri;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
class GeckoAppShell
public class GeckoAppShell
{
// static members only
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.
public static native void notifyGeckoOfEvent(GeckoEvent event);
@ -471,6 +477,28 @@ class GeckoAppShell
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) {
SensorManager sm = (SensorManager)
GeckoApp.surfaceView.getContext().getSystemService(Context.SENSOR_SERVICE);
@ -868,4 +896,47 @@ class GeckoAppShell
config.locale = locale;
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_DESTROYED = 13;
public static final int GECKO_EVENT_SYNC = 14;
public static final int IME_COMPOSITION_END = 0;
public static final int IME_COMPOSITION_BEGIN = 1;

View File

@ -561,9 +561,10 @@ public class GeckoInputConnection
GeckoAppShell.sendEventToGecko(
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)

View File

@ -43,30 +43,24 @@ import android.content.*;
import android.util.*;
import android.os.*;
import java.io.*;
import org.mozilla.gecko.GeckoAppShell;
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
public void onCreate(Bundle savedInstanceState) {
Log.i("Restarter", "trying to restart @MOZ_APP_NAME@");
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) {
Log.i("Restarter", e.toString());
}
@ -84,6 +78,8 @@ public class Restarter extends Activity {
} catch (Exception e) {
Log.i("Restarter", e.toString());
}
// Give the new process time to start before we die
GeckoAppShell.waitForAnotherGeckoProc();
System.exit(0);
}
};

View File

@ -2237,6 +2237,7 @@ ComputeIsJITBroken()
do {
if (0 == line.find("Hardware")) {
const char* blacklist[] = {
"SCH-I400", // Samsung Continuum
"SGH-T959", // Samsung i9000, Vibrant device
"SGH-I897", // Samsung i9000, Captivate 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.)
*/
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,
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()
: mFD(nsnull)
, mCloseFD(PR_TRUE)
, mBehaviorFlags(0)
, mDeferredOpen(false)
{
}
nsFileStream::~nsFileStream()
{
if (mCloseFD)
Close();
}
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
nsFileStream::Close()
{
CleanUpOpen();
nsresult rv = NS_OK;
if (mFD) {
if (mCloseFD)
if (PR_Close(mFD) == PR_FAILURE)
rv = NS_BASE_STREAM_OSERROR;
mFD = nsnull;
@ -115,6 +101,9 @@ nsFileStream::Close()
NS_IMETHODIMP
nsFileStream::Seek(PRInt32 whence, PRInt64 offset)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (mFD == nsnull)
return NS_BASE_STREAM_CLOSED;
@ -128,6 +117,9 @@ nsFileStream::Seek(PRInt32 whence, PRInt64 offset)
NS_IMETHODIMP
nsFileStream::Tell(PRInt64 *result)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (mFD == nsnull)
return NS_BASE_STREAM_CLOSED;
@ -174,6 +166,62 @@ nsFileStream::SetEOF()
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
@ -232,12 +280,10 @@ nsFileInputStream::Open(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm)
if (aPerm == -1)
aPerm = 0;
PRFileDesc* fd;
rv = localFile->OpenNSPRFileDesc(aIOFlags, aPerm, &fd);
rv = MaybeOpen(localFile, aIOFlags, aPerm,
mBehaviorFlags & nsIFileInputStream::DEFER_OPEN);
if (NS_FAILED(rv)) return rv;
mFD = fd;
if (mBehaviorFlags & DELETE_ON_CLOSE) {
// POSIX compatible filesystems allow a file to be unlinked while a
// 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)
{
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;
@ -291,6 +337,9 @@ nsFileInputStream::Close()
NS_IMETHODIMP
nsFileInputStream::Available(PRUint32* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFD) {
return NS_BASE_STREAM_CLOSED;
}
@ -310,6 +359,9 @@ nsFileInputStream::Available(PRUint32* aResult)
NS_IMETHODIMP
nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFD) {
*aResult = 0;
return NS_OK;
@ -333,6 +385,9 @@ nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
NS_IMETHODIMP
nsFileInputStream::ReadLine(nsACString& aLine, PRBool* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mLineBuffer) {
nsresult rv = NS_InitLineBuffer(&mLineBuffer);
if (NS_FAILED(rv)) return rv;
@ -365,6 +420,9 @@ nsFileInputStream::IsNonBlocking(PRBool *aNonBlocking)
NS_IMETHODIMP
nsFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
PR_FREEIF(mLineBuffer); // this invalidates the line buffer
if (!mFD) {
if (mBehaviorFlags & REOPEN_ON_REWIND) {
@ -564,6 +622,9 @@ nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
PRInt32 behaviorFlags)
{
NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED);
mBehaviorFlags = behaviorFlags;
nsresult rv;
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
@ -573,12 +634,8 @@ nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
if (perm <= 0)
perm = 0664;
PRFileDesc* fd;
rv = localFile->OpenNSPRFileDesc(ioFlags, perm, &fd);
if (NS_FAILED(rv)) return rv;
mFD = fd;
return NS_OK;
return MaybeOpen(localFile, ioFlags, perm,
mBehaviorFlags & nsIFileOutputStream::DEFER_OPEN);
}
NS_IMETHODIMP
@ -590,6 +647,9 @@ nsFileOutputStream::Close()
NS_IMETHODIMP
nsFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (mFD == nsnull)
return NS_BASE_STREAM_CLOSED;
@ -604,6 +664,9 @@ nsFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
NS_IMETHODIMP
nsFileOutputStream::Flush(void)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (mFD == nsnull)
return NS_BASE_STREAM_CLOSED;
@ -653,7 +716,16 @@ NS_IMETHODIMP
nsSafeFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
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);
if (NS_FAILED(rv)) {
@ -680,16 +752,21 @@ nsSafeFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
PRUint32 origPerm;
if (NS_FAILED(file->GetPermissions(&origPerm))) {
NS_ERROR("Can't get permissions of target file");
origPerm = perm;
origPerm = mOpenParams.perm;
}
// XXX What if |perm| is more restrictive then |origPerm|?
// This leaves the user supplied permissions as they were.
rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm);
}
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;
mTargetFile = file;
rv = nsFileOutputStream::Init(mTempFile, ioFlags, perm, behaviorFlags);
rv = nsFileOutputStream::DoOpen();
}
return rv;
}

View File

@ -64,13 +64,57 @@ public:
virtual ~nsFileStream();
nsresult Close();
nsresult InitWithFileDescriptor(PRFileDesc* fd, nsISupports* parent);
protected:
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()
{
mLineBuffer = nsnull;
mBehaviorFlags = 0;
}
virtual ~nsFileInputStream()
{
@ -118,10 +161,6 @@ protected:
* The permissions passed to Init() for the file open.
*/
PRInt32 mPerm;
/**
* Flags describing our behavior. See the IDL file for possible values.
*/
PRInt32 mBehaviorFlags;
protected:
/**
@ -194,6 +233,8 @@ public:
virtual ~nsSafeFileOutputStream() { nsSafeFileOutputStream::Close(); }
virtual nsresult DoOpen();
NS_IMETHODIMP Close();
NS_IMETHODIMP Write(const char *buf, PRUint32 count, PRUint32 *result);
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.
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
@ -75,10 +78,15 @@ function getFileContents(aFile)
return string.value;
}
////////////////////////////////////////////////////////////////////////////////
//// Tests
function test_async_write_file()
/**
* Tests asynchronously writing a file using NetUtil.asyncCopy.
*
* @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();
@ -90,9 +98,8 @@ function test_async_write_file()
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
// Then, we need an output stream to our output file.
let ostream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
ostream.init(file, -1, -1, 0);
let ostream = Cc[aContractId].createInstance(Ci.nsIFileOutputStream);
ostream.init(file, -1, -1, aDeferOpen ? Ci.nsIFileOutputStream.DEFER_OPEN : 0);
// Finally, we need an input stream to take data from.
const TEST_DATA = "this is a test string";
@ -113,39 +120,23 @@ function test_async_write_file()
});
}
function test_async_write_file_nsISafeOutputStream()
{
do_test_pending();
////////////////////////////////////////////////////////////////////////////////
//// Tests
// First, we need an output file to write to.
let file = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties).
get("ProfD", Ci.nsIFile);
file.append("NetUtil-async-test-file.tmp");
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
function test_async_write_file() {
async_write_file(OUTPUT_STREAM_CONTRACT_ID);
}
// Then, we need an output stream to our output file.
let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
ostream.init(file, -1, -1, 0);
function test_async_write_file_deferred() {
async_write_file(OUTPUT_STREAM_CONTRACT_ID, true);
}
// Finally, we need an input stream to take data from.
const TEST_DATA = "this is a test string";
let istream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
istream.setData(TEST_DATA, TEST_DATA.length);
function test_async_write_file_safe() {
async_write_file(SAFE_OUTPUT_STREAM_CONTRACT_ID);
}
NetUtil.asyncCopy(istream, ostream, function(aResult) {
// Make sure the copy was successful!
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_async_write_file_safe_deferred() {
async_write_file(SAFE_OUTPUT_STREAM_CONTRACT_ID, true);
}
function test_newURI_no_spec_throws()
@ -529,7 +520,9 @@ function test_readInputStreamToString_too_many_bytes()
let tests = [
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,
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
if (extractLibs) {
char fullpath[256];
snprintf(fullpath, 256, "%s/%s", getenv("CACHE_PATH"), path + 4);
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s/%s", getenv("CACHE_PATH"), path + 4);
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "resolved %s to %s", path, fullpath);
extractFile(fullpath, entry, data);
handle = __wrap_dlopen(fullpath, RTLD_LAZY);
@ -474,7 +474,7 @@ static void * mozload(const char * path, void *zip,
}
#endif
size_t offset = letoh32(entry->offset) + sizeof(*file) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
bool skipLibCache = false;
int fd = zip_fd;
void * buf = NULL;
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);
fd = cache_fd;
if (fd < 0) {
char fullpath[256];
snprintf(fullpath, 256, "%s/%s", getenv("CACHE_PATH"), path + 4);
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s/%s", getenv("CACHE_PATH"), path + 4);
fd = open(fullpath, O_RDWR);
struct stat 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);
#endif
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;
}
// 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,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
@ -520,8 +537,10 @@ static void * mozload(const char * path, void *zip,
if (cache_fd < 0) {
extractLib(entry, data, buf);
if (!skipLibCache)
addLibCacheFd(path + 4, fd, lib_size, buf);
}
// preload libxul, to avoid slowly demand-paging it
if (!strcmp(path, "lib/libxul.so"))
madvise(buf, entry->uncompressed_size, MADV_WILLNEED);
@ -719,9 +738,9 @@ Java_org_mozilla_gecko_GeckoAppShell_loadLibs(JNIEnv *jenv, jclass jGeckoAppShel
if (!info->buffer)
continue;
char fullpath[256];
snprintf(fullpath, 256, "%s/%s", getenv("CACHE_PATH"), info->name);
char tmp_path[256];
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s/%s", getenv("CACHE_PATH"), info->name);
char tmp_path[PATH_MAX];
sprintf(tmp_path, "%s.tmp", fullpath);
int file_fd = open(tmp_path, O_CREAT | O_WRONLY);
// using sendfile would be preferable, but it doesn't seem to work

View File

@ -2560,7 +2560,7 @@ SearchService.prototype = {
try {
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";
let data = converter.convertToInputStream(JSON.stringify(cache));

View File

@ -929,6 +929,40 @@ function validateFileName(aFileName)
}
else if (navigator.appVersion.indexOf("Macintosh") != -1)
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, "_");
}

View File

@ -107,6 +107,8 @@ AndroidBridge::Init(JNIEnv *jEnv,
jNotifyIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIME", "(II)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");
jAcknowledgeEventSync = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "acknowledgeEventSync", "()V");
jEnableAccelerometer = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableAccelerometer", "(Z)V");
jEnableLocation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocation", "(Z)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);
}
void
AndroidBridge::AcknowledgeEventSync()
{
ALOG_BRIDGE("AndroidBridge::AcknowledgeEventSync");
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jAcknowledgeEventSync);
}
void
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);
void AcknowledgeEventSync();
void EnableAccelerometer(bool aEnable);
void EnableLocation(bool aEnable);
@ -246,6 +248,7 @@ protected:
jmethodID jNotifyIME;
jmethodID jNotifyIMEEnabled;
jmethodID jNotifyIMEChange;
jmethodID jAcknowledgeEventSync;
jmethodID jEnableAccelerometer;
jmethodID jEnableLocation;
jmethodID jReturnIMEQueryResult;

View File

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

View File

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

View File

@ -119,6 +119,7 @@ static nsresult MacErrorMapper(OSErr inErr);
#ifdef ANDROID
#include "AndroidBridge.h"
#include "nsIMIMEService.h"
#include <linux/magic.h>
#endif
#include "nsNativeCharsetUtils.h"
@ -1103,9 +1104,20 @@ nsLocalFile::SetPermissions(PRUint32 aPermissions)
* Race condition here: we should use fchmod instead, there's no way to
* guarantee the name still refers to the same file.
*/
if (chmod(mPath.get(), aPermissions) < 0)
return NSRESULT_FOR_ERRNO();
if (chmod(mPath.get(), aPermissions) >= 0)
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