mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge fx-team to mozilla-central
This commit is contained in:
commit
1c6c687f00
@ -558,10 +558,14 @@ CustomizeMode.prototype = {
|
||||
if (aPlace != "toolbar") {
|
||||
wrapper.setAttribute("context", contextMenuForPlace);
|
||||
}
|
||||
if (currentContextMenu) {
|
||||
// Only keep track of the menu if it is non-default.
|
||||
if (currentContextMenu &&
|
||||
currentContextMenu != contextMenuForPlace) {
|
||||
aNode.setAttribute("wrapped-context", currentContextMenu);
|
||||
aNode.setAttribute("wrapped-contextAttrName", contextMenuAttrName)
|
||||
aNode.removeAttribute(contextMenuAttrName);
|
||||
} else if (currentContextMenu == contextMenuForPlace) {
|
||||
aNode.removeAttribute(contextMenuAttrName);
|
||||
}
|
||||
|
||||
wrapper.addEventListener("mousedown", this);
|
||||
|
@ -241,6 +241,51 @@ let gTests = [
|
||||
this.otherWin = null;
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "Bug 945191 - Combined buttons show wrong context menu options when they are in the toolbar.",
|
||||
setup: startCustomizing,
|
||||
run: function () {
|
||||
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
|
||||
let shownPromise = contextMenuShown(contextMenu);
|
||||
let zoomControls = document.getElementById("wrapper-zoom-controls");
|
||||
EventUtils.synthesizeMouse(zoomControls, 2, 2, {type: "contextmenu", button: 2});
|
||||
yield shownPromise;
|
||||
// Execute the command to move the item from the panel to the toolbar.
|
||||
contextMenu.childNodes[0].doCommand();
|
||||
let hiddenPromise = contextMenuHidden(contextMenu);
|
||||
contextMenu.hidePopup();
|
||||
yield hiddenPromise;
|
||||
yield endCustomizing();
|
||||
|
||||
zoomControls = document.getElementById("zoom-controls");
|
||||
is(zoomControls.parentNode.id, "nav-bar-customization-target", "Zoom-controls should be on the nav-bar");
|
||||
|
||||
contextMenu = document.getElementById("toolbar-context-menu");
|
||||
shownPromise = contextMenuShown(contextMenu);
|
||||
EventUtils.synthesizeMouse(zoomControls, 2, 2, {type: "contextmenu", button: 2});
|
||||
yield shownPromise;
|
||||
|
||||
let expectedEntries = [
|
||||
[".customize-context-addToPanel", true],
|
||||
[".customize-context-removeFromToolbar", true],
|
||||
["---"]
|
||||
];
|
||||
if (!isOSX) {
|
||||
expectedEntries.push(["#toggle_toolbar-menubar", true]);
|
||||
}
|
||||
expectedEntries.push(
|
||||
["#toggle_PersonalToolbar", true],
|
||||
["---"],
|
||||
[".viewCustomizeToolbar", true]
|
||||
);
|
||||
checkContextMenu(contextMenu, expectedEntries);
|
||||
|
||||
hiddenPromise = contextMenuHidden(contextMenu);
|
||||
contextMenu.hidePopup();
|
||||
yield hiddenPromise;
|
||||
},
|
||||
teardown: resetCustomization,
|
||||
}
|
||||
];
|
||||
|
||||
function test() {
|
||||
|
@ -124,7 +124,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
private static final int GECKO_TOOLS_MENU = -1;
|
||||
private static final int ADDON_MENU_OFFSET = 1000;
|
||||
private class MenuItemInfo {
|
||||
private static class MenuItemInfo {
|
||||
public int id;
|
||||
public String label;
|
||||
public String icon;
|
||||
@ -133,6 +133,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
public boolean enabled = true;
|
||||
public boolean visible = true;
|
||||
public int parent;
|
||||
public boolean added = false; // So we can re-add after a locale change.
|
||||
}
|
||||
|
||||
// The types of guest mdoe dialogs we show
|
||||
@ -1639,6 +1640,20 @@ abstract public class BrowserApp extends GeckoApp
|
||||
showHomePagerWithAnimator(page, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocaleReady(final String locale) {
|
||||
super.onLocaleReady(locale);
|
||||
if (mHomePager != null) {
|
||||
// Blow it away and rebuild it with the right strings.
|
||||
mHomePager.redisplay(getSupportFragmentManager());
|
||||
}
|
||||
|
||||
if (mMenu != null) {
|
||||
mMenu.clear();
|
||||
onCreateOptionsMenu(mMenu);
|
||||
}
|
||||
}
|
||||
|
||||
private void showHomePagerWithAnimator(HomePager.Page page, PropertyAnimator animator) {
|
||||
if (isHomePagerVisible()) {
|
||||
return;
|
||||
@ -1811,7 +1826,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
}
|
||||
}
|
||||
|
||||
private Menu findParentMenu(Menu menu, MenuItem item) {
|
||||
private static Menu findParentMenu(Menu menu, MenuItem item) {
|
||||
final int itemId = item.getItemId();
|
||||
|
||||
final int count = (menu != null) ? menu.size() : 0;
|
||||
@ -1831,54 +1846,58 @@ abstract public class BrowserApp extends GeckoApp
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addAddonMenuItem(final MenuItemInfo info) {
|
||||
if (mMenu == null) {
|
||||
if (mAddonMenuItemsCache == null)
|
||||
mAddonMenuItemsCache = new Vector<MenuItemInfo>();
|
||||
|
||||
mAddonMenuItemsCache.add(info);
|
||||
return;
|
||||
}
|
||||
|
||||
Menu menu;
|
||||
/**
|
||||
* Add the provided item to the provided menu, which should be
|
||||
* the root (mMenu).
|
||||
*/
|
||||
private void addAddonMenuItemToMenu(final Menu menu, final MenuItemInfo info) {
|
||||
info.added = true;
|
||||
|
||||
final Menu destination;
|
||||
if (info.parent == 0) {
|
||||
menu = mMenu;
|
||||
destination = menu;
|
||||
} else if (info.parent == GECKO_TOOLS_MENU) {
|
||||
MenuItem tools = mMenu.findItem(R.id.tools);
|
||||
menu = tools != null ? tools.getSubMenu() : mMenu;
|
||||
MenuItem tools = menu.findItem(R.id.tools);
|
||||
destination = tools != null ? tools.getSubMenu() : menu;
|
||||
} else {
|
||||
MenuItem parent = mMenu.findItem(info.parent);
|
||||
if (parent == null)
|
||||
MenuItem parent = menu.findItem(info.parent);
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Menu parentMenu = findParentMenu(mMenu, parent);
|
||||
Menu parentMenu = findParentMenu(menu, parent);
|
||||
|
||||
if (!parent.hasSubMenu()) {
|
||||
parentMenu.removeItem(parent.getItemId());
|
||||
menu = parentMenu.addSubMenu(Menu.NONE, parent.getItemId(), Menu.NONE, parent.getTitle());
|
||||
if (parent.getIcon() != null)
|
||||
((SubMenu) menu).getItem().setIcon(parent.getIcon());
|
||||
destination = parentMenu.addSubMenu(Menu.NONE, parent.getItemId(), Menu.NONE, parent.getTitle());
|
||||
if (parent.getIcon() != null) {
|
||||
((SubMenu) destination).getItem().setIcon(parent.getIcon());
|
||||
}
|
||||
} else {
|
||||
menu = parent.getSubMenu();
|
||||
destination = parent.getSubMenu();
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem item = menu.add(Menu.NONE, info.id, Menu.NONE, info.label);
|
||||
MenuItem item = destination.add(Menu.NONE, info.id, Menu.NONE, info.label);
|
||||
|
||||
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
Log.i(LOGTAG, "menu item clicked");
|
||||
Log.i(LOGTAG, "Menu item clicked");
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Menu:Clicked", Integer.toString(info.id - ADDON_MENU_OFFSET)));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (info.icon != null) {
|
||||
if (info.icon == null) {
|
||||
item.setIcon(R.drawable.ic_menu_addons_filler);
|
||||
} else {
|
||||
final int id = info.id;
|
||||
BitmapUtils.getDrawable(this, info.icon, new BitmapUtils.BitmapLoader() {
|
||||
@Override
|
||||
public void onBitmapFound(Drawable d) {
|
||||
MenuItem item = mMenu.findItem(id);
|
||||
// TODO: why do we re-find the item?
|
||||
MenuItem item = destination.findItem(id);
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
@ -1889,8 +1908,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||
item.setIcon(d);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
item.setIcon(R.drawable.ic_menu_addons_filler);
|
||||
}
|
||||
|
||||
item.setCheckable(info.checkable);
|
||||
@ -1899,6 +1916,24 @@ abstract public class BrowserApp extends GeckoApp
|
||||
item.setVisible(info.visible);
|
||||
}
|
||||
|
||||
private void addAddonMenuItem(final MenuItemInfo info) {
|
||||
if (mAddonMenuItemsCache == null) {
|
||||
mAddonMenuItemsCache = new Vector<MenuItemInfo>();
|
||||
}
|
||||
|
||||
// Mark it as added if the menu was ready.
|
||||
info.added = (mMenu != null);
|
||||
|
||||
// Always cache so we can rebuild after a locale switch.
|
||||
mAddonMenuItemsCache.add(info);
|
||||
|
||||
if (mMenu == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
addAddonMenuItemToMenu(mMenu, info);
|
||||
}
|
||||
|
||||
private void removeAddonMenuItem(int id) {
|
||||
// Remove add-on menu item from cache, if available.
|
||||
if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
|
||||
@ -1928,13 +1963,15 @@ abstract public class BrowserApp extends GeckoApp
|
||||
item.checked = options.optBoolean("checked", item.checked);
|
||||
item.enabled = options.optBoolean("enabled", item.enabled);
|
||||
item.visible = options.optBoolean("visible", item.visible);
|
||||
item.added = (mMenu != null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mMenu == null)
|
||||
if (mMenu == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MenuItem menuItem = mMenu.findItem(id);
|
||||
if (menuItem != null) {
|
||||
@ -1948,22 +1985,23 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Sets mMenu = menu.
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
// Inform the menu about the action-items bar.
|
||||
if (menu instanceof GeckoMenu && HardwareUtils.isTablet())
|
||||
if (menu instanceof GeckoMenu &&
|
||||
HardwareUtils.isTablet()) {
|
||||
((GeckoMenu) menu).setActionItemBarPresenter(mBrowserToolbar);
|
||||
}
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.browser_app_menu, mMenu);
|
||||
|
||||
// Add add-on menu items if any.
|
||||
// Add add-on menu items, if any exist.
|
||||
if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
|
||||
for (MenuItemInfo item : mAddonMenuItemsCache) {
|
||||
addAddonMenuItem(item);
|
||||
addAddonMenuItemToMenu(mMenu, item);
|
||||
}
|
||||
|
||||
mAddonMenuItemsCache.clear();
|
||||
}
|
||||
|
||||
// Action providers are available only ICS+.
|
||||
|
@ -1,6 +1,15 @@
|
||||
package org.mozilla.gecko;
|
||||
import android.content.Context;
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
interface ContextGetter {
|
||||
Context getContext();
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public interface ContextGetter {
|
||||
Context getContext();
|
||||
SharedPreferences getSharedPreferences();
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,15 @@ public class GeckoActivity extends FragmentActivity implements GeckoActivityStat
|
||||
// has this activity recently started another Gecko activity?
|
||||
private boolean mGeckoActivityOpened = false;
|
||||
|
||||
/**
|
||||
* Display any resources that show strings or encompass locale-specific
|
||||
* representations.
|
||||
*
|
||||
* onLocaleReady must always be called on the UI thread.
|
||||
*/
|
||||
public void onLocaleReady(final String locale) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
@ -126,12 +126,18 @@ import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
abstract public class GeckoApp
|
||||
extends GeckoActivity
|
||||
implements GeckoEventListener, SensorEventListener, LocationListener,
|
||||
Tabs.OnTabsChangedListener, GeckoEventResponder,
|
||||
GeckoMenu.Callback, GeckoMenu.MenuPresenter,
|
||||
ContextGetter, GeckoAppShell.GeckoInterface
|
||||
public abstract class GeckoApp
|
||||
extends GeckoActivity
|
||||
implements
|
||||
ContextGetter,
|
||||
GeckoAppShell.GeckoInterface,
|
||||
GeckoEventListener,
|
||||
GeckoEventResponder,
|
||||
GeckoMenu.Callback,
|
||||
GeckoMenu.MenuPresenter,
|
||||
LocationListener,
|
||||
SensorEventListener,
|
||||
Tabs.OnTabsChangedListener
|
||||
{
|
||||
private static final String LOGTAG = "GeckoApp";
|
||||
|
||||
@ -233,10 +239,20 @@ abstract public class GeckoApp
|
||||
|
||||
void focusChrome() { }
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return sAppContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferences getSharedPreferences() {
|
||||
return GeckoApp.getAppSharedPreferences();
|
||||
}
|
||||
|
||||
public static SharedPreferences getAppSharedPreferences() {
|
||||
return GeckoApp.sAppContext.getSharedPreferences(GeckoApp.PREFS_NAME, 0);
|
||||
}
|
||||
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
@ -258,10 +274,6 @@ abstract public class GeckoApp
|
||||
return this;
|
||||
}
|
||||
|
||||
public static SharedPreferences getAppSharedPreferences() {
|
||||
return GeckoApp.sAppContext.getSharedPreferences(PREFS_NAME, 0);
|
||||
}
|
||||
|
||||
public View getCameraView() {
|
||||
return mCameraView;
|
||||
}
|
||||
@ -703,6 +715,8 @@ abstract public class GeckoApp
|
||||
GeckoAppShell.openUriExternal(message.optString("url"),
|
||||
message.optString("mime"), message.optString("packageName"),
|
||||
message.optString("className"), message.optString("action"), message.optString("title"));
|
||||
} else if (event.equals("Locale:Set")) {
|
||||
setLocale(message.getString("locale"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
|
||||
@ -1179,7 +1193,7 @@ abstract public class GeckoApp
|
||||
}
|
||||
|
||||
BrowserDB.initialize(getProfile().getName());
|
||||
((GeckoApplication)getApplication()).initialize();
|
||||
((GeckoApplication) getApplication()).initialize();
|
||||
|
||||
sAppContext = this;
|
||||
GeckoAppShell.setContextGetter(this);
|
||||
@ -1193,12 +1207,13 @@ abstract public class GeckoApp
|
||||
Log.e(LOGTAG, "Exception starting favicon cache. Corrupt resources?", e);
|
||||
}
|
||||
|
||||
// When we detect a locale change, we need to restart Gecko, which
|
||||
// actually means restarting the entire application. This logic should
|
||||
// actually be handled elsewhere since GeckoApp may not be alive to
|
||||
// handle this event if "Don't keep activities" is enabled (filed as
|
||||
// bug 889082).
|
||||
if (((GeckoApplication)getApplication()).needsRestart()) {
|
||||
// Did the OS locale change while we were backgrounded? If so,
|
||||
// we need to die so that Gecko will re-init add-ons that touch
|
||||
// the UI.
|
||||
// This is using a sledgehammer to crack a nut, but it'll do for
|
||||
// now.
|
||||
if (LocaleManager.systemLocaleDidChange()) {
|
||||
Log.i(LOGTAG, "System locale changed. Restarting.");
|
||||
doRestart();
|
||||
System.exit(0);
|
||||
return;
|
||||
@ -1279,6 +1294,11 @@ abstract public class GeckoApp
|
||||
public void run() {
|
||||
final SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
|
||||
// Wait until now to set this, because we'd rather throw an exception than
|
||||
// have a caller of LocaleManager regress startup.
|
||||
LocaleManager.setContextGetter(GeckoApp.this);
|
||||
LocaleManager.initialize();
|
||||
|
||||
SessionInformation previousSession = SessionInformation.fromSharedPrefs(prefs);
|
||||
if (previousSession.wasKilled()) {
|
||||
Telemetry.HistogramAdd("FENNEC_WAS_KILLED", 1);
|
||||
@ -1299,17 +1319,29 @@ abstract public class GeckoApp
|
||||
final String profilePath = getProfile().getDir().getAbsolutePath();
|
||||
final EventDispatcher dispatcher = GeckoAppShell.getEventDispatcher();
|
||||
Log.i(LOGTAG, "Creating BrowserHealthRecorder.");
|
||||
final String osLocale = Locale.getDefault().toString();
|
||||
Log.d(LOGTAG, "Locale is " + osLocale);
|
||||
|
||||
// Replace the duplicate `osLocale` argument when we support switchable
|
||||
// application locales.
|
||||
final String osLocale = Locale.getDefault().toString();
|
||||
String appLocale = LocaleManager.getAndApplyPersistedLocale();
|
||||
Log.d(LOGTAG, "OS locale is " + osLocale + ", app locale is " + appLocale);
|
||||
|
||||
if (appLocale == null) {
|
||||
appLocale = osLocale;
|
||||
}
|
||||
|
||||
mHealthRecorder = new BrowserHealthRecorder(GeckoApp.this,
|
||||
profilePath,
|
||||
dispatcher,
|
||||
osLocale,
|
||||
osLocale, // Placeholder.
|
||||
appLocale,
|
||||
previousSession);
|
||||
|
||||
final String uiLocale = appLocale;
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GeckoApp.this.onLocaleReady(uiLocale);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -1317,6 +1349,30 @@ abstract public class GeckoApp
|
||||
NotificationHelper.init(getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* At this point, the resource system and the rest of the browser are
|
||||
* aware of the locale.
|
||||
*
|
||||
* Now we can display strings!
|
||||
*/
|
||||
@Override
|
||||
public void onLocaleReady(final String locale) {
|
||||
if (!ThreadUtils.isOnUiThread()) {
|
||||
throw new RuntimeException("onLocaleReady must always be called from the UI thread.");
|
||||
}
|
||||
|
||||
// The URL bar hint needs to be populated.
|
||||
TextView urlBar = (TextView) findViewById(R.id.url_bar_title);
|
||||
if (urlBar == null) {
|
||||
return;
|
||||
}
|
||||
final String hint = getResources().getString(R.string.url_bar_default_text);
|
||||
urlBar.setHint(hint);
|
||||
|
||||
// Allow onConfigurationChanged to take care of the rest.
|
||||
onConfigurationChanged(getResources().getConfiguration());
|
||||
}
|
||||
|
||||
protected void initializeChrome() {
|
||||
mDoorHangerPopup = new DoorHangerPopup(this, null);
|
||||
mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container);
|
||||
@ -1526,6 +1582,7 @@ abstract public class GeckoApp
|
||||
registerEventListener("Contact:Add");
|
||||
registerEventListener("Intent:Open");
|
||||
registerEventListener("Intent:GetHandlers");
|
||||
registerEventListener("Locale:Set");
|
||||
|
||||
if (SmsManager.getInstance() != null) {
|
||||
SmsManager.getInstance().start();
|
||||
@ -1579,23 +1636,6 @@ abstract public class GeckoApp
|
||||
// intervals.
|
||||
GeckoPreferences.broadcastAnnouncementsPref(context);
|
||||
GeckoPreferences.broadcastHealthReportUploadPref(context);
|
||||
|
||||
/*
|
||||
XXXX see Bug 635342.
|
||||
We want to disable this code if possible. It is about 145ms in runtime.
|
||||
|
||||
If this code ever becomes live again, you'll need to chain the
|
||||
new locale into BrowserHealthRecorder correctly. See
|
||||
GeckoAppShell.setSelectedLocale.
|
||||
We pass the OS locale into the BHR constructor: we need to grab
|
||||
that *before* we modify the current locale!
|
||||
|
||||
SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
|
||||
String localeCode = settings.getString(getPackageName() + ".locale", "");
|
||||
if (localeCode != null && localeCode.length() > 0)
|
||||
GeckoAppShell.setSelectedLocale(localeCode);
|
||||
*/
|
||||
|
||||
if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.Launched)) {
|
||||
return;
|
||||
}
|
||||
@ -2140,6 +2180,8 @@ abstract public class GeckoApp
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
Log.d(LOGTAG, "onConfigurationChanged: " + newConfig.locale);
|
||||
LocaleManager.correctLocale(getResources(), newConfig);
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
if (mOrientation != newConfig.orientation) {
|
||||
@ -2725,4 +2767,52 @@ abstract public class GeckoApp
|
||||
}
|
||||
return versionCode;
|
||||
}
|
||||
|
||||
// FHR reason code for a session end prior to a restart for a
|
||||
// locale change.
|
||||
private static final String SESSION_END_LOCALE_CHANGED = "L";
|
||||
|
||||
/**
|
||||
* Use LocaleManager to change our persisted and current locales,
|
||||
* and poke BrowserHealthRecorder to tell it of our changed state.
|
||||
*/
|
||||
private void setLocale(final String locale) {
|
||||
if (locale == null) {
|
||||
return;
|
||||
}
|
||||
final String resultant = LocaleManager.setSelectedLocale(locale);
|
||||
if (resultant == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final BrowserHealthRecorder rec = mHealthRecorder;
|
||||
if (rec == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean startNewSession = true;
|
||||
final boolean shouldRestart = false;
|
||||
rec.onAppLocaleChanged(resultant);
|
||||
rec.onEnvironmentChanged(startNewSession, SESSION_END_LOCALE_CHANGED);
|
||||
|
||||
if (!shouldRestart) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GeckoApp.this.onLocaleReady(resultant);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Do this in the background so that the health recorder has its
|
||||
// time to finish.
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GeckoApp.this.doRestart();
|
||||
GeckoApp.this.finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -229,8 +229,7 @@ public class GeckoAppShell
|
||||
}
|
||||
|
||||
if (e instanceof OutOfMemoryError) {
|
||||
SharedPreferences prefs =
|
||||
getContext().getSharedPreferences(GeckoApp.PREFS_NAME, 0);
|
||||
SharedPreferences prefs = getSharedPreferences();
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean(GeckoApp.PREFS_OOM_EXCEPTION, true);
|
||||
editor.commit();
|
||||
@ -1587,45 +1586,6 @@ public class GeckoAppShell
|
||||
}
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static void setSelectedLocale(String localeCode) {
|
||||
/* Bug 713464: This method is still called from Gecko side.
|
||||
Earlier we had an option to run Firefox in a language other than system's language.
|
||||
However, this is not supported as of now.
|
||||
Gecko resets the locale to en-US by calling this function with an empty string.
|
||||
This affects GeckoPreferences activity in multi-locale builds.
|
||||
|
||||
N.B., if this code ever becomes live again, you need to hook it up to locale
|
||||
recording in BrowserHealthRecorder: we track the current app and OS locales
|
||||
as part of the recorded environment.
|
||||
|
||||
See similar note in GeckoApp.java for the startup path.
|
||||
|
||||
//We're not using this, not need to save it (see bug 635342)
|
||||
SharedPreferences settings =
|
||||
getContext().getPreferences(Activity.MODE_PRIVATE);
|
||||
settings.edit().putString(getContext().getPackageName() + ".locale",
|
||||
localeCode).commit();
|
||||
Locale locale;
|
||||
int index;
|
||||
if ((index = localeCode.indexOf('-')) != -1 ||
|
||||
(index = localeCode.indexOf('_')) != -1) {
|
||||
String langCode = localeCode.substring(0, index);
|
||||
String countryCode = localeCode.substring(index + 1);
|
||||
locale = new Locale(langCode, countryCode);
|
||||
} else {
|
||||
locale = new Locale(localeCode);
|
||||
}
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Resources res = getContext().getBaseContext().getResources();
|
||||
Configuration config = res.getConfiguration();
|
||||
config.locale = locale;
|
||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@WrapElementForJNI(stubName = "GetSystemColoursWrapper")
|
||||
public static int[] getSystemColors() {
|
||||
// attrsAppearance[] must correspond to AndroidSystemColors structure in android/AndroidBridge.h
|
||||
@ -2146,6 +2106,13 @@ public class GeckoAppShell
|
||||
sContextGetter = cg;
|
||||
}
|
||||
|
||||
public static SharedPreferences getSharedPreferences() {
|
||||
if (sContextGetter == null) {
|
||||
throw new IllegalStateException("No ContextGetter; cannot fetch prefs.");
|
||||
}
|
||||
return sContextGetter.getSharedPreferences();
|
||||
}
|
||||
|
||||
public interface AppStateListener {
|
||||
public void onPause();
|
||||
public void onResume();
|
||||
|
@ -12,20 +12,46 @@ import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Configuration;
|
||||
import android.util.Log;
|
||||
|
||||
public class GeckoApplication extends Application {
|
||||
private static final String LOG_TAG = "GeckoApplication";
|
||||
|
||||
private boolean mInited;
|
||||
private boolean mInBackground;
|
||||
private boolean mPausedGecko;
|
||||
private boolean mNeedsRestart;
|
||||
|
||||
private LightweightTheme mLightweightTheme;
|
||||
|
||||
/**
|
||||
* We need to do locale work here, because we need to intercept
|
||||
* each hit to onConfigurationChanged.
|
||||
*/
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration config) {
|
||||
Log.d(LOG_TAG, "onConfigurationChanged: " + config.locale +
|
||||
", background: " + mInBackground);
|
||||
|
||||
// Do nothing if we're in the background. It'll simply cause a loop
|
||||
// (Bug 936756 Comment 11), and it's not necessary.
|
||||
if (mInBackground) {
|
||||
super.onConfigurationChanged(config);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, correct the locale. This catches some cases that GeckoApp
|
||||
// doesn't get a chance to.
|
||||
try {
|
||||
LocaleManager.correctLocale(getResources(), config);
|
||||
} catch (IllegalStateException ex) {
|
||||
// GeckoApp hasn't started, so we have no ContextGetter in LocaleManager.
|
||||
Log.w(LOG_TAG, "Couldn't correct locale.", ex);
|
||||
}
|
||||
|
||||
super.onConfigurationChanged(config);
|
||||
}
|
||||
|
||||
protected void initialize() {
|
||||
if (mInited)
|
||||
return;
|
||||
@ -43,14 +69,6 @@ public class GeckoApplication extends Application {
|
||||
GeckoNetworkManager.getInstance().init(getApplicationContext());
|
||||
MemoryMonitor.getInstance().init(getApplicationContext());
|
||||
|
||||
BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
mNeedsRestart = true;
|
||||
}
|
||||
};
|
||||
registerReceiver(receiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
|
||||
|
||||
mInited = true;
|
||||
}
|
||||
|
||||
@ -90,10 +108,6 @@ public class GeckoApplication extends Application {
|
||||
mInBackground = false;
|
||||
}
|
||||
|
||||
protected boolean needsRestart() {
|
||||
return mNeedsRestart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
HardwareUtils.init(getApplicationContext());
|
||||
|
@ -19,6 +19,7 @@ import org.json.JSONObject;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
@ -30,6 +31,7 @@ import java.util.List;
|
||||
public class GeckoView extends LayerView
|
||||
implements GeckoEventListener, ContextGetter {
|
||||
|
||||
private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView";
|
||||
private static final String LOGTAG = "GeckoView";
|
||||
|
||||
private ChromeDelegate mChromeDelegate;
|
||||
@ -305,6 +307,14 @@ public class GeckoView extends LayerView
|
||||
return GeckoAppShell.getGeckoInterface();
|
||||
}
|
||||
|
||||
protected String getSharedPreferencesFile() {
|
||||
return DEFAULT_SHARED_PREFERENCES_FILE;
|
||||
}
|
||||
|
||||
public SharedPreferences getSharedPreferences() {
|
||||
return getContext().getSharedPreferences(getSharedPreferencesFile(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for a browser in the GeckoView container. Associated with a browser
|
||||
* element in the Gecko system.
|
||||
|
216
mobile/android/base/LocaleManager.java
Normal file
216
mobile/android/base/LocaleManager.java
Normal file
@ -0,0 +1,216 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* 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;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This class manages persistence, application, and otherwise handling of
|
||||
* user-specified locales.
|
||||
*
|
||||
* Of note:
|
||||
*
|
||||
* * It's a singleton, because its scope extends to that of the application,
|
||||
* and definitionally all changes to the locale of the app must go through
|
||||
* this.
|
||||
* * It's lazy.
|
||||
* * It has ties into the Gecko event system, because it has to tell Gecko when
|
||||
* to switch locale.
|
||||
* * It relies on using the SharedPreferences file owned by the browser (in
|
||||
* Fennec's case, "GeckoApp") for performance.
|
||||
*/
|
||||
public class LocaleManager {
|
||||
private static final String LOG_TAG = "GeckoLocales";
|
||||
|
||||
// These are both volatile because we don't impose restrictions
|
||||
// over which thread calls our methods.
|
||||
private static volatile ContextGetter getter = null;
|
||||
private static volatile Locale currentLocale = null;
|
||||
|
||||
private static volatile boolean inited = false;
|
||||
private static boolean systemLocaleDidChange = false;
|
||||
private static BroadcastReceiver receiver;
|
||||
|
||||
public static void setContextGetter(ContextGetter getter) {
|
||||
Log.d(LOG_TAG, "Calling setContextGetter: " + getter);
|
||||
LocaleManager.getter = getter;
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
if (inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
systemLocaleDidChange = true;
|
||||
}
|
||||
};
|
||||
getContext().registerReceiver(receiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
|
||||
inited = true;
|
||||
}
|
||||
|
||||
public static boolean systemLocaleDidChange() {
|
||||
return systemLocaleDidChange;
|
||||
}
|
||||
|
||||
private static Context getContext() {
|
||||
if (getter == null) {
|
||||
throw new IllegalStateException("No ContextGetter; cannot fetch context.");
|
||||
}
|
||||
return getter.getContext();
|
||||
}
|
||||
|
||||
private static SharedPreferences getSharedPreferences() {
|
||||
if (getter == null) {
|
||||
throw new IllegalStateException("No ContextGetter; cannot fetch prefs.", new RuntimeException("No prefs."));
|
||||
}
|
||||
return getter.getSharedPreferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Every time the system gives us a new configuration, it
|
||||
* carries the external locale. Fix it.
|
||||
*/
|
||||
public static void correctLocale(Resources res, Configuration config) {
|
||||
Locale current = getCurrentLocale();
|
||||
if (current == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// I know it's tempting to short-circuit here if the config seems to be
|
||||
// up-to-date, but the rest is necessary.
|
||||
|
||||
config.locale = current;
|
||||
|
||||
// The following two lines are heavily commented in case someone
|
||||
// decides to chase down performance improvements and decides to
|
||||
// question what's going on here.
|
||||
// Both lines should be cheap, *but*...
|
||||
|
||||
// This is unnecessary for basic string choice, but it almost
|
||||
// certainly comes into play when rendering numbers, deciding on RTL,
|
||||
// etc. Take it out if you can prove that's not the case.
|
||||
Locale.setDefault(current);
|
||||
|
||||
// This seems to be a no-op, but every piece of documentation under the
|
||||
// sun suggests that it's necessary, and it certainly makes sense.
|
||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||
}
|
||||
|
||||
private static Locale parseLocaleCode(final String localeCode) {
|
||||
int index;
|
||||
if ((index = localeCode.indexOf('-')) != -1 ||
|
||||
(index = localeCode.indexOf('_')) != -1) {
|
||||
final String langCode = localeCode.substring(0, index);
|
||||
final String countryCode = localeCode.substring(index + 1);
|
||||
return new Locale(langCode, countryCode);
|
||||
} else {
|
||||
return new Locale(localeCode);
|
||||
}
|
||||
}
|
||||
|
||||
public static Locale getCurrentLocale() {
|
||||
if (currentLocale != null) {
|
||||
return currentLocale;
|
||||
}
|
||||
|
||||
final String current = getPersistedLocale();
|
||||
if (current == null) {
|
||||
return null;
|
||||
}
|
||||
return currentLocale = parseLocaleCode(current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the persisted locale if it differed from the current.
|
||||
*/
|
||||
public static String updateLocale(String localeCode) {
|
||||
// Fast path.
|
||||
final Locale defaultLocale = Locale.getDefault();
|
||||
if (defaultLocale.toString().equals(localeCode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Locale locale = parseLocaleCode(localeCode);
|
||||
|
||||
// Fast path.
|
||||
if (defaultLocale.equals(locale)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Locale.setDefault(locale);
|
||||
currentLocale = locale;
|
||||
|
||||
// Update resources.
|
||||
Resources res = getContext().getResources();
|
||||
Configuration config = res.getConfiguration();
|
||||
config.locale = locale;
|
||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||
|
||||
// Tell Gecko.
|
||||
GeckoEvent ev = GeckoEvent.createBroadcastEvent("Locale:Changed", locale.toString());
|
||||
GeckoAppShell.sendEventToGecko(ev);
|
||||
|
||||
return locale.toString();
|
||||
}
|
||||
|
||||
private static String getPrefName() {
|
||||
return getContext().getPackageName() + ".locale";
|
||||
}
|
||||
|
||||
public static String getPersistedLocale() {
|
||||
final SharedPreferences settings = getSharedPreferences();
|
||||
|
||||
// N.B., it is expected that any per-profile settings will be
|
||||
// implemented via SharedPreferences multiplexing in ContextGetter, not
|
||||
// via profile-annotated preference names.
|
||||
final String locale = settings.getString(getPrefName(), "");
|
||||
|
||||
if ("".equals(locale)) {
|
||||
return null;
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
private static void persistLocale(String localeCode) {
|
||||
final SharedPreferences settings = getSharedPreferences();
|
||||
settings.edit().putString(getPrefName(), localeCode).commit();
|
||||
}
|
||||
|
||||
public static String getAndApplyPersistedLocale() {
|
||||
final long t1 = android.os.SystemClock.uptimeMillis();
|
||||
final String localeCode = getPersistedLocale();
|
||||
if (localeCode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
updateLocale(localeCode);
|
||||
final long t2 = android.os.SystemClock.uptimeMillis();
|
||||
Log.i(LOG_TAG, "Locale read and update took: " + (t2 - t1) + "ms.");
|
||||
return localeCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set locale if it changed. Always persists.
|
||||
*/
|
||||
public static String setSelectedLocale(String localeCode) {
|
||||
final String resultant = updateLocale(localeCode);
|
||||
persistLocale(localeCode);
|
||||
return resultant;
|
||||
}
|
||||
}
|
||||
|
@ -319,6 +319,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
||||
}
|
||||
|
||||
public void onAppLocaleChanged(String to) {
|
||||
Log.d(LOG_TAG, "Setting health recorder app locale to " + to);
|
||||
this.profileCache.beginInitialization();
|
||||
this.profileCache.setAppLocale(to);
|
||||
}
|
||||
@ -349,10 +350,19 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
||||
* Invoke this method after calls that mutate the environment.
|
||||
*
|
||||
* If this change resulted in a transition between two environments, {@link
|
||||
* #onEnvironmentTransition(int, int)} will be invoked on the background
|
||||
* #onEnvironmentTransition(int, int, boolean, String)} will be invoked on the background
|
||||
* thread.
|
||||
*/
|
||||
public synchronized void onEnvironmentChanged() {
|
||||
onEnvironmentChanged(true, "E");
|
||||
}
|
||||
|
||||
/**
|
||||
* If `startNewSession` is false, it means no new session should begin
|
||||
* (e.g., because we're about to restart, and we don't want to create
|
||||
* an orphan).
|
||||
*/
|
||||
public synchronized void onEnvironmentChanged(final boolean startNewSession, final String sessionEndReason) {
|
||||
final int previousEnv = this.env;
|
||||
this.env = -1;
|
||||
try {
|
||||
@ -374,7 +384,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
onEnvironmentTransition(previousEnv, updatedEnv);
|
||||
onEnvironmentTransition(previousEnv, updatedEnv, startNewSession, sessionEndReason);
|
||||
} catch (Exception e) {
|
||||
Log.w(LOG_TAG, "Could not record environment transition.", e);
|
||||
}
|
||||
@ -643,7 +653,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
||||
* Invoked in the background whenever the environment transitions between
|
||||
* two valid values.
|
||||
*/
|
||||
protected void onEnvironmentTransition(int prev, int env) {
|
||||
protected void onEnvironmentTransition(int prev, int env, boolean startNewSession, String sessionEndReason) {
|
||||
if (this.state != State.INITIALIZED) {
|
||||
Log.d(LOG_TAG, "Not initialized: not recording env transition (" + prev + " => " + env + ").");
|
||||
return;
|
||||
@ -652,7 +662,12 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
||||
final SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
recordSessionEnd("E", editor, prev);
|
||||
recordSessionEnd(sessionEndReason, editor, prev);
|
||||
|
||||
if (!startNewSession) {
|
||||
editor.commit();
|
||||
return;
|
||||
}
|
||||
|
||||
final SessionInformation newSession = SessionInformation.forRuntimeTransition();
|
||||
setCurrentSession(newSession);
|
||||
|
@ -129,6 +129,11 @@ public class HomePager extends ViewPager {
|
||||
super.addView(child, index, params);
|
||||
}
|
||||
|
||||
public void redisplay(FragmentManager fm) {
|
||||
final TabsAdapter adapter = (TabsAdapter) getAdapter();
|
||||
show(fm, adapter.getCurrentPage(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and initializes the pager.
|
||||
*
|
||||
@ -281,6 +286,12 @@ public class HomePager extends ViewPager {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Page getCurrentPage() {
|
||||
int currentItem = getCurrentItem();
|
||||
TabInfo info = mTabs.get(currentItem);
|
||||
return info.page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mTabs.size();
|
||||
|
@ -95,7 +95,7 @@ public class TabMenuStrip extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
// Page scroll animates the drawable and it's bounds from the previous to next child view.
|
||||
// Page scroll animates the drawable and its bounds from the previous to next child view.
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
if (mStrip == null) {
|
||||
|
@ -218,15 +218,15 @@ public class TwoLinePageRow extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
// Use the URL instead of an empty title for consistency with the normal URL
|
||||
// bar view - this is the equivalent of getDisplayTitle() in Tab.java
|
||||
setTitle(TextUtils.isEmpty(title) ? url : title);
|
||||
|
||||
// No point updating the below things if URL has not changed. Prevents evil Favicon flicker.
|
||||
if (url.equals(mPageUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the URL instead of an empty title for consistency with the normal URL
|
||||
// bar view - this is the equivalent of getDisplayTitle() in Tab.java
|
||||
setTitle(TextUtils.isEmpty(title) ? url : title);
|
||||
|
||||
// Blank the Favicon, so we don't show the wrong Favicon if we scroll and miss DB.
|
||||
mFavicon.clearImage();
|
||||
mLoadFaviconJobId = Favicons.getSizedFaviconForPageFromLocal(url, mFaviconListener);
|
||||
|
@ -10,6 +10,7 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.ActionProvider;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
@ -114,7 +115,7 @@ public class GeckoMenu extends ListView
|
||||
mItems = new ArrayList<GeckoMenuItem>();
|
||||
mActionItems = new HashMap<GeckoMenuItem, View>();
|
||||
|
||||
mActionItemBarPresenter = (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_action_bar, null);
|
||||
mActionItemBarPresenter = (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_action_bar, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -219,14 +220,26 @@ public class GeckoMenu extends ListView
|
||||
public void clear() {
|
||||
for (GeckoMenuItem menuItem : mItems) {
|
||||
if (menuItem.hasSubMenu()) {
|
||||
menuItem.getSubMenu().clear();
|
||||
SubMenu sub = menuItem.getSubMenu();
|
||||
if (sub == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
sub.clear();
|
||||
} catch (Exception ex) {
|
||||
Log.e(LOGTAG, "Couldn't clear submenu.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mAdapter.clear();
|
||||
|
||||
mItems.clear();
|
||||
|
||||
/*
|
||||
* Reinflating the menu will re-add any action items to the toolbar, so
|
||||
* remove the old ones. This also ensures that any text associated with
|
||||
* these is switched to the correct locale.
|
||||
*/
|
||||
if (mActionItemBarPresenter != null) {
|
||||
for (View item : mActionItems.values()) {
|
||||
mActionItemBarPresenter.removeActionItem(item);
|
||||
|
@ -142,7 +142,11 @@ public class GeckoMenuItem implements MenuItem {
|
||||
|
||||
@Override
|
||||
public SubMenu getSubMenu() {
|
||||
return mSubMenu;
|
||||
// For consistency with hasSubMenu.
|
||||
if (mActionProvider == null) {
|
||||
return mSubMenu;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -232,6 +232,7 @@ gbjar.sources += [
|
||||
'JavaAddonManager.java',
|
||||
'LightweightTheme.java',
|
||||
'LightweightThemeDrawable.java',
|
||||
'LocaleManager.java',
|
||||
'MemoryMonitor.java',
|
||||
'menu/GeckoMenu.java',
|
||||
'menu/GeckoMenuInflater.java',
|
||||
|
@ -104,7 +104,6 @@
|
||||
android:textColor="@color/url_bar_title"
|
||||
android:textColorHint="@color/url_bar_title_hint"
|
||||
android:gravity="center_vertical|left"
|
||||
android:hint="@string/url_bar_default_text"
|
||||
android:layout_gravity="center_vertical"
|
||||
gecko:autoUpdateTheme="false"/>
|
||||
|
||||
|
@ -12,22 +12,29 @@ public class testAboutPage extends PixelTest {
|
||||
return TEST_MOCHITEST;
|
||||
}
|
||||
|
||||
private void ensureTitleMatches(final String regex) {
|
||||
Element urlBarTitle = mDriver.findElement(getActivity(), URL_BAR_TITLE_ID);
|
||||
mAsserter.isnot(urlBarTitle, null, "Got the URL bar title");
|
||||
assertMatches(urlBarTitle.getText(), regex, "page title match");
|
||||
}
|
||||
|
||||
public void testAboutPage() {
|
||||
blockForGeckoReady();
|
||||
|
||||
// Load the about: page and verify its title
|
||||
// Load the about: page and verify its title.
|
||||
String url = "about:";
|
||||
loadAndPaint(url);
|
||||
|
||||
Element urlBarTitle = mDriver.findElement(getActivity(), URL_BAR_TITLE_ID);
|
||||
mAsserter.isnot(urlBarTitle, null, "Got the URL bar title");
|
||||
assertMatches(urlBarTitle.getText(), "About (Fennec|Nightly|Aurora|Firefox|Firefox Beta)", "page title match");
|
||||
ensureTitleMatches("About (Fennec|Nightly|Aurora|Firefox|Firefox Beta)");
|
||||
|
||||
// Open a new page to remove the about: page from the current tab
|
||||
// Open a new page to remove the about: page from the current tab.
|
||||
url = getAbsoluteUrl("/robocop/robocop_blank_01.html");
|
||||
inputAndLoadUrl(url);
|
||||
|
||||
// Set up listeners to catch the page load we're about to do
|
||||
// At this point the page title should have been set.
|
||||
ensureTitleMatches("Browser Blank Page 01");
|
||||
|
||||
// Set up listeners to catch the page load we're about to do.
|
||||
Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
|
||||
Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
|
||||
|
||||
@ -40,9 +47,7 @@ public class testAboutPage extends PixelTest {
|
||||
tabEventExpecter.unregisterListener();
|
||||
contentEventExpecter.unregisterListener();
|
||||
|
||||
// Grab the title to make sure the about: page was loaded
|
||||
urlBarTitle = mDriver.findElement(getActivity(), URL_BAR_TITLE_ID);
|
||||
mAsserter.isnot(urlBarTitle, null, "Got the URL bar title");
|
||||
assertMatches(urlBarTitle.getText(), "About (Fennec|Nightly|Aurora|Firefox|Firefox Beta)", "page title match");
|
||||
// Grab the title to make sure the about: page was loaded.
|
||||
ensureTitleMatches("About (Fennec|Nightly|Aurora|Firefox|Firefox Beta)");
|
||||
}
|
||||
}
|
||||
|
@ -353,7 +353,8 @@ var SelectionHandler = {
|
||||
SelectionHandler.copySelection();
|
||||
aElement.value = aElement.value.substring(0, start) + aElement.value.substring(end)
|
||||
|
||||
SelectionHandler._updateMenu();
|
||||
// copySelection closes the selection. Show a caret where we just cut the text.
|
||||
SelectionHandler.attachCaret(aElement);
|
||||
},
|
||||
selector: ClipboardHelper.cutContext,
|
||||
},
|
||||
@ -364,7 +365,6 @@ var SelectionHandler = {
|
||||
icon: "drawable://copy",
|
||||
action: function() {
|
||||
SelectionHandler.copySelection();
|
||||
SelectionHandler._updateMenu();
|
||||
},
|
||||
selector: ClipboardHelper.getCopyContext(false)
|
||||
},
|
||||
@ -387,7 +387,6 @@ var SelectionHandler = {
|
||||
icon: "drawable://ic_menu_share",
|
||||
action: function() {
|
||||
SelectionHandler.shareSelection();
|
||||
SelectionHandler._closeSelection();
|
||||
},
|
||||
showAsAction: function(aElement) {
|
||||
return !(aElement instanceof HTMLInputElement && aElement.mozIsTextField(false))
|
||||
|
@ -274,6 +274,7 @@ var BrowserApp = {
|
||||
|
||||
Services.androidBridge.browserApp = this;
|
||||
|
||||
Services.obs.addObserver(this, "Locale:Changed", false);
|
||||
Services.obs.addObserver(this, "Tab:Load", false);
|
||||
Services.obs.addObserver(this, "Tab:Selected", false);
|
||||
Services.obs.addObserver(this, "Tab:Closed", false);
|
||||
@ -410,6 +411,14 @@ var BrowserApp = {
|
||||
return "";
|
||||
},
|
||||
|
||||
/**
|
||||
* Pass this a locale string, such as "fr" or "es_ES".
|
||||
*/
|
||||
setLocale: function (locale) {
|
||||
console.log("browser.js: requesting locale set: " + locale);
|
||||
sendMessageToJava({ type: "Locale:Set", locale: locale });
|
||||
},
|
||||
|
||||
initContextMenu: function ba_initContextMenu() {
|
||||
// TODO: These should eventually move into more appropriate classes
|
||||
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.openInNewTab"),
|
||||
@ -1496,6 +1505,13 @@ var BrowserApp = {
|
||||
this.notifyPrefObservers(aData);
|
||||
break;
|
||||
|
||||
case "Locale:Changed":
|
||||
// TODO: do we need to be more nuanced here -- e.g., checking for the
|
||||
// OS locale -- or should it always be false on Fennec?
|
||||
Services.prefs.setBoolPref("intl.locale.matchOS", false);
|
||||
Services.prefs.setCharPref("general.useragent.locale", aData);
|
||||
break;
|
||||
|
||||
default:
|
||||
dump('BrowserApp.observe: unexpected topic "' + aTopic + '"\n');
|
||||
break;
|
||||
|
@ -77,7 +77,6 @@ jmethodID GeckoAppShell::jScheduleRestart = 0;
|
||||
jmethodID GeckoAppShell::jSendMessageWrapper = 0;
|
||||
jmethodID GeckoAppShell::jSetFullScreen = 0;
|
||||
jmethodID GeckoAppShell::jSetKeepScreenOn = 0;
|
||||
jmethodID GeckoAppShell::jSetSelectedLocale = 0;
|
||||
jmethodID GeckoAppShell::jSetURITitle = 0;
|
||||
jmethodID GeckoAppShell::jShowAlertNotificationWrapper = 0;
|
||||
jmethodID GeckoAppShell::jShowFilePickerAsyncWrapper = 0;
|
||||
@ -158,7 +157,6 @@ void GeckoAppShell::InitStubs(JNIEnv *jEnv) {
|
||||
jSendMessageWrapper = getStaticMethod("sendMessage", "(Ljava/lang/String;Ljava/lang/String;I)V");
|
||||
jSetFullScreen = getStaticMethod("setFullScreen", "(Z)V");
|
||||
jSetKeepScreenOn = getStaticMethod("setKeepScreenOn", "(Z)V");
|
||||
jSetSelectedLocale = getStaticMethod("setSelectedLocale", "(Ljava/lang/String;)V");
|
||||
jSetURITitle = getStaticMethod("setUriTitle", "(Ljava/lang/String;Ljava/lang/String;)V");
|
||||
jShowAlertNotificationWrapper = getStaticMethod("showAlertNotification", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
||||
jShowFilePickerAsyncWrapper = getStaticMethod("showFilePickerAsync", "(Ljava/lang/String;J)V");
|
||||
@ -2055,35 +2053,6 @@ void GeckoAppShell::SetKeepScreenOn(bool a0) {
|
||||
env->PopLocalFrame(NULL);
|
||||
}
|
||||
|
||||
void GeckoAppShell::SetSelectedLocale(const nsAString& a0) {
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (!env) {
|
||||
ALOG_BRIDGE("Aborted: No env - %s", __PRETTY_FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (env->PushLocalFrame(1) != 0) {
|
||||
ALOG_BRIDGE("Exceptional exit of: %s", __PRETTY_FUNCTION__);
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
return;
|
||||
}
|
||||
|
||||
jstring j0 = AndroidBridge::NewJavaString(env, a0);
|
||||
|
||||
env->CallStaticVoidMethod(mGeckoAppShellClass, jSetSelectedLocale, j0);
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
ALOG_BRIDGE("Exceptional exit of: %s", __PRETTY_FUNCTION__);
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
env->PopLocalFrame(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
env->PopLocalFrame(NULL);
|
||||
}
|
||||
|
||||
void GeckoAppShell::SetURITitle(const nsAString& a0, const nsAString& a1) {
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (!env) {
|
||||
|
@ -84,7 +84,6 @@ public:
|
||||
static void SendMessageWrapper(const nsAString& a0, const nsAString& a1, int32_t a2);
|
||||
static void SetFullScreen(bool a0);
|
||||
static void SetKeepScreenOn(bool a0);
|
||||
static void SetSelectedLocale(const nsAString& a0);
|
||||
static void SetURITitle(const nsAString& a0, const nsAString& a1);
|
||||
static void ShowAlertNotificationWrapper(const nsAString& a0, const nsAString& a1, const nsAString& a2, const nsAString& a3, const nsAString& a4);
|
||||
static void ShowFilePickerAsyncWrapper(const nsAString& a0, int64_t a1);
|
||||
|
@ -163,12 +163,8 @@ nsAppShell::NotifyNativeEvent()
|
||||
mQueueCond.Notify();
|
||||
}
|
||||
|
||||
#define PREFNAME_MATCH_OS "intl.locale.matchOS"
|
||||
#define PREFNAME_UA_LOCALE "general.useragent.locale"
|
||||
#define PREFNAME_COALESCE_TOUCHES "dom.event.touch.coalescing.enabled"
|
||||
static const char* kObservedPrefs[] = {
|
||||
PREFNAME_MATCH_OS,
|
||||
PREFNAME_UA_LOCALE,
|
||||
PREFNAME_COALESCE_TOUCHES,
|
||||
nullptr
|
||||
};
|
||||
@ -182,8 +178,6 @@ nsAppShell::Init()
|
||||
#endif
|
||||
|
||||
nsresult rv = nsBaseAppShell::Init();
|
||||
AndroidBridge* bridge = AndroidBridge::Bridge();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsServ =
|
||||
mozilla::services::GetObserverService();
|
||||
if (obsServ) {
|
||||
@ -193,27 +187,7 @@ nsAppShell::Init()
|
||||
if (sPowerManagerService)
|
||||
sPowerManagerService->AddWakeLockListener(sWakeLockListener);
|
||||
|
||||
if (!bridge)
|
||||
return rv;
|
||||
|
||||
Preferences::AddStrongObservers(this, kObservedPrefs);
|
||||
|
||||
bool match;
|
||||
rv = Preferences::GetBool(PREFNAME_MATCH_OS, &match);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (match) {
|
||||
GeckoAppShell::SetSelectedLocale(EmptyString());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString locale;
|
||||
rv = Preferences::GetLocalizedString(PREFNAME_UA_LOCALE, &locale);
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = Preferences::GetString(PREFNAME_UA_LOCALE, &locale);
|
||||
}
|
||||
|
||||
GeckoAppShell::SetSelectedLocale(locale);
|
||||
mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
|
||||
return rv;
|
||||
}
|
||||
@ -228,30 +202,9 @@ nsAppShell::Observe(nsISupports* aSubject,
|
||||
// or we'll see crashes, as the app shell outlives XPConnect.
|
||||
mObserversHash.Clear();
|
||||
return nsBaseAppShell::Observe(aSubject, aTopic, aData);
|
||||
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) && aData && (
|
||||
nsDependentString(aData).Equals(
|
||||
NS_LITERAL_STRING(PREFNAME_UA_LOCALE)) ||
|
||||
nsDependentString(aData).Equals(
|
||||
NS_LITERAL_STRING(PREFNAME_COALESCE_TOUCHES)) ||
|
||||
nsDependentString(aData).Equals(
|
||||
NS_LITERAL_STRING(PREFNAME_MATCH_OS)))) {
|
||||
bool match;
|
||||
nsresult rv = Preferences::GetBool(PREFNAME_MATCH_OS, &match);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (match) {
|
||||
GeckoAppShell::SetSelectedLocale(EmptyString());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString locale;
|
||||
if (NS_FAILED(Preferences::GetLocalizedString(PREFNAME_UA_LOCALE,
|
||||
&locale))) {
|
||||
locale = Preferences::GetString(PREFNAME_UA_LOCALE);
|
||||
}
|
||||
|
||||
GeckoAppShell::SetSelectedLocale(locale);
|
||||
|
||||
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
|
||||
aData &&
|
||||
nsDependentString(aData).Equals(NS_LITERAL_STRING(PREFNAME_COALESCE_TOUCHES))) {
|
||||
mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user