Bug 708651 - Create a fake profile directory if BrowserProvider is started before Gecko. r=blassey

This commit is contained in:
Wes Johnston 2012-01-04 17:28:04 -08:00
parent fc3b37def5
commit 1d05dc9cb7
5 changed files with 269 additions and 43 deletions

View File

@ -52,6 +52,8 @@ import org.mozilla.gecko.Tab.HistoryEntry;
import java.io.*;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.zip.*;
import java.net.URL;
import java.nio.*;
@ -133,7 +135,6 @@ abstract public class GeckoApp
private static GeckoSoftwareLayerClient mSoftwareLayerClient;
private AboutHomeContent mAboutHomeContent;
private static AbsoluteLayout mPluginContainer;
boolean mUserDefinedProfile = false;
public String mLastUri;
public String mLastTitle;
@ -417,8 +418,8 @@ abstract public class GeckoApp
InputStream is = (InputStream) url.getContent();
Drawable drawable = Drawable.createFromStream(is, "src");
mi.setIcon(drawable);
} catch(Exception e) {
Log.e(LOGTAG, "onPrepareOptionsMenu: Unable to set icon", e);
} catch (Exception e) {
Log.w(LOGTAG, "onPrepareOptionsMenu: Unable to set icon", e);
}
}
});
@ -757,24 +758,16 @@ abstract public class GeckoApp
}
public File getProfileDir() {
// XXX: TO-DO read profiles.ini to get the default profile
return getProfileDir("default");
}
public File getProfileDir(final String profileName) {
if (mProfileDir == null && !mUserDefinedProfile) {
File mozDir = new File(GeckoAppShell.sHomeDir, "mozilla");
if (!mozDir.exists()) {
// Profile directory may have been deleted or not created yet.
return null;
}
File[] profiles = mozDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith("." + profileName);
}
});
if (profiles != null && profiles.length == 1)
mProfileDir = profiles[0];
if (mProfileDir != null)
return mProfileDir;
try {
mProfileDir = GeckoDirProvider.getProfileDir(mAppContext, profileName);
} catch (IOException ex) {
Log.e(LOGTAG, "Error getting profile dir.", ex);
}
return mProfileDir;
}
@ -1431,8 +1424,15 @@ abstract public class GeckoApp
String args = intent.getStringExtra("args");
if (args != null && args.contains("-profile")) {
// XXX: TO-DO set mProfileDir to the path passed in
mUserDefinedProfile = true;
Pattern p = Pattern.compile("(?:-profile\\s*)(\\w*)(\\s*)");
Matcher m = p.matcher(args);
if (m.find()) {
mLastUri = null;
mLastTitle = null;
mLastViewport = null;
mProfileDir = new File(m.group(1));
}
}
prefetchDNS(intent.getData());
@ -1501,11 +1501,8 @@ abstract public class GeckoApp
* run experience, perhaps?
*/
mLayerController = new LayerController(this);
mPlaceholderLayerClient = mUserDefinedProfile ? null :
PlaceholderLayerClient.createInstance(this);
if (mPlaceholderLayerClient != null) {
mLayerController.setLayerClient(mPlaceholderLayerClient);
}
mPlaceholderLayerClient = PlaceholderLayerClient.createInstance(this);
mLayerController.setLayerClient(mPlaceholderLayerClient);
mGeckoLayout.addView(mLayerController.getView(), 0);
}

View File

@ -302,17 +302,19 @@ public class GeckoAppShell
// libxul will depend on. Not ideal.
GeckoApp geckoApp = GeckoApp.mAppContext;
String homeDir;
sHomeDir = GeckoDirProvider.getFilesDir(geckoApp);
homeDir = sHomeDir.getPath();
// handle the application being moved to phone from sdcard
File profileDir = new File(homeDir, "mozilla");
File oldHome = new File("/data/data/" +
GeckoApp.mAppContext.getPackageName() + "/mozilla");
if (oldHome.exists())
moveDir(oldHome, profileDir);
if (Build.VERSION.SDK_INT < 8 ||
geckoApp.getApplication().getPackageResourcePath().startsWith("/data") ||
geckoApp.getApplication().getPackageResourcePath().startsWith("/system")) {
sHomeDir = geckoApp.getFilesDir();
homeDir = sHomeDir.getPath();
// handle the application being moved to phone from sdcard
File profileDir = new File(homeDir, "mozilla");
File oldHome = new File("/data/data/" +
GeckoApp.mAppContext.getPackageName() + "/mozilla");
if (oldHome.exists())
moveDir(oldHome, profileDir);
if (Build.VERSION.SDK_INT >= 8) {
File extHome = geckoApp.getExternalFilesDir(null);
File extProf = new File (extHome, "mozilla");
@ -320,15 +322,6 @@ public class GeckoAppShell
moveDir(extProf, profileDir);
}
} else {
sHomeDir = geckoApp.getExternalFilesDir(null);
homeDir = sHomeDir.getPath();
// handle the application being moved to phone from sdcard
File profileDir = new File(homeDir, "mozilla");
File oldHome = new File("/data/data/" +
GeckoApp.mAppContext.getPackageName() + "/mozilla");
if (oldHome.exists())
moveDir(oldHome, profileDir);
File intHome = geckoApp.getFilesDir();
File intProf = new File(intHome, "mozilla");
if (intHome != null && intProf != null && intProf.exists())

View File

@ -0,0 +1,215 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Wes Johnston <wjohnston@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.gecko;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.Void;
import java.util.Date;
import java.util.Random;
import java.util.Map;
import java.util.HashMap;
abstract public class GeckoDirProvider
{
private static final String LOGTAG = "GeckoDirProvider";
private static HashMap<String, File> mProfileDirs = new HashMap<String, File>();
/**
* Get the default Mozilla profile directory for a given Activity instance.
*
* @param aContext
* The context for the activity. Must not be null
* @return
* The profile directory.
*/
static public File getProfileDir(final Context aContext)
throws IllegalArgumentException, IOException {
// XXX: TO-DO read profiles.ini to get the default profile. bug 71530
return getProfileDir(aContext, "default");
}
/**
* Get a particular profile directory for a given Activity.
* If no profile directory currently exists, will create and return a profile directory.
* Otherwise will return null;
*
* @param aContext
* The context for the Activity we want a profile for. Must not be null.
* @param aProfileName
* The name of the profile to open. Must be a non-empty string
* @return
* The profile directory.
*/
static public File getProfileDir(final Context aContext, final String aProfileName)
throws IllegalArgumentException, IOException {
if (aContext == null)
throw new IllegalArgumentException("Must provide a valid context");
if (aProfileName == null || aProfileName.trim().equals(""))
throw new IllegalArgumentException("Profile name: '" + aProfileName + "' is not valid");
Log.i(LOGTAG, "Get profile dir for " + aProfileName);
synchronized (mProfileDirs) {
File profileDir = mProfileDirs.get(aProfileName);
if (profileDir != null)
return profileDir;
// we do not want to call File.exists on startup, so we first don't
// attempt to create the mozilla directory.
File mozDir = GeckoDirProvider.ensureMozillaDirectory(aContext);
profileDir = GeckoDirProvider.getProfileDir(mozDir, aProfileName);
if (profileDir == null) {
// Throws if cannot create.
profileDir = GeckoDirProvider.createProfileDir(mozDir, aProfileName);
}
mProfileDirs.put(aProfileName, profileDir);
return profileDir;
}
}
private static File getProfileDir(final File aRoot, final String aProfileName)
throws IllegalArgumentException {
if (aRoot == null)
throw new IllegalArgumentException("Invalid root directory");
File[] profiles = aRoot.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith("." + aProfileName);
}
});
if (profiles != null && profiles.length > 0)
return profiles[0];
return null;
}
private static File ensureMozillaDirectory(final Context aContext)
throws IOException, IllegalArgumentException {
if (aContext == null)
throw new IllegalArgumentException("Must provide a valid context");
File filesDir = GeckoDirProvider.getFilesDir(aContext);
File mozDir = new File(filesDir, "mozilla");
if (!mozDir.exists()) {
if (!mozDir.mkdir())
throw new IOException("Unable to create mozilla directory at " + mozDir.getPath());
}
return mozDir;
}
static final char kTable[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
private static File createProfileDir(final File aRootDir, final String aProfileName)
throws IOException, IllegalArgumentException {
if (aRootDir == null)
throw new IllegalArgumentException("Must provide a valid root directory");
if (aProfileName == null || aProfileName.trim().equals(""))
throw new IllegalArgumentException("Profile name: '" + aProfileName + "' is not valid");
// XXX: TO-DO If we already have an ini file, we should append the
// new profile information to it. For now we just throw an exception.
// see bug 715391
final File profileIni = new File(aRootDir, "profiles.ini");
if (profileIni.exists())
throw new IOException("Can't create new profiles");
String saltedName = saltProfileName(aProfileName);
File profile = new File(aRootDir, saltedName);
while (profile.exists()) {
saltedName = saltProfileName(aProfileName);
profile = new File(aRootDir, saltedName);
}
if (!profile.mkdir())
throw new IOException("Unable to create profile at " + profile.getPath());
Log.i(LOGTAG, "Creating new profile at " + profile.getPath());
final String fSaltedName = saltedName;
FileWriter outputStream = new FileWriter(profileIni, true);
outputStream.write("[General]\n" +
"StartWithLastProfile=1\n" +
"\n" +
"[Profile0]\n" +
"Name=" + aProfileName + "\n" +
"IsRelative=1\n" +
"Path=" + fSaltedName + "\n" +
"Default=1\n");
outputStream.close();
return profile;
}
private static String saltProfileName(final String aName) {
Random randomGenerator = new Random(System.nanoTime());
StringBuilder salt = new StringBuilder();
int i;
for (i = 0; i < 8; ++i)
salt.append(kTable[randomGenerator.nextInt(kTable.length)]);
salt.append(".");
return salt.append(aName).toString();
}
public static File getFilesDir(final Context aContext) {
if (aContext == null)
throw new IllegalArgumentException("Must provide a valid context");
if (Build.VERSION.SDK_INT < 8 ||
aContext.getPackageResourcePath().startsWith("/data") ||
aContext.getPackageResourcePath().startsWith("/system")) {
return aContext.getFilesDir();
}
return aContext.getExternalFilesDir(null);
}
}

View File

@ -78,6 +78,7 @@ JAVAFILES = \
GeckoAsyncTask.java \
GeckoBatteryManager.java \
GeckoConnectivityReceiver.java \
GeckoDirProvider.java \
GeckoEvent.java \
GeckoEventListener.java \
GeckoInputConnection.java \

View File

@ -41,10 +41,12 @@
package @ANDROID_PACKAGE_NAME@.db;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Random;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoDirProvider;
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
import org.mozilla.gecko.db.BrowserContract.CommonColumns;
import org.mozilla.gecko.db.BrowserContract.History;
@ -72,6 +74,7 @@ import android.util.Log;
public class BrowserProvider extends ContentProvider {
private static final String LOGTAG = "GeckoBrowserProvider";
private Context mContext;
static final String DATABASE_NAME = "browser.db";
@ -417,7 +420,13 @@ public class BrowserProvider extends ContentProvider {
return "browser-" + profile + ".db";
}
File profileDir = GeckoApp.mAppContext.getProfileDir(profile);
File profileDir = null;
try {
profileDir = GeckoDirProvider.getProfileDir(mContext, profile);
} catch (IOException ex) {
Log.e(LOGTAG, "Error getting profile dir", ex);
}
if (profileDir == null) {
Log.d(LOGTAG, "Couldn't find directory for profile: " + profile);
return null;
@ -507,7 +516,18 @@ public class BrowserProvider extends ContentProvider {
public boolean onCreate() {
Log.d(LOGTAG, "Creating BrowserProvider");
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
// Kick this off early. It is synchronized so that other callers will wait
try {
GeckoDirProvider.getProfileDir(getContext());
} catch (Exception ex) {
Log.e(LOGTAG, "Error getting profile dir", ex);
}
}
});
synchronized (this) {
mContext = getContext();
mDatabasePerProfile = new HashMap<String, DatabaseHelper>();
}