Bug 1105316 - Look in Gecko chrome registry for fallback locale in search activity. r=rnewman, a=lsblakk
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Tue, 09 Dec 2014 12:45:13 -0800
changeset 234330 1c97f07f4f7df068f24b2e38dde705e6e3db5536
parent 234329 166f199a7fb6d150c538192a1169143332dd5515
child 234331 d3a7fa02da1ed74d48ec374466177d30f2ba7219
push id4268
push userryanvm@gmail.com
push dateMon, 22 Dec 2014 22:22:50 +0000
treeherdermozilla-beta@e83d78f377b6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman, lsblakk
bugs1105316
milestone35.0
Bug 1105316 - Look in Gecko chrome registry for fallback locale in search activity. r=rnewman, a=lsblakk
mobile/android/base/util/GeckoJarReader.java
mobile/android/search/java/org/mozilla/search/providers/SearchEngineManager.java
--- a/mobile/android/base/util/GeckoJarReader.java
+++ b/mobile/android/base/util/GeckoJarReader.java
@@ -109,16 +109,17 @@ public final class GeckoJarReader {
         Stack<String> jarUrls = parseUrl(url);
         try {
             NativeZip zip = getZipFile(jarUrls.pop());
             return getStream(zip, jarUrls, url);
         } catch (Exception ex) {
             // Some JNI code throws IllegalArgumentException on a bad file name;
             // swallow the error and return null.  We could also see legitimate
             // IOExceptions here.
+            Log.e(LOGTAG, "Exception getting input stream from jar URL: " + url, ex);
             return null;
         }
     }
 
     private static InputStream getStream(NativeZip zip, Stack<String> jarUrls, String origUrl) {
         InputStream inputStream = null;
 
         // loop through children jar files until we reach the innermost one
--- a/mobile/android/search/java/org/mozilla/search/providers/SearchEngineManager.java
+++ b/mobile/android/search/java/org/mozilla/search/providers/SearchEngineManager.java
@@ -26,16 +26,17 @@ import org.xmlpull.v1.XmlPullParserExcep
 
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
 public class SearchEngineManager implements SharedPreferences.OnSharedPreferenceChangeListener {
     private static final String LOG_TAG = "GeckoSearchEngineManager";
 
     // Gecko pref that defines the name of the default search engine.
@@ -44,16 +45,20 @@ public class SearchEngineManager impleme
     // Key for shared preference that stores default engine name.
     private static final String PREF_DEFAULT_ENGINE_KEY = "search.engines.defaultname";
 
     private Context context;
     private Distribution distribution;
     private SearchEngineCallback changeCallback;
     private SearchEngine engine;
 
+    // Cached version of default locale included in Gecko chrome manifest.
+    // This should only be accessed from the background thread.
+    private String fallbackLocale;
+
     public static interface SearchEngineCallback {
         public void execute(SearchEngine engine);
     }
 
     public SearchEngineManager(Context context, Distribution distribution) {
         this.context = context;
         this.distribution = distribution;
         GeckoSharedPrefs.forApp(context).registerOnSharedPreferenceChangeListener(this);
@@ -304,41 +309,32 @@ public class SearchEngineManager impleme
      * with the right name. Unfortunately, we need to do this because there is no
      * other way to map the search engine "name" to the file for the search plugin.
      *
      * @param name Search engine name.
      * @return SearchEngine instance for name.
      */
     private SearchEngine createEngineFromLocale(String name) {
         final InputStream in = getInputStreamFromSearchPluginsJar("list.txt");
-        InputStreamReader isr = null;
+        final BufferedReader br = getBufferedReader(in);
 
         try {
-            isr = new InputStreamReader(in);
-            BufferedReader br = new BufferedReader(isr);
             String identifier;
             while ((identifier = br.readLine()) != null) {
                 final InputStream pluginIn = getInputStreamFromSearchPluginsJar(identifier + ".xml");
                 final SearchEngine engine = createEngineFromInputStream(identifier, pluginIn);
                 if (engine != null && engine.getName().equals(name)) {
                     return engine;
                 }
             }
         } catch (IOException e) {
             Log.e(LOG_TAG, "Error creating shipped search engine from name: " + name, e);
         } finally {
-            if (isr != null) {
-                try {
-                    isr.close();
-                } catch (IOException e) {
-                    // Ignore.
-                }
-            }
             try {
-                in.close();
+                br.close();
             } catch (IOException e) {
                 // Ignore.
             }
         }
         return null;
     }
 
     /**
@@ -436,25 +432,74 @@ public class SearchEngineManager impleme
         if (!languageTag.equals(language)) {
             url = getSearchPluginsJarURL(language, fileName);
             in = GeckoJarReader.getStream(url);
             if (in != null) {
                 return in;
             }
         }
 
-        // Finally, fall back to en-US.
-        url = getSearchPluginsJarURL("en-US", fileName);
+        // Finally, fall back to default locale defined in chrome registry.
+        url = getSearchPluginsJarURL(getFallbackLocale(), fileName);
         return GeckoJarReader.getStream(url);
     }
 
     /**
+     * Finds a fallback locale in the Gecko chrome registry. If a locale is declared
+     * here, we should be guaranteed to find a searchplugins directory for it.
+     *
+     * This method should only be accessed from the background thread.
+     */
+    private String getFallbackLocale() {
+        if (fallbackLocale != null) {
+            return fallbackLocale;
+        }
+
+        final InputStream in = GeckoJarReader.getStream(getJarURL("!/chrome/chrome.manifest"));
+        final BufferedReader br = getBufferedReader(in);
+
+        try {
+            String line;
+            while ((line = br.readLine()) != null) {
+                // We're looking for a line like "locale global en-US en-US/locale/en-US/global/"
+                // https://developer.mozilla.org/en/docs/Chrome_Registration#locale
+                if (line.startsWith("locale global ")) {
+                    fallbackLocale = line.split(" ", 4)[2];
+                    break;
+                }
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Error reading fallback locale from chrome registry", e);
+        } finally {
+            try {
+                br.close();
+            } catch (IOException e) {
+                // Ignore.
+            }
+        }
+        return fallbackLocale;
+    }
+
+    /**
      * Gets the jar URL for a file in the searchplugins directory.
      *
      * @param locale String representing the Gecko locale (e.g. "en-US").
      * @param fileName The name of the file to read.
      * @return URL for jar file.
      */
     private String getSearchPluginsJarURL(String locale, String fileName) {
         final String path = "!/chrome/" + locale + "/locale/" + locale + "/browser/searchplugins/" + fileName;
+        return getJarURL(path);
+    }
+
+    private String getJarURL(String path) {
         return "jar:jar:file://" + context.getPackageResourcePath() + "!/" + AppConstants.OMNIJAR_NAME + path;
     }
+
+    private BufferedReader getBufferedReader(InputStream in) {
+        try {
+            return new BufferedReader(new InputStreamReader(in, "UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            // Cannot happen.
+            return null;
+        }
+    }
 }