--- a/.inferconfig
+++ b/.inferconfig
@@ -1,7 +1,8 @@
{
"infer-blacklist-path-regex": [
// This is full of issues, and is a dependency we need to discard
// sooner rather than later anyway:
- "mobile/android/thirdparty/ch/boye/httpclientandroidlib"
+ "mobile/android/thirdparty/ch/boye/httpclientandroidlib",
+ "mobile/android/thirdparty/"
]
}
\ No newline at end of file
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -3086,17 +3086,21 @@ public class BrowserApp extends GeckoApp
final MenuItem tools = menu.findItem(R.id.tools);
destination = tools != null ? tools.getSubMenu() : menu;
} else {
final MenuItem parent = menu.findItem(info.parent);
if (parent == null) {
return;
}
- Menu parentMenu = findParentMenu(menu, parent);
+ final Menu parentMenu = findParentMenu(menu, parent);
+
+ if (parentMenu == null) {
+ // TODO: ?
+ }
if (!parent.hasSubMenu()) {
parentMenu.removeItem(parent.getItemId());
destination = parentMenu.addSubMenu(Menu.NONE, parent.getItemId(), Menu.NONE, parent.getTitle());
if (parent.getIcon() != null) {
((SubMenu) destination).getItem().setIcon(parent.getIcon());
}
} else {
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java
@@ -130,17 +130,19 @@ public class GeckoService extends Servic
return intent;
}
public static Intent getIntentToCreateServices(final Context context, final String category) {
return getIntentToCreateServices(context, category, /* data */ null);
}
public static Intent getIntentToLoadLibs(final Context context) {
- return getIntentForAction(context, INTENT_ACTION_LOAD_LIBS);
+ // We don't want a profile specific intent since the profile might not exist yet
+ return new Intent(INTENT_ACTION_LOAD_LIBS, /* uri */ null, context, GeckoService.class);
+// return getIntentForAction(context, INTENT_ACTION_LOAD_LIBS);
}
public static Intent getIntentToStartGecko(final Context context) {
return getIntentForAction(context, INTENT_ACTION_START_GECKO);
}
public static void setIntentProfile(final Intent intent, final String profileName,
final String profileDir) {
--- a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java
@@ -1109,16 +1109,17 @@ public class LocalBrowserDB extends Brow
return mFolderIdMap.get(guid);
}
final Cursor c = cr.query(mBookmarksUriWithProfile,
new String[] { Bookmarks._ID },
Bookmarks.GUID + " = ?",
new String[] { guid },
null);
+
try {
if (c == null || !c.moveToFirst()) {
return FOLDER_NOT_FOUND;
}
final int col = c.getColumnIndexOrThrow(Bookmarks._ID);
if (c.isNull(col)) {
return FOLDER_NOT_FOUND;
--- a/mobile/android/base/java/org/mozilla/gecko/home/activitystream/menu/ActivityStreamContextMenu.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/activitystream/menu/ActivityStreamContextMenu.java
@@ -91,17 +91,17 @@ public abstract class ActivityStreamCont
// Disable "dismiss" for topsites until we have decided on its behaviour for topsites
// (currently "dismiss" adds the URL to a highlights-specific blocklist, which the topsites
// query has no knowledge of).
if (mode == MenuMode.TOPSITE) {
final MenuItem dismissItem = getItemByID(R.id.dismiss);
dismissItem.setVisible(false);
}
- if (item.isBookmarked() == null) {
+ if (Boolean.TRUE.equals(item.isBookmarked())) {
// Disable the bookmark item until we know its bookmark state
bookmarkItem.setEnabled(false);
(new UIAsyncTask.WithoutParams<Boolean>(ThreadUtils.getBackgroundHandler()) {
@Override
protected Boolean doInBackground() {
return BrowserDB.from(context).isBookmark(context.getContentResolver(), item.getUrl());
}
@@ -193,17 +193,17 @@ public abstract class ActivityStreamCont
case R.id.bookmark:
final TelemetryContract.Event telemetryEvent;
final String telemetryExtra;
SavedReaderViewHelper rch = SavedReaderViewHelper.getSavedReaderViewHelper(context);
final boolean isReaderViewPage = rch.isURLCached(item.getUrl());
// While isBookmarked is nullable, behaviour of postInit - disabling 'bookmark' item
// until we know value of isBookmarked - guarantees that it will be set when we get here.
- if (item.isBookmarked()) {
+ if (Boolean.TRUE.equals(item.isBookmarked())) {
telemetryEvent = TelemetryContract.Event.UNSAVE;
if (isReaderViewPage) {
telemetryExtra = "as_bookmark_reader";
} else {
telemetryExtra = "as_bookmark";
}
telemetryExtraBuilder.set(ActivityStreamTelemetry.Contract.ITEM, ActivityStreamTelemetry.Contract.ITEM_REMOVE_BOOKMARK);
@@ -217,43 +217,43 @@ public abstract class ActivityStreamCont
// a concrete event in case it is used by other queries to estimate feature usage.
Telemetry.sendUIEvent(telemetryEvent, TelemetryContract.Method.CONTEXT_MENU, telemetryExtra);
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
final BrowserDB db = BrowserDB.from(context);
- if (item.isBookmarked()) {
+ if (Boolean.TRUE.equals(item.isBookmarked())) {
db.removeBookmarksWithURL(context.getContentResolver(), item.getUrl());
} else {
// We only store raw URLs in history (and bookmarks), hence we won't ever show about:reader
// URLs in AS topsites or highlights. Therefore we don't need to do any special about:reader handling here.
db.addBookmark(context.getContentResolver(), item.getTitle(), item.getUrl());
}
}
});
break;
case R.id.pin:
// While isPinned is nullable, behaviour of postInit - disabling 'pin' item
// until we know value of isPinned - guarantees that it will be set when we get here.
- if (item.isPinned()) {
+ if (Boolean.TRUE.equals(item.isPinned())) {
telemetryExtraBuilder.set(ActivityStreamTelemetry.Contract.ITEM, ActivityStreamTelemetry.Contract.ITEM_UNPIN);
} else {
telemetryExtraBuilder.set(ActivityStreamTelemetry.Contract.ITEM, ActivityStreamTelemetry.Contract.ITEM_PIN);
}
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
final BrowserDB db = BrowserDB.from(context);
- if (item.isPinned()) {
+ if (Boolean.TRUE.equals(item.isPinned())) {
db.unpinSiteForAS(context.getContentResolver(), item.getUrl());
} else {
db.pinSiteForAS(context.getContentResolver(), item.getUrl(), item.getTitle());
}
}
});
break;
--- a/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java
+++ b/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java
@@ -9,16 +9,17 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoSharedPrefs;
+import org.mozilla.gecko.GeckoThread;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.WindowUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
@@ -93,16 +94,18 @@ public class LightweightTheme implements
// Let's keep the saved data in sync.
mSavedURL = mHeaderURL;
mSavedColor = mColor;
}
@Override
public void run() {
+// GeckoThread.waitOnGecko();
+
// Load the data from preferences, if it exists.
loadFromPrefs();
if (TextUtils.isEmpty(mHeaderURL)) {
// mHeaderURL is null is this is the early startup path. Use
// the saved values, if we have any.
mHeaderURL = mSavedURL;
mColor = mSavedColor;
--- a/mobile/android/base/java/org/mozilla/gecko/search/SearchEngine.java
+++ b/mobile/android/base/java/org/mozilla/gecko/search/SearchEngine.java
@@ -1,15 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.search;
import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Xml;
import org.mozilla.gecko.util.StringUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@@ -21,16 +23,22 @@ import java.util.Set;
/**
* Extend this class to add a new search engine to
* the search activity.
*/
public class SearchEngine {
private static final String LOG_TAG = "SearchEngine";
+ private enum URLTYPE {
+ SUGGEST_JSON,
+ SEARCH_HTML_MOBILE,
+ SEARCH_HTML;
+ }
+
private static final String URLTYPE_SUGGEST_JSON = "application/x-suggestions+json";
private static final String URLTYPE_SEARCH_HTML = "text/html";
private static final String URL_REL_MOBILE = "mobile";
// Parameters copied from nsSearchService.js
private static final String MOZ_PARAM_LOCALE = "\\{moz:locale\\}";
private static final String MOZ_PARAM_DIST_ID = "\\{moz:distributionID\\}";
@@ -48,77 +56,131 @@ public class SearchEngine {
// head of a web page. The actual CSS is inserted at `%s`.
private static final String STYLE_INJECTION_SCRIPT =
"javascript:(function(){" +
"var tag=document.createElement('style');" +
"tag.type='text/css';" +
"document.getElementsByTagName('head')[0].appendChild(tag);" +
"tag.innerText='%s'})();";
+
// The Gecko search identifier. This will be null for engines that don't ship with the locale.
- private final String identifier;
+ private final @Nullable String identifier;
+
+ // Mandatory
+ private final @NonNull String shortName;
+ // Optional
+ private final @Nullable String iconURL;
- private String shortName;
- private String iconURL;
+ // Ordered list of preferred results URIs
+ // At least one URL is mandatory
+ private final @NonNull List<Uri> resultsUris;
+ // = new ArrayList<>();
+ // Optional
+ private final @Nullable Uri suggestUri;
- // Ordered list of preferred results URIs.
- private final List<Uri> resultsUris = new ArrayList<Uri>();
- private Uri suggestUri;
+ private SearchEngine(final @Nullable String identifier, final @NonNull String shortName, final @Nullable String iconURL, final @NonNull List<Uri> resultsUris, final @Nullable Uri suggestUri) {
+ this.identifier = identifier;
+ this.shortName = shortName;
+ this.iconURL = iconURL;
+ this.resultsUris = resultsUris;
+ this.suggestUri = suggestUri;
+ }
/**
*
* @param in InputStream of open search plugin XML
*/
- public SearchEngine(String identifier, InputStream in) throws IOException, XmlPullParserException {
- this.identifier = identifier;
-
+ public static SearchEngine readSearchPlugin(@Nullable String identifier, InputStream in) throws XmlPullParserException, IOException {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parser.nextTag();
- readSearchPlugin(parser);
- }
- private void readSearchPlugin(XmlPullParser parser) throws XmlPullParserException, IOException {
if (XmlPullParser.START_TAG != parser.getEventType()) {
throw new XmlPullParserException("Expected start tag: " + parser.getPositionDescription());
}
final String name = parser.getName();
if (!"SearchPlugin".equals(name) && !"OpenSearchDescription".equals(name)) {
throw new XmlPullParserException("Expected <SearchPlugin> or <OpenSearchDescription> as root tag: "
+ parser.getPositionDescription());
}
+ String shortName = null;
+ String iconURL = null;
+ final List<Uri> resultsUris = new ArrayList<Uri>();
+ Uri suggestUri = null;
+
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
final String tag = parser.getName();
if (tag.equals("ShortName")) {
- readShortName(parser);
+ // Must appear only once according to spec
+ if (shortName != null) {
+ throw new IllegalStateException("SearchEngine definition cannot contain more than one shortName");
+ }
+ shortName = readShortName(parser);
} else if (tag.equals("Url")) {
- readUrl(parser);
+ final UrlItem item = readSuggestURI(parser);
+ if (item != null) {
+ switch (item.type) {
+ case SEARCH_HTML_MOBILE:
+ // MOBILE specific urls are preferred
+ resultsUris.add(0, item.url);
+ break;
+ case SEARCH_HTML:
+ resultsUris.add(item.url);
+ break;
+ case SUGGEST_JSON:
+ suggestUri = item.url;
+ break;
+ }
+ }
+
} else if (tag.equals("Image")) {
- readImage(parser);
+ iconURL = readImageURL(parser);
} else {
skip(parser);
}
}
+
+ if (shortName == null) {
+ throw new IllegalStateException("Invalid SearchEngine definition: must contain shortName");
+ } else if (resultsUris.size() == 0) {
+ throw new IllegalStateException("Invaid SearchEngine definition: must contain at least one search url");
+ }
+
+ return new SearchEngine(identifier, shortName, iconURL, resultsUris, suggestUri);
}
- private void readShortName(XmlPullParser parser) throws IOException, XmlPullParserException {
+ private static String readShortName(XmlPullParser parser) throws IOException, XmlPullParserException {
parser.require(XmlPullParser.START_TAG, null, "ShortName");
if (parser.next() == XmlPullParser.TEXT) {
- shortName = parser.getText();
+ final String shortName = parser.getText();
parser.nextTag();
+ return shortName;
+ }
+
+ throw new IllegalStateException("Wrong datatype when loading shortName");
+ }
+
+ private static class UrlItem {
+ final URLTYPE type;
+ final Uri url;
+
+ UrlItem(final URLTYPE type, final Uri url) {
+ this.type = type;
+ this.url = url;
}
}
- private void readUrl(XmlPullParser parser) throws XmlPullParserException, IOException {
+ private static UrlItem readSuggestURI(XmlPullParser parser) throws XmlPullParserException, IOException {
parser.require(XmlPullParser.START_TAG, null, "Url");
final String type = parser.getAttributeValue(null, "type");
final String template = parser.getAttributeValue(null, "template");
final String rel = parser.getAttributeValue(null, "rel");
Uri uri = Uri.parse(template);
@@ -137,41 +199,46 @@ public class SearchEngine {
// TODO: Support for other tags
//} else if (tag.equals("MozParam")) {
} else {
skip(parser);
}
}
if (type.equals(URLTYPE_SEARCH_HTML)) {
- // Prefer mobile URIs.
if (rel != null && rel.equals(URL_REL_MOBILE)) {
- resultsUris.add(0, uri);
+ return new UrlItem(URLTYPE.SEARCH_HTML_MOBILE, uri);
+
} else {
- resultsUris.add(uri);
+ return new UrlItem(URLTYPE.SEARCH_HTML, uri);
}
} else if (type.equals(URLTYPE_SUGGEST_JSON)) {
- suggestUri = uri;
+ return new UrlItem(URLTYPE.SUGGEST_JSON, uri);
+ } else {
+ throw new IllegalStateException("Error");
}
}
- private void readImage(XmlPullParser parser) throws XmlPullParserException, IOException {
+ private static String readImageURL(XmlPullParser parser) throws XmlPullParserException, IOException {
parser.require(XmlPullParser.START_TAG, null, "Image");
// TODO: Use width and height to get a preferred icon URL.
//final int width = Integer.parseInt(parser.getAttributeValue(null, "width"));
//final int height = Integer.parseInt(parser.getAttributeValue(null, "height"));
if (parser.next() == XmlPullParser.TEXT) {
- iconURL = parser.getText();
+ final String iconURL = parser.getText();
parser.nextTag();
+ return iconURL;
}
+
+ throw new IllegalStateException("Unexpected datatype for URL");
}
- private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+ private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
if (parser.getEventType() != XmlPullParser.START_TAG) {
throw new IllegalStateException();
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
case XmlPullParser.END_TAG:
depth--;
@@ -264,19 +331,19 @@ public class SearchEngine {
final String template = Uri.decode(suggestUri.toString());
return paramSubstitution(template, Uri.encode(query));
}
/**
* @return Preferred results URI.
*/
private Uri getResultsUri() {
- if (resultsUris.isEmpty()) {
- return null;
- }
+// if (resultsUris.isEmpty()) {
+// return null;
+// }
return resultsUris.get(0);
}
/**
* Formats template string with proper parameters. Modeled after
* ParamSubstitution in nsSearchService.js
*
* @param template
--- a/mobile/android/base/java/org/mozilla/gecko/search/SearchEngineManager.java
+++ b/mobile/android/base/java/org/mozilla/gecko/search/SearchEngineManager.java
@@ -668,17 +668,17 @@ public class SearchEngineManager impleme
* @param identifier Seach engine identifier. This only exists for search engines that
* ship with the default set of engines in the locale.
* @param in InputStream for search plugin XML file.
* @return SearchEngine instance.
*/
private SearchEngine createEngineFromInputStream(String identifier, InputStream in) {
try {
try {
- return new SearchEngine(identifier, in);
+ return SearchEngine.readSearchPlugin(identifier, in);
} finally {
in.close();
}
} catch (Exception e) {
Log.e(LOG_TAG, "Exception creating search engine", e);
}
return null;
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsLayout.java
@@ -58,18 +58,23 @@ public abstract class TabsLayout extends
setAdapter(tabsAdapter);
RecyclerViewClickSupport.addTo(this).setOnItemClickListener(this);
setRecyclerListener(new RecyclerListener() {
@Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
final TabsLayoutItemView itemView = (TabsLayoutItemView) holder.itemView;
- itemView.setThumbnail(null);
- itemView.setCloseVisible(true);
+
+ if (itemView != null) {
+ itemView.setThumbnail(null);
+ itemView.setCloseVisible(true);
+ } else {
+ throw new IllegalStateException("TabsLayout ViewHolder is of unexpected/unhandled type: " + holder.getClass().getName());
+ }
}
});
}
@Override
public void setTabsPanel(TabsPanel panel) {
tabsPanel = panel;
}
--- a/mobile/android/base/java/org/mozilla/gecko/widget/ActivityChooserModel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/ActivityChooserModel.java
@@ -1088,17 +1088,17 @@ public class ActivityChooserModel extend
/**
* Command for reading the historical records from a file off the UI thread.
*/
private void readHistoricalDataImpl() {
try {
GeckoProfile profile = GeckoProfile.get(mContext);
File f = profile.getFile(mHistoryFileName);
- if (!f.exists()) {
+ if (f == null || !f.exists()) {
// Fall back to the non-profile aware file if it exists...
File oldFile = new File(mHistoryFileName);
oldFile.renameTo(f);
}
readHistoricalDataFromStream(new FileInputStream(f));
} catch (FileNotFoundException fnfe) {
final Distribution dist = Distribution.getInstance(mContext);
dist.addOnDistributionReadyCallback(new Distribution.ReadyCallback() {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java
@@ -3,16 +3,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
+import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
@@ -204,17 +205,17 @@ public final class GeckoProfile {
// ------------------------------------------
// Yes | Yes | Active profile or default profile.
// No | Yes | Profile with specified name at default dir.
// Yes | No | Custom (anonymous) profile with specified dir.
// No | No | Profile with specified name at specified dir.
if (profileName == null && profileDir == null) {
// If no profile info was passed in, look for the active profile or a default profile.
- final GeckoProfile profile = GeckoThread.getActiveProfile();
+ final GeckoProfile profile = GeckoThread.getActiveProfileIfExists();
if (profile != null) {
return profile;
}
final String args;
if (context instanceof Activity) {
args = IntentUtils.getStringExtraSafe(((Activity) context).getIntent(), "args");
} else {
@@ -391,50 +392,50 @@ public final class GeckoProfile {
return this;
}
/**
* Retrieves the directory backing the profile. This method acts
* as a lazy initializer for the GeckoProfile instance.
*/
@RobocopTarget
- public synchronized File getDir() {
- forceCreateLocked();
- return mProfileDir;
+ public synchronized @NonNull File getDir() {
+ return forceCreateLocked();
}
/**
* Forces profile creation. Consider using {@link #getDir()} to initialize the profile instead - it is the
* lazy initializer and, for our code reasoning abilities, we should initialize the profile in one place.
*/
- private void forceCreateLocked() {
+ private @NonNull File forceCreateLocked() {
if (mProfileDir != null) {
- return;
+ return mProfileDir;
}
+ // Check if a profile with this name already exists.
try {
- // Check if a profile with this name already exists.
+ mProfileDir = findProfileDir();
+ Log.d(LOGTAG, "Found profile dir.");
+
+ return mProfileDir;
+ } catch (NoSuchProfileException noSuchProfile) {
+ // If it doesn't exist, create it.
try {
- mProfileDir = findProfileDir();
- Log.d(LOGTAG, "Found profile dir.");
- } catch (NoSuchProfileException noSuchProfile) {
- // If it doesn't exist, create it.
mProfileDir = createProfileDir();
+ return mProfileDir;
+ } catch (IOException e) {
+ throw new IllegalStateException("Error creating profile dir, cannot continue", e);
}
- } catch (IOException ioe) {
- Log.e(LOGTAG, "Error getting profile dir", ioe);
}
}
- public File getFile(String aFile) {
- File f = getDir();
- if (f == null)
- return null;
+ public File getFile(final String aFile) {
+ final File profileDir = getDir();
- return new File(f, aFile);
+ return new File(profileDir, aFile);
}
/**
* Retrieves the Gecko client ID from the filesystem. If the client ID does not exist, we attempt to migrate and
* persist it from FHR and, if that fails, we attempt to create a new one ourselves.
*
* This method assumes the client ID is located in a file at a hard-coded path within the profile. The format of
* this file is a JSONObject which at the bottom level contains a String -> String mapping containing the client ID.
@@ -880,27 +881,32 @@ public final class GeckoProfile {
sDefaultProfileName = DEFAULT_PROFILE;
return DEFAULT_PROFILE;
}
sDefaultProfileName = profileName;
return sDefaultProfileName;
}
- private File findProfileDir() throws NoSuchProfileException {
+ private @NonNull File findProfileDir() throws NoSuchProfileException {
if (isCustomProfile()) {
+ if (mProfileDir == null) {
+ throw new IllegalStateException("mProfileDir must be set if isCustomProfile()");
+ }
return mProfileDir;
}
return GeckoProfileDirectories.findProfileDir(mMozillaDir, mName);
}
@WorkerThread
- private File createProfileDir() throws IOException {
+ private @NonNull File createProfileDir() throws IOException {
if (isCustomProfile()) {
- // Custom profiles must already exist.
+ if (mProfileDir == null) {
+ throw new IllegalStateException("mProfileDir must be set if isCustomProfile()");
+ }
return mProfileDir;
}
INIParser parser = GeckoProfileDirectories.getProfilesINI(mMozillaDir);
// Salt the name of our requested profile
String saltedName;
File profileDir;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
@@ -14,16 +14,18 @@ import org.mozilla.gecko.util.ThreadUtil
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
@@ -325,23 +327,36 @@ public class GeckoThread extends Thread
Log.w(LOGTAG, "STARTUP PERFORMANCE WARNING: un-official build: purging the " +
"startup (JavaScript) caches.");
args.add("-purgecaches");
}
return args.toArray(new String[args.size()]);
}
- public static GeckoProfile getActiveProfile() {
+ public static @Nullable GeckoProfile getActiveProfileIfExists() {
+ return INSTANCE.getProfileIfExists();
+ }
+
+ public static @NonNull GeckoProfile getActiveProfile() {
return INSTANCE.getProfile();
}
- public synchronized GeckoProfile getProfile() {
+ public synchronized @Nullable GeckoProfile getProfileIfExists() {
+ if (mProfile != null) {
+ return mProfile;
+ } else {
+ return null;
+ }
+ }
+
+
+ public synchronized @NonNull GeckoProfile getProfile() {
if (!mInitialized) {
- return null;
+ throw new IllegalStateException("Can't retrieve GeckoProfile without prior call to init()");
}
if (isChildProcess()) {
throw new UnsupportedOperationException(
"Cannot access profile from child process");
}
if (mProfile == null) {
final Context context = GeckoAppShell.getApplicationContext();
mProfile = GeckoProfile.initFromArgs(context, mExtraArgs);
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java
@@ -119,17 +119,16 @@ public abstract class AbstractCommunicat
String logMsg;
try {
if (isAlreadyZipped != ZippedState.eAlreadyZipped) {
data = zipData(data);
}
mHttpURLConnection.setRequestProperty("Content-Encoding","gzip");
} catch (IOException e) {
Log.e(LOG_TAG, "Couldn't compress and send data, falling back to plain-text: ", e);
- close();
}
try {
sendData(data);
} finally {
close();
}
sBytesSentTotal += data.length;