Merge fx-team to m-c

This commit is contained in:
Wes Kocher 2013-09-13 18:08:07 -07:00
commit a9fefc1dc8
16 changed files with 193 additions and 190 deletions

View File

@ -81,3 +81,6 @@ function testShowPanel(assert, panel) {
return promise;
}
//Test disabled because of bug 911071
module.exports = {}

View File

@ -164,6 +164,8 @@ exports["test Document Reload"] = function(assert, done) {
assert.pass('Panel was created');
};
// Test disabled because of bug 910230
/*
exports["test Parent Resize Hack"] = function(assert, done) {
const { Panel } = require('sdk/panel');
@ -216,6 +218,7 @@ exports["test Parent Resize Hack"] = function(assert, done) {
panel.show();
}
*/
exports["test Resize Panel"] = function(assert, done) {
const { Panel } = require('sdk/panel');

View File

@ -25,7 +25,7 @@
<div id="status" class="banner-box">
<div class="banner-content">
<span template='{"type":"localizedContent","property":"connection.connectedToDevice","paths":["device.description.name"]}'></span>
<button class="action-cancel" onclick="UI.disconnect()">&connection.disconnect;</button>
<button class="action-cancel" onclick="UI.disconnect()" title="&connection.disconnectTooltip;">&connection.disconnect;</button>
</div>
</div>
</div>
@ -36,11 +36,11 @@
<div class="banner-box">
<div class="banner-content">
<span>&connection.notConnected;</span>
<button class="action-primary left" onclick="UI.connect()" id="connect-button" template='{"type":"localizedContent","property":"connection.connectTo","paths":["connection.host","connection.port"]}'></button>
<button class="right" onclick="UI.editConnectionParameters()">&connection.changeHostAndPort;</button>
<button class="action-primary left" onclick="UI.connect()" id="connect-button" template='{"type":"localizedContent","property":"connection.connectTo","paths":["connection.host","connection.port"]}' title="&connection.connectTooltip;"></button>
<button class="right" onclick="UI.editConnectionParameters()" title="&connection.changeHostAndPortTooltip;">&connection.changeHostAndPort;</button>
<div id="start-simulator-box">
<span>&connection.or;</span>
<button id="start-simulator-button" class="action-primary" onclick="UI.showSimulatorList()">&connection.startSimulator;</button>
<button id="start-simulator-button" class="action-primary" onclick="UI.showSimulatorList()" title="&connection.startSimulatorTooltip;">&connection.startSimulator;</button>
</div>
</div>
</div>
@ -52,7 +52,7 @@
<div id="status" class="banner-box">
<div class="banner-content">
<span>&connection.connecting;</span>
<button class="action-cancel" onclick="UI.disconnect()">&connection.cancel;</button>
<button class="action-cancel" onclick="UI.disconnect()" title="&connection.cancelConnectTooltip;">&connection.cancel;</button>
</div>
</div>
</div>
@ -75,7 +75,7 @@
<form onsubmit="UI.saveConnectionInfo()">
<input class="host" template='{"type":"attribute","path":"connection.host","name":"value"}'></input>
<input class="port" pattern="\d+" template='{"type":"attribute","path":"connection.port","name":"value"}' type="number"></input>
<button type="submit">&connection.saveConnectionInfo;</button>
<button type="submit" title="&connection.saveConnectionInfoTooltip;">&connection.saveConnectionInfo;</button>
</form>
</div>
</div>
@ -88,13 +88,13 @@
<div class="banner-content">
<div class="no-simulator">
<span>&connection.noSimulatorInstalled;</span>
<button class="action-primary" onclick="UI.installSimulator()">&connection.installFirstSimulator;</button>
<button class="action-primary" onclick="UI.installSimulator()" title="&connection.installOneSimulatorTooltip;">&connection.installOneSimulator;</button>
</div>
<div class="found-simulator">
<span template-loop='{"arrayPath":"simulators.versions","childSelector":"#simulator-item-template"}'></span>
<button class="action-primary" onclick="UI.installSimulator()">&connection.installAnotherSimulator;</button>
<button class="action-primary" onclick="UI.installSimulator()" title="&connection.installAnotherSimulatorTooltip;">&connection.installAnotherSimulator;</button>
</div>
<button class="action-cancel" onclick="UI.cancelShowSimulatorList()">&connection.cancel;</button>
<button class="action-cancel" onclick="UI.cancelShowSimulatorList()" title="&connection.cancelShowSimulatorTooltip;">&connection.cancel;</button>
</div>
</div>
</div>
@ -113,7 +113,7 @@
<template id="simulator-item-template">
<span>
<button class="simulator-item" onclick="UI.startSimulator(this.dataset.version)" template='{"type":"attribute","path":"version","name":"data-version"}'>
<button class="simulator-item" onclick="UI.startSimulator(this.dataset.version)" template='{"type":"attribute","path":"version","name":"data-version"}' title="&connection.startSimulatorTooltip;">
<span template='{"type":"textContent", "path":"version"}'></span>
</button>
</span>

View File

@ -36,10 +36,15 @@
</h3>
<p template='{"type":"localizedContent","property":"device.deviceSize", "paths":["device.description.width","device.description.height","device.description.dpi"]}'></p>
</header>
<button onclick="UI.screenshot()">&device.screenshot;</button>
<button onclick="UI.screenshot()" title="&device.screenshotTooltip;">&device.screenshot;</button>
<div id="tabs-headers">
<div onclick="UI.setTab('apps')" class="tab sidebar-item apps">&device.installedApps;</div>
<div onclick="UI.setTab('permissions')" class="tab sidebar-item permissions">&device.permissions;</div>
<div onclick="UI.setTab('apps')" class="tab sidebar-item apps" title="&device.installedAppsTooltip;">&device.installedApps;</div>
<div onclick="UI.setTab('permissions')" class="tab sidebar-item permissions" title="&device.permissionsTooltip;">
&device.permissions;
<a target="_blank" href="&device.permissionsHelpLink;">
<button class="help">&device.help;</button>
</a>
</div>
</div>
</div>
</aside>
@ -59,9 +64,9 @@
<section template-loop='{"arrayPath":"device.permissions","childSelector":"#permission-template"}'></section>
</div>
<div class="permission-table-footer">
<div class="allow-label">&device.allow;</div>
<div class="prompt-label">&device.prompt;</div>
<div class="deny-label">&device.deny;</div>
<div class="allow-label" title="&device.allowTooltip;">&device.allow;</div>
<div class="prompt-label" title="&device.promptTooltip;">&device.prompt;</div>
<div class="deny-label" title="&device.denyTooltip;">&device.deny;</div>
</div>
</div>
</div>
@ -84,9 +89,9 @@
<img class="app-icon" template='{"type":"attribute","path":"iconURL","name":"src"}'></img>
<span class="app-name" template='{"type":"textContent","path":"name"}'></span>
<div class="app-buttons">
<button class="button-debug" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.openToolbox(this.dataset.manifest)">&device.debugApp;</button>
<button class="button-start" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.startApp(this.dataset.manifest)">&device.startApp;</button>
<button class="button-stop" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.stopApp(this.dataset.manifest)">&device.stopApp;</button>
<button class="button-debug" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.openToolbox(this.dataset.manifest)" title="&device.debugAppTooltip;">&device.debugApp;</button>
<button class="button-start" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.startApp(this.dataset.manifest)" title="&device.startAppTooltip;">&device.startApp;</button>
<button class="button-stop" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.stopApp(this.dataset.manifest)" title="&device.stopAppTooltip;">&device.stopApp;</button>
</div>
</div>
</template>

View File

@ -22,11 +22,11 @@
<div template-loop='{"arrayPath":"projects","childSelector":"#project-item-template"}'></div>
<div id="no-project">&projects.noProjects;</div>
</div>
<div id="new-packaged-project" onclick="UI.addPackaged()">&projects.addPackaged;</div>
<div id="new-packaged-project" onclick="UI.addPackaged()" title="&projects.addPackagedTooltip;">&projects.addPackaged;</div>
<div id="new-hosted-project">&projects.addHosted;
<form onsubmit="UI.addHosted(); return false;" id="new-hosted-project-wrapper">
<input value="" id="url-input" type="url" pattern="https?://.+" placeholder="&projects.hostedManifestPlaceHolder2;" size="50" />
<div onclick="UI.addHosted()" id="new-hosted-project-click"></div>
<div onclick="UI.addHosted()" id="new-hosted-project-click" title="&projects.addHostedTooltip;"></div>
<input type="submit" hidden="true"></input>
</form>
</div>
@ -39,7 +39,7 @@
<div class="project-item-status" template='{"type":"attribute","path":"validationStatus","name":"status"}'></div>
<img class="project-item-icon" template='{"type":"attribute","path":"icon","name":"src"}' />
<div class="project-item-meta">
<div class="button-remove" onclick="UI.remove(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}' title="&projects.removeApp;"></div>
<div class="button-remove" onclick="UI.remove(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}' title="&projects.removeAppFromList;"></div>
<strong template='{"type":"textContent","path":"name"}'></strong>
<span class="project-item-type" template='{"type":"textContent","path":"type"}'></span>
<p class="project-item-description" template='{"type":"textContent","path":"manifest.description"}'></p>

View File

@ -7,42 +7,64 @@
<!ENTITY index.device2 "Device">
<!ENTITY device.screenshot "Screenshot">
<!ENTITY device.screenshotTooltip "Open a screenshot of the current state of the device in a new tab.">
<!ENTITY device.title "Device Control Center">
<!ENTITY device.notConnected "Not connected. Please connect your device below.">
<!ENTITY device.startApp "Start">
<!ENTITY device.startAppTooltip "Start this app on the device.">
<!ENTITY device.stopApp "Stop">
<!ENTITY device.stopAppTooltip "Stop this app on the device.">
<!ENTITY device.debugApp "Debug">
<!ENTITY device.debugAppTooltip "Open the Developer Tools connected to this app on the device.">
<!ENTITY device.name "Name">
<!ENTITY device.app "App">
<!ENTITY device.privileged "Privileged">
<!ENTITY device.certified "Certified">
<!ENTITY device.allow "Allow">
<!ENTITY device.allowTooltip "This permission is allowed for apps of this type.">
<!ENTITY device.prompt "Prompt">
<!ENTITY device.promptTooltip "This permission requires a user prompt for apps of this type.">
<!ENTITY device.deny "Deny">
<!ENTITY device.denyTooltip "This permission is denied for apps of this type.">
<!ENTITY device.installedApps "Installed Apps">
<!ENTITY device.installedAppsTooltip "View a list of apps installed on the device. Some apps, such as certified apps, may be excluded from this view.">
<!ENTITY device.permissions "Permissions">
<!ENTITY device.permissionsTooltip "View a table of the permissions accessible to the different types of apps.">
<!ENTITY device.permissionsHelpLink "https://developer.mozilla.org/docs/Web/Apps/App_permissions">
<!ENTITY device.help "Help">
<!ENTITY connection.connectTooltip "Connect to the device.">
<!ENTITY connection.disconnect "Disconnect">
<!ENTITY connection.disconnectTooltip "Disconnect from the current device or simulator.">
<!ENTITY connection.showDeviceCtrlCenter "Click for More Details">
<!ENTITY connection.notConnected "Not Connected">
<!ENTITY connection.changeHostAndPort "Change">
<!ENTITY connection.changeHostAndPortTooltip "Change the host and port used to connect to the device. (Defaults to localhost:6000)">
<!ENTITY connection.startSimulator "Start Simulator">
<!ENTITY connection.startSimulatorTooltip "Start an instance of the Simulator and connect to it.">
<!ENTITY connection.saveConnectionInfo "Save">
<!ENTITY connection.saveConnectionInfoTooltip "Save the host and port.">
<!ENTITY connection.connecting "Connecting…">
<!ENTITY connection.disconnecting "Disconnecting…">
<!ENTITY connection.cancel "Cancel">
<!ENTITY connection.cancelConnectTooltip "Cancel the connection in progress.">
<!ENTITY connection.cancelShowSimulatorTooltip "Exit the Simulator connection mode and return to the initial prompt.">
<!ENTITY connection.or "or">
<!ENTITY connection.noSimulatorInstalled "No simulator installed.">
<!ENTITY connection.installFirstSimulator "Install simulator.">
<!ENTITY connection.installOneSimulator "Install Simulator">
<!ENTITY connection.installOneSimulatorTooltip "Install a version of the Simulator by downloading the relevant add-on.">
<!ENTITY connection.installAnotherSimulator "Add">
<!ENTITY connection.installAnotherSimulatorTooltip "Install an additional version of the Simulator by downloading the relevant add-on.">
<!ENTITY projects.localApps "Local Apps">
<!ENTITY projects.addApp "Add">
<!ENTITY projects.addPackaged "Add Packaged App">
<!ENTITY projects.addPackagedTooltip "Add a new packaged app (a directory) from your computer.">
<!ENTITY projects.addHosted "Add Hosted App">
<!ENTITY projects.addHostedTooltip "Add a new hosted app (link to a manifest.webapp file) from a remote website.">
<!ENTITY projects.title "Local Apps">
<!ENTITY projects.appDetails "App Details">
<!ENTITY projects.removeApp "Remove">
<!ENTITY projects.removeAppFromList "Remove this app from the list of apps you are working on. This will not remove it from a device or a simulator.">
<!ENTITY projects.reloadFiles "Refresh">
<!ENTITY projects.installApp "Install">
<!ENTITY projects.startApp "Start">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 445 B

After

Width:  |  Height:  |  Size: 535 B

View File

@ -273,7 +273,10 @@ button {
color: #FFF;
}
.help {
float: right;
padding: 0 5px;
}
/***************** HEADER *****************/

View File

@ -108,9 +108,6 @@ abstract public class BrowserApp extends GeckoApp
private View mHomePagerContainer;
protected Telemetry.Timer mAboutHomeStartupTimer = null;
// Set the default session restore value
private int mSessionRestore = -1;
private static final int GECKO_TOOLS_MENU = -1;
private static final int ADDON_MENU_OFFSET = 1000;
private class MenuItemInfo {
@ -385,15 +382,6 @@ abstract public class BrowserApp extends GeckoApp
});
}
@Override
protected int getSessionRestoreState(Bundle savedInstanceState) {
if (mSessionRestore > -1) {
return mSessionRestore;
}
return super.getSessionRestoreState(savedInstanceState);
}
@Override
public void onCreate(Bundle savedInstanceState) {
mAboutHomeStartupTimer = new Telemetry.Timer("FENNEC_STARTUP_TIME_ABOUTHOME");
@ -401,8 +389,8 @@ abstract public class BrowserApp extends GeckoApp
String args = getIntent().getStringExtra("args");
if (args != null && args.contains(GUEST_BROWSING_ARG)) {
mProfile = GeckoProfile.createGuestProfile(this);
} else if (GeckoProfile.maybeCleanupGuestProfile(this)) {
mSessionRestore = RESTORE_NORMAL;
} else {
GeckoProfile.maybeCleanupGuestProfile(this);
}
super.onCreate(savedInstanceState);
@ -463,6 +451,20 @@ abstract public class BrowserApp extends GeckoApp
}
});
mBrowserToolbar.setOnStartEditingListener(new BrowserToolbar.OnStartEditingListener() {
public void onStartEditing() {
// Temporarily disable doorhanger notifications.
mDoorHangerPopup.disable();
}
});
mBrowserToolbar.setOnStopEditingListener(new BrowserToolbar.OnStopEditingListener() {
public void onStopEditing() {
// Re-enable doorhanger notifications.
mDoorHangerPopup.enable();
}
});
// Intercept key events for gamepad shortcuts
mBrowserToolbar.setOnKeyListener(this);
@ -826,7 +828,7 @@ abstract public class BrowserApp extends GeckoApp
@Override
protected void loadStartupTab(String url) {
// We aren't showing about:home, so cancel the telemetry timer
if (url != null || mRestoreMode != RESTORE_NONE) {
if (url != null || mShouldRestore) {
mAboutHomeStartupTimer.cancel();
}
@ -961,13 +963,6 @@ abstract public class BrowserApp extends GeckoApp
}
}
public View getActionBarLayout() {
RelativeLayout actionBar = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.browser_toolbar, null);
actionBar.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT,
(int) getResources().getDimension(R.dimen.browser_toolbar_height)));
return actionBar;
}
@Override
public boolean hasTabsSideBar() {
return (mTabsPanel != null && mTabsPanel.isSideBar());
@ -1411,7 +1406,7 @@ abstract public class BrowserApp extends GeckoApp
animator.start();
}
void commitEditingMode() {
private void commitEditingMode() {
if (!mBrowserToolbar.isEditing()) {
return;
}
@ -1486,7 +1481,7 @@ abstract public class BrowserApp extends GeckoApp
}
}
boolean dismissEditingMode() {
private boolean dismissEditingMode() {
if (!mBrowserToolbar.isEditing()) {
return false;
}

View File

@ -101,6 +101,14 @@ public class BrowserToolbar extends GeckoRelativeLayout
public void onFilter(String searchText, AutocompleteHandler handler);
}
public interface OnStartEditingListener {
public void onStartEditing();
}
public interface OnStopEditingListener {
public void onStopEditing();
}
private LayoutParams mAwesomeBarParams;
private View mUrlDisplayContainer;
private View mUrlEditContainer;
@ -128,10 +136,13 @@ public class BrowserToolbar extends GeckoRelativeLayout
private LinearLayout mActionItemBar;
private MenuPopup mMenuPopup;
private List<? extends View> mFocusOrder;
private OnActivateListener mActivateListener;
private OnCommitListener mCommitListener;
private OnDismissListener mDismissListener;
private OnFilterListener mFilterListener;
private OnStartEditingListener mStartEditingListener;
private OnStopEditingListener mStopEditingListener;
final private BrowserApp mActivity;
private boolean mHasSoftMenuButton;
@ -1181,6 +1192,14 @@ public class BrowserToolbar extends GeckoRelativeLayout
mFilterListener = listener;
}
public void setOnStartEditingListener(OnStartEditingListener listener) {
mStartEditingListener = listener;
}
public void setOnStopEditingListener(OnStopEditingListener listener) {
mStopEditingListener = listener;
}
private void showSoftInput() {
InputMethodManager imm =
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
@ -1268,6 +1287,10 @@ public class BrowserToolbar extends GeckoRelativeLayout
mUrlEditText.setText(url != null ? url : "");
mIsEditing = true;
if (mStartEditingListener != null) {
mStartEditingListener.onStartEditing();
}
final int entryTranslation = getUrlBarEntryTranslation();
final int curveTranslation = getUrlBarCurveTranslation();
@ -1376,6 +1399,10 @@ public class BrowserToolbar extends GeckoRelativeLayout
}
mIsEditing = false;
if (mStopEditingListener != null) {
mStopEditingListener.onStopEditing();
}
if (HardwareUtils.isTablet() || Build.VERSION.SDK_INT < 11) {
hideUrlEditContainer();

View File

@ -132,7 +132,6 @@ public class CrashReporter extends Activity
getSharedPreferences(GeckoApp.PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, true);
editor.putBoolean(GeckoApp.PREFS_CRASHED, true);
editor.commit();
final CheckBox allowContactCheckBox = (CheckBox) findViewById(R.id.allow_contact);
@ -413,6 +412,7 @@ public class CrashReporter extends Activity
Intent intent = new Intent(action);
intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.BROWSER_INTENT_CLASS);
intent.putExtra("didRestart", true);
Log.i(LOGTAG, intent.toString());
startActivity(intent);
} catch (Exception e) {

View File

@ -30,6 +30,9 @@ public class DoorHangerPopup extends ArrowPopup
// uniquely identified by its tabId and value.
private HashSet<DoorHanger> mDoorHangers;
// Whether or not the doorhanger popup is disabled.
private boolean mDisabled;
DoorHangerPopup(GeckoApp activity, View anchor) {
super(activity, anchor);
@ -46,6 +49,24 @@ public class DoorHangerPopup extends ArrowPopup
Tabs.unregisterOnTabsChangedListener(this);
}
/**
* Temporarily disables the doorhanger popup. If the popup is disabled,
* it will not be shown to the user, but it will continue to process
* calls to add/remove doorhanger notifications.
*/
void disable() {
mDisabled = true;
updatePopup();
}
/**
* Re-enables the doorhanger popup.
*/
void enable() {
mDisabled = false;
updatePopup();
}
@Override
public void handleMessage(String event, JSONObject geckoObject) {
try {
@ -246,12 +267,13 @@ public class DoorHangerPopup extends ArrowPopup
*
* This method must be called on the UI thread.
*/
void updatePopup() {
private void updatePopup() {
// Bail if the selected tab is null, if there are no active doorhangers,
// or if we haven't inflated the layout yet (this can happen if updatePopup()
// is called before the runnable from addDoorHanger() runs).
// if we haven't inflated the layout yet (this can happen if updatePopup()
// is called before the runnable from addDoorHanger() runs), or if the
// doorhanger popup is temporarily disabled.
Tab tab = Tabs.getInstance().getSelectedTab();
if (tab == null || mDoorHangers.size() == 0 || !mInflated) {
if (tab == null || mDoorHangers.size() == 0 || !mInflated || mDisabled) {
dismiss();
return;
}

View File

@ -149,13 +149,8 @@ abstract public class GeckoApp
public static final String PREFS_NAME = "GeckoApp";
public static final String PREFS_OOM_EXCEPTION = "OOMException";
public static final String PREFS_WAS_STOPPED = "wasStopped";
public static final String PREFS_CRASHED = "crashed";
public static final String PREFS_VERSION_CODE = "versionCode";
static public final int RESTORE_NONE = 0;
static public final int RESTORE_NORMAL = 1;
static public final int RESTORE_CRASH = 2;
static private final String LOCATION_URL = "https://location.services.mozilla.com/v1/submit";
// Delay before running one-time "cleanup" tasks that may be needed
@ -197,7 +192,7 @@ abstract public class GeckoApp
private HashMap<String, PowerManager.WakeLock> mWakeLocks = new HashMap<String, PowerManager.WakeLock>();
protected int mRestoreMode = RESTORE_NONE;
protected boolean mShouldRestore;
protected boolean mInitialized = false;
private Telemetry.Timer mJavaUiStartupTimer;
private Telemetry.Timer mGeckoReadyStartupTimer;
@ -1243,11 +1238,9 @@ abstract public class GeckoApp
mNotificationHelper = new NotificationHelper(this);
mToast = new ButtonToast(findViewById(R.id.toast));
// Check if the last run was exited due to a normal kill while
// we were in the background, or a more harsh kill while we were
// active.
mRestoreMode = getSessionRestoreState(savedInstanceState);
if (mRestoreMode == RESTORE_NORMAL && savedInstanceState != null) {
// Determine whether we should restore tabs.
mShouldRestore = getSessionRestoreState(savedInstanceState);
if (mShouldRestore && savedInstanceState != null) {
boolean wasInBackground =
savedInstanceState.getBoolean(SAVED_STATE_IN_BACKGROUND, false);
@ -1334,7 +1327,7 @@ abstract public class GeckoApp
*/
protected void loadStartupTab(String url) {
if (url == null) {
if (mRestoreMode == RESTORE_NONE) {
if (!mShouldRestore) {
// Show about:home if we aren't restoring previous session and
// there's no external URL
Tab tab = Tabs.getInstance().loadUrl("about:home", Tabs.LOADURL_NEW_TAB);
@ -1387,24 +1380,26 @@ abstract public class GeckoApp
initializeChrome();
// If we are doing a restore, read the session data and send it to Gecko
String restoreMessage = null;
if (mRestoreMode != RESTORE_NONE && !mIsRestoringActivity) {
try {
// restoreSessionTabs() will create simple tab stubs with the
// URL and title for each page, but we also need to restore
// session history. restoreSessionTabs() will inject the IDs
// of the tab stubs into the JSON data (which holds the session
// history). This JSON data is then sent to Gecko so session
// history can be restored for each tab.
restoreMessage = restoreSessionTabs(isExternalURL);
} catch (SessionRestoreException e) {
// If restore failed, do a normal startup
Log.e(LOGTAG, "An error occurred during restore", e);
mRestoreMode = RESTORE_NONE;
if (!mIsRestoringActivity) {
String restoreMessage = null;
if (mShouldRestore) {
try {
// restoreSessionTabs() will create simple tab stubs with the
// URL and title for each page, but we also need to restore
// session history. restoreSessionTabs() will inject the IDs
// of the tab stubs into the JSON data (which holds the session
// history). This JSON data is then sent to Gecko so session
// history can be restored for each tab.
restoreMessage = restoreSessionTabs(isExternalURL);
} catch (SessionRestoreException e) {
// If restore failed, do a normal startup
Log.e(LOGTAG, "An error occurred during restore", e);
mShouldRestore = false;
}
}
}
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Session:Restore", restoreMessage));
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Session:Restore", restoreMessage));
}
// External URLs should always be loaded regardless of whether Gecko is
// already running.
@ -1414,17 +1409,14 @@ abstract public class GeckoApp
loadStartupTab(null);
}
if (mRestoreMode == RESTORE_NORMAL) {
// If we successfully did an OOM restore, we now have tab stubs
// from the last session. Any future tabs should be animated.
Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
} else {
// Move the session file if it exists
getProfile().moveSessionFile();
}
// We now have tab stubs from the last session. Any future tabs should
// be animated.
Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
if (mRestoreMode == RESTORE_NONE) {
Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
// If we're not restoring, move the session file so it can be read for
// the last tabs section.
if (!mShouldRestore) {
getProfile().moveSessionFile();
}
Telemetry.HistogramAdd("FENNEC_STARTUP_GECKOAPP_ACTION", startupAction.ordinal());
@ -1595,7 +1587,7 @@ abstract public class GeckoApp
// If we are doing an OOM restore, parse the session data and
// stub the restored tabs immediately. This allows the UI to be
// updated before Gecko has restored.
if (mRestoreMode == RESTORE_NORMAL) {
if (mShouldRestore) {
final JSONArray tabs = new JSONArray();
SessionParser parser = new SessionParser() {
@Override
@ -1633,7 +1625,6 @@ abstract public class GeckoApp
}
JSONObject restoreData = new JSONObject();
restoreData.put("normalRestore", mRestoreMode == RESTORE_NORMAL);
restoreData.put("sessionString", sessionString);
return restoreData.toString();
@ -1650,9 +1641,15 @@ abstract public class GeckoApp
return mProfile;
}
protected int getSessionRestoreState(Bundle savedInstanceState) {
/**
* Determine whether the session should be restored.
*
* @param savedInstanceState Saved instance state given to the activity
* @return Whether to restore
*/
protected boolean getSessionRestoreState(Bundle savedInstanceState) {
final SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
int restoreMode = RESTORE_NONE;
boolean shouldRestore = false;
final int versionCode = getVersionCode();
if (prefs.getInt(PREFS_VERSION_CODE, 0) != versionCode) {
@ -1667,33 +1664,23 @@ abstract public class GeckoApp
}
});
restoreMode = RESTORE_NORMAL;
} else if (savedInstanceState != null || PreferenceManager.getDefaultSharedPreferences(this)
.getString(GeckoPreferences.PREFS_RESTORE_SESSION, "quit")
.equals("always")) {
// We're coming back from a background kill by the OS or the user
// has chosen to always restore, so restore.
restoreMode = RESTORE_NORMAL;
shouldRestore = true;
} else if (savedInstanceState != null || getSessionRestorePreference().equals("always") || getRestartFromIntent()) {
// We're coming back from a background kill by the OS, the user
// has chosen to always restore, or we just restarted.
shouldRestore = true;
}
// We record crashes in the crash reporter. If sessionstore.js
// exists, but we didn't flag a crash in the crash reporter, we
// were probably just force killed by the user, so we shouldn't do
// a restore.
if (prefs.getBoolean(PREFS_CRASHED, false)) {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
prefs.edit()
.putBoolean(PREFS_CRASHED, false)
.commit();
}
});
return shouldRestore;
}
restoreMode = RESTORE_CRASH;
}
private String getSessionRestorePreference() {
return PreferenceManager.getDefaultSharedPreferences(this)
.getString(GeckoPreferences.PREFS_RESTORE_SESSION, "quit");
}
return restoreMode;
private boolean getRestartFromIntent() {
return getIntent().getBooleanExtra("didRestart", false);
}
/**
@ -2155,12 +2142,14 @@ abstract public class GeckoApp
Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
if (args != null)
intent.putExtra("args", args);
intent.putExtra("didRestart", true);
Log.d(LOGTAG, "Restart intent: " + intent.toString());
GeckoAppShell.killAnyZombies();
startActivity(intent);
} catch (Exception e) {
Log.e(LOGTAG, "Error effecting restart.", e);
}
finish();
// Give the restart process time to start before we die
GeckoAppShell.waitForAnotherGeckoProc();

View File

@ -152,9 +152,9 @@ public class WebAppImpl extends GeckoApp {
}
@Override
protected int getSessionRestoreState(Bundle savedInstanceState) {
protected boolean getSessionRestoreState(Bundle savedInstanceState) {
// for now webapps never restore your session
return RESTORE_NONE;
return false;
}
@Override

View File

@ -70,17 +70,10 @@ interface nsISessionStore : nsISupports
*/
void deleteTabValue(in jsval aTab, in AString aKey);
/**
* @returns A boolean indicating we should restore previous browser session
*/
boolean shouldRestore();
/**
* Restores the previous browser session using a fast, lightweight strategy
* @param aNormalRestore True for a normal session restore; false for a crash
* restore.
* @param aSessionString The session string to restore from. If null, the
* backup session file is read from.
*/
void restoreLastSession(in boolean aNormalRestore, in AString aSessionString);
void restoreLastSession(in AString aSessionString);
};

View File

@ -44,7 +44,6 @@ SessionStore.prototype = {
_lastSaveTime: 0,
_interval: 10000,
_maxTabsUndo: 1,
_shouldRestore: false,
init: function ss_init() {
// Get file references
@ -57,12 +56,6 @@ SessionStore.prototype = {
this._interval = Services.prefs.getIntPref("browser.sessionstore.interval");
this._maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
// Do we need to restore session just this once, in case of a restart?
if (this._sessionFileBackup.exists() && Services.prefs.getBoolPref("browser.sessionstore.resume_session_once")) {
Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", false);
this._shouldRestore = true;
}
},
_clearDisk: function ss_clearDisk() {
@ -132,26 +125,9 @@ SessionStore.prototype = {
this._loadState = STATE_QUITTING;
break;
case "quit-application":
// If we are restarting, lets restore the tabs
if (aData == "restart") {
Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
// Ignore purges when restarting. The notification is fired after "quit-application".
Services.obs.removeObserver(this, "browser:purge-session-history");
}
// Freeze the data at what we've got (ignoring closing windows)
this._loadState = STATE_QUITTING;
// Move this session to sessionstore.bak so that:
// 1) we can get "tabs from last time" from sessionstore.bak
// 2) if sessionstore.js exists on next start, we know we crashed
OS.File.move(this._sessionFile.path, this._sessionFileBackup.path).then(null, function onError(reason) {
if (!(reason instanceof OS.File.Error && reason.becauseNoSuchFile)) {
Cu.reportError("Error moving sessionstore files: " + reason);
}
});
observerService.removeObserver(this, "domwindowopened");
observerService.removeObserver(this, "domwindowclosed");
observerService.removeObserver(this, "browser-lastwindow-close-granted");
@ -217,13 +193,7 @@ SessionStore.prototype = {
// Do a restore, triggered by Java
let data = JSON.parse(aData);
this.restoreLastSession(data.normalRestore, data.sessionString);
} else if (this._shouldRestore) {
// Do a restore triggered by Gecko (e.g., if
// browser.sessionstore.resume_session_once is true). In these cases,
// our Java front-end doesn't know we're doing a restore, so it has
// already opened an about:home tab.
this.restoreLastSession(false, null);
this.restoreLastSession(data.sessionString);
} else {
// Not doing a restore; just send restore message
Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
@ -939,11 +909,7 @@ SessionStore.prototype = {
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
},
shouldRestore: function ss_shouldRestore() {
return this._shouldRestore;
},
restoreLastSession: function ss_restoreLastSession(aNormalRestore, aSessionString) {
restoreLastSession: function ss_restoreLastSession(aSessionString) {
let self = this;
function restoreWindow(data) {
@ -959,31 +925,6 @@ SessionStore.prototype = {
}
try {
if (!aNormalRestore && !this._shouldRestore) {
// If we're here, it means we're restoring from a crash. Check prefs
// and other conditions to make sure we want to continue with the
// restore.
// TODO: Since the tabs have already been created as stubs after
// crashing, it's too late to try to abort the restore here. This logic
// should be moved to Java; see bug 889722.
// Disable crash recovery if it has been turned off.
if (!Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash")) {
throw "Restore is disabled via prefs";
}
// Check to see if we've exceeded the maximum number of crashes to
// avoid a crash loop
let maxCrashes = Services.prefs.getIntPref("browser.sessionstore.max_resumed_crashes");
let recentCrashes = Services.prefs.getIntPref("browser.sessionstore.recent_crashes") + 1;
Services.prefs.setIntPref("browser.sessionstore.recent_crashes", recentCrashes);
Services.prefs.savePrefFile(null);
if (recentCrashes > maxCrashes) {
throw "Exceeded maximum number of allowed restores";
}
}
// Normally, we'll receive the session string from Java, but there are
// cases where we may want to restore that Java cannot detect (e.g., if
// browser.sessionstore.resume_session_once is true). In these cases, the