Bug 699052 - Android back button should close the selected tab and return to the parent tab when possible [r=mfinkle]

This commit is contained in:
Matt Brubeck 2011-12-19 10:44:52 -08:00
parent efc97fe9ac
commit c9a6ef2daf
6 changed files with 135 additions and 46 deletions

View File

@ -548,7 +548,7 @@ abstract public class GeckoApp
GeckoAppShell.sendEventToGecko(new GeckoEvent("Permissions:Get", null));
return true;
case R.id.addons:
GeckoAppShell.sendEventToGecko(new GeckoEvent("about:addons"));
loadUrlInNewTab("about:addons");
return true;
case R.id.agent_mode:
Tab selectedTab = Tabs.getInstance().getSelectedTab();
@ -911,12 +911,10 @@ abstract public class GeckoApp
doCameraCapture();
} else if (event.equals("Tab:Added")) {
Log.i(LOGTAG, "Created a new tab");
int tabId = message.getInt("tabID");
String uri = message.getString("uri");
Tab tab = handleAddTab(message);
Boolean selected = message.getBoolean("selected");
handleAddTab(tabId, uri);
if (selected)
handleSelectTab(tabId);
handleSelectTab(tab.getId());
} else if (event.equals("Tab:Closed")) {
Log.i(LOGTAG, "Destroyed a tab");
int tabId = message.getInt("tabID");
@ -1137,14 +1135,17 @@ abstract public class GeckoApp
});
}
void handleAddTab(final int tabId, final String uri) {
final Tab tab = Tabs.getInstance().addTab(tabId, uri);
Tab handleAddTab(JSONObject params) throws JSONException {
Log.i(LOGTAG, params.toString());
final Tab tab = Tabs.getInstance().addTab(params);
mMainHandler.post(new Runnable() {
public void run() {
mBrowserToolbar.updateTabs(Tabs.getInstance().getCount());
}
});
return tab;
}
void handleCloseTab(final int tabId) {
@ -2008,10 +2009,31 @@ abstract public class GeckoApp
return;
}
Tab tab = Tabs.getInstance().getSelectedTab();
if (tab == null || !tab.doBack()) {
Tabs tabs = Tabs.getInstance();
Tab tab = tabs.getSelectedTab();
if (tab == null) {
moveTaskToBack(true);
return;
}
if (tab.doBack())
return;
if (tab.isExternal()) {
moveTaskToBack(true);
tabs.closeTab(tab);
return;
}
int parentId = tab.getParentId();
Tab parent = tabs.getTab(parentId);
if (parent != null) {
// The back button should always return to the parent (not a sibling).
tabs.closeTab(tab, parent);
return;
}
moveTaskToBack(true);
}
static int kCaptureIndex = 0;
@ -2128,6 +2150,22 @@ abstract public class GeckoApp
loadRequest(url, type, null);
}
/**
* Open the link as a new tab, and mark the selected tab as its "parent".
* Use this for tabs opened by the browser chrome, so users can press the
* "Back" button to return to the previous tab.
*/
public void loadUrlInNewTab(String url) {
JSONObject args = new JSONObject();
try {
args.put("url", url);
args.put("parentId", Tabs.getInstance().getSelectedTabId());
} catch (Exception e) {
Log.e(LOGTAG, "error building JSON arguments");
}
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Add", args.toString()));
}
public GeckoSoftwareLayerClient getSoftwareLayerClient() { return mSoftwareLayerClient; }
public LayerController getLayerController() { return mLayerController; }

View File

@ -55,7 +55,7 @@ class LinkPreference extends Preference {
@Override
protected void onClick() {
GeckoApp.mAppContext.loadUrl(mUrl, AwesomeBar.Type.ADD);
GeckoApp.mAppContext.loadUrlInNewTab(mUrl);
callChangeListener(mUrl);
}
}

View File

@ -70,6 +70,8 @@ public class Tab {
private Drawable mThumbnail;
private List<HistoryEntry> mHistory;
private int mHistoryIndex;
private int mParentId;
private boolean mExternal;
private boolean mLoading;
private boolean mBookmark;
private HashMap<String, DoorHanger> mDoorHangers;
@ -89,12 +91,14 @@ public class Tab {
}
public Tab() {
this(-1, "");
this(-1, "", false, -1);
}
public Tab(int id, String url) {
public Tab(int id, String url, boolean external, int parentId) {
mId = id;
mUrl = url;
mExternal = external;
mParentId = parentId;
mTitle = "";
mFavicon = null;
mFaviconUrl = null;
@ -113,6 +117,10 @@ public class Tab {
return mId;
}
public int getParentId() {
return mParentId;
}
public String getURL() {
return mUrl;
}
@ -178,6 +186,10 @@ public class Tab {
return mBookmark;
}
public boolean isExternal() {
return mExternal;
}
public void updateURL(String url) {
if (url != null && url.length() > 0) {
mUrl = url;

View File

@ -44,6 +44,7 @@ import android.graphics.drawable.*;
import android.util.Log;
import org.json.JSONObject;
import org.json.JSONException;
public class Tabs implements GeckoEventListener {
private static final String LOGTAG = "GeckoTabs";
@ -67,11 +68,16 @@ public class Tabs implements GeckoEventListener {
return tabs.size();
}
public Tab addTab(int id, String url) {
public Tab addTab(JSONObject params) throws JSONException {
int id = params.getInt("tabID");
if (tabs.containsKey(id))
return tabs.get(id);
Tab tab = new Tab(id, url);
String url = params.getString("uri");
Boolean external = params.getBoolean("external");
int parentId = params.getInt("parentId");
Tab tab = new Tab(id, url, external, parentId);
tabs.put(id, tab);
order.add(tab);
Log.i(LOGTAG, "Added a tab with id: " + id + ", url: " + url);
@ -127,6 +133,42 @@ public class Tabs implements GeckoEventListener {
return tabs.get(id);
}
/** Close tab and then select the default next tab */
public void closeTab(Tab tab) {
closeTab(tab, getNextTab(tab));
}
/** Close tab and then select nextTab */
public void closeTab(Tab tab, Tab nextTab) {
if (tab == null)
return;
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", String.valueOf(nextTab.getId())));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", String.valueOf(tab.getId())));
}
/** Return the tab that will be selected by default after this one is closed */
public Tab getNextTab(Tab tab) {
Tab selectedTab = getSelectedTab();
if (selectedTab != tab)
return selectedTab;
int index = getIndexOf(tab);
Tab nextTab = getTabAt(index + 1);
if (nextTab == null)
nextTab = getTabAt(index - 1);
Tab parent = getTab(tab.getParentId());
if (parent != null) {
// If the next tab is a sibling, switch to it. Otherwise go back to the parent.
if (nextTab != null && nextTab.getParentId() == tab.getParentId())
return nextTab;
else
return parent;
}
return nextTab;
}
public HashMap<Integer, Tab> getTabs() {
if (getCount() == 0)
return null;

View File

@ -180,23 +180,7 @@ public class TabsTray extends Activity implements GeckoApp.OnTabsChangedListener
String tabId = v.getTag().toString();
Tabs tabs = Tabs.getInstance();
Tab tab = tabs.getTab(Integer.parseInt(tabId));
if (tab == null)
return;
if (tabs.isSelectedTab(tab)) {
int index = tabs.getIndexOf(tab);
if (index >= 1)
index--;
else
index = 1;
int id = tabs.getTabAt(index).getId();
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", String.valueOf(id)));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", tabId));
} else {
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", tabId));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", String.valueOf(tabs.getSelectedTabId())));
}
tabs.closeTab(tab);
}
};
}

View File

@ -582,22 +582,21 @@ var BrowserApp = {
});
},
getSearchOrFixupURI: function(aData) {
let args = JSON.parse(aData);
getSearchOrFixupURI: function(aParams) {
let uri;
if (args.engine) {
if (aParams.engine) {
let engine;
if (args.engine == "__default__")
if (aParams.engine == "__default__")
engine = Services.search.currentEngine || Services.search.defaultEngine;
else
engine = Services.search.getEngineByName(args.engine);
engine = Services.search.getEngineByName(aParams.engine);
if (engine)
uri = engine.getSubmission(args.url).uri;
uri = engine.getSubmission(aParams.url).uri;
} else {
uri = URIFixup.createFixupURI(args.url, Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
uri = URIFixup.createFixupURI(aParams.url, Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
}
return uri ? uri.spec : args.url;
return uri ? uri.spec : aParams.url;
},
scrollToFocusedInput: function(aBrowser) {
@ -629,14 +628,17 @@ var BrowserApp = {
} else if (aTopic == "Session:Stop") {
browser.stop();
} else if (aTopic == "Tab:Add" || aTopic == "Tab:Load") {
let data = JSON.parse(aData);
// Pass LOAD_FLAGS_DISALLOW_INHERIT_OWNER to prevent any loads from
// inheriting the currently loaded document's principal.
let params = {
selected: true,
parentId: ("parentId" in data) ? data.parentId : -1,
flags: Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER
};
let url = this.getSearchOrFixupURI(aData);
let url = this.getSearchOrFixupURI(data);
if (aTopic == "Tab:Add")
this.addTab(url, params);
else
@ -807,7 +809,7 @@ var NativeWindow = {
this.linkContext,
function(aTarget) {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
BrowserApp.addTab(url, { selected: false });
BrowserApp.addTab(url, { selected: false, parentId: BrowserApp.selectedTab.id });
});
this.add(Strings.browser.GetStringFromName("contextmenu.fullScreen"),
@ -1020,9 +1022,18 @@ nsBrowserAccess.prototype = {
}
}
let newTab = (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW || aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB);
let parentId = -1;
if (newTab && !isExternal) {
let parent = BrowserApp.getTabForBrowser(BrowserApp.getBrowserForWindow(aOpener));
if (parent)
parentId = parent.id;
}
let browser;
if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW || aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
let tab = BrowserApp.addTab("about:blank", { selected: true });
if (newTab) {
let tab = BrowserApp.addTab("about:blank", { external: isExternal, parentId: parentId, selected: true });
browser = tab.browser;
} else { // OPEN_CURRENTWINDOW and illegal values
browser = BrowserApp.selectedBrowser;
@ -1114,6 +1125,8 @@ Tab.prototype = {
type: "Tab:Added",
tabID: this.id,
uri: aURL,
parentId: ("parentId" in aParams) ? aParams.parentId : -1,
external: ("external" in aParams) ? aParams.external : false,
selected: ("selected" in aParams) ? aParams.selected : true
}
};