Bug 1450559 - Remove nsISessionStore in favor of SessionStore.jsm. r=mikedeboer

MozReview-Commit-ID: 8spvIOus9ai

--HG--
rename : browser/components/sessionstore/nsSessionStore.manifest => browser/components/sessionstore/nsSessionStartup.manifest
extra : rebase_source : fc353af87bd620b7039d3e6241cc60c3788e3da6
This commit is contained in:
Dão Gottwald 2018-04-05 16:30:48 +02:00
parent a0259c665b
commit bf9d434a13
20 changed files with 35 additions and 310 deletions

View File

@ -3240,9 +3240,7 @@ function getMeOutOfHere() {
* that has certificate errors, we can get them somewhere safe.
*/
function goBackFromErrorPage() {
const ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
let state = JSON.parse(ss.getTabState(gBrowser.selectedTab));
let state = JSON.parse(SessionStore.getTabState(gBrowser.selectedTab));
if (state.index == 1) {
// If the unsafe page is the first or the only one in history, go to the
// start page.

View File

@ -12,7 +12,6 @@ const BAD_CERT = "https://expired.example.com/";
const UNKNOWN_ISSUER = "https://self-signed.example.com ";
const BAD_STS_CERT = "https://badchain.include-subdomains.pinning.example.com:443";
const {TabStateFlusher} = ChromeUtils.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
function injectErrorPageFrame(tab, src) {
return ContentTask.spawn(tab.linkedBrowser, {frameSrc: src}, async function({frameSrc}) {
@ -61,7 +60,7 @@ add_task(async function checkReturnToAboutHome() {
// Populate the shistory entries manually, since it happens asynchronously
// and the following tests will be too soon otherwise.
await TabStateFlusher.flush(browser);
let {entries} = JSON.parse(ss.getTabState(tab));
let {entries} = JSON.parse(SessionStore.getTabState(tab));
is(entries.length, 1, "there is one shistory entry");
info("Clicking the go back button on about:certerror");
@ -113,7 +112,7 @@ add_task(async function checkReturnToPreviousPage() {
// Populate the shistory entries manually, since it happens asynchronously
// and the following tests will be too soon otherwise.
await TabStateFlusher.flush(browser);
let {entries} = JSON.parse(ss.getTabState(tab));
let {entries} = JSON.parse(SessionStore.getTabState(tab));
is(entries.length, 2, "there are two shistory entries");
info("Clicking the go back button on about:certerror");

View File

@ -8,8 +8,6 @@ Services.prefs.setIntPref("security.tls.version.max", 3);
Services.prefs.setIntPref("security.tls.version.min", 3);
const LOW_TLS_VERSION = "https://tls1.example.com/";
const {TabStateFlusher} = ChromeUtils.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
add_task(async function checkReturnToPreviousPage() {
info("Loading a TLS page that isn't supported, ensure we have a fix button and clicking it then loads the page");

View File

@ -170,8 +170,7 @@ async function test_mute_tab(tab, icon, expectMuted) {
}
function get_tab_state(tab) {
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
return JSON.parse(ss.getTabState(tab));
return JSON.parse(SessionStore.getTabState(tab));
}
async function test_muting_using_menu(tab, expectMuted) {

View File

@ -23,8 +23,7 @@ const restartTab = async function(tab) {
};
function get_tab_state(tab) {
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
return JSON.parse(ss.getTabState(tab));
return JSON.parse(SessionStore.getTabState(tab));
}
add_task(async function() {

View File

@ -1,5 +1,3 @@
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
const {Utils} = ChromeUtils.import("resource://gre/modules/sessionstore/Utils.jsm", {});
const triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL;
@ -68,7 +66,7 @@ add_task(async function() {
let tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:blank");
// Fake a post-crash tab
ss.setTabState(tab, JSON.stringify(TAB_STATE));
SessionStore.setTabState(tab, JSON.stringify(TAB_STATE));
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
let doc = tab.linkedBrowser.contentDocument;

View File

@ -2334,6 +2334,9 @@ var SessionStoreInternal = {
},
getTabState: function ssi_getTabState(aTab) {
if (!aTab || !aTab.ownerGlobal) {
throw Components.Exception("Need a valid tab", Cr.NS_ERROR_INVALID_ARG);
}
if (!aTab.ownerGlobal.__SSi) {
throw Components.Exception("Default view is not tracked", Cr.NS_ERROR_INVALID_ARG);
}
@ -2360,7 +2363,7 @@ var SessionStoreInternal = {
}
let window = aTab.ownerGlobal;
if (!("__SSi" in window)) {
if (!window || !("__SSi" in window)) {
throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
}
@ -2375,6 +2378,9 @@ var SessionStoreInternal = {
},
duplicateTab: function ssi_duplicateTab(aWindow, aTab, aDelta = 0, aRestoreImmediately = true) {
if (!aTab || !aTab.ownerGlobal) {
throw Components.Exception("Need a valid tab", Cr.NS_ERROR_INVALID_ARG);
}
if (!aTab.ownerGlobal.__SSi) {
throw Components.Exception("Default view is not tracked", Cr.NS_ERROR_INVALID_ARG);
}

View File

@ -8,6 +8,8 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
var gStateObject;
var gTreeData;
@ -151,12 +153,11 @@ function restoreSession() {
}
var stateString = JSON.stringify(gStateObject);
var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
var top = getBrowserWindow();
// if there's only this page open, reuse the window for restoring the session
if (top.gBrowser.tabs.length == 1) {
ss.setWindowState(top, stateString, true);
SessionStore.setWindowState(top, stateString, true);
return;
}
@ -169,7 +170,7 @@ function restoreSession() {
}
Services.obs.removeObserver(observe, topic);
ss.setWindowState(newWindow, stateString, true);
SessionStore.setWindowState(newWindow, stateString, true);
var tabbrowser = top.gBrowser;
var tabIndex = tabbrowser.getBrowserIndexForDocument(document);
@ -268,12 +269,11 @@ function restoreSingleTab(aIx, aShifted) {
var newTab = tabbrowser.addTab();
var item = gTreeData[aIx];
var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
var tabState = gStateObject.windows[item.parent.ix]
.tabs[aIx - gTreeData.indexOf(item.parent) - 1];
// ensure tab would be visible on the tabstrip.
tabState.hidden = false;
ss.setTabState(newTab, JSON.stringify(tabState));
SessionStore.setTabState(newTab, JSON.stringify(tabState));
// respect the preference as to whether to select the tab (the Shift key inverses)
if (Services.prefs.getBoolPref("browser.tabs.loadInBackground") != !aShifted)

View File

@ -11,7 +11,6 @@ JAR_MANIFESTS += ['jar.mn']
XPIDL_SOURCES += [
'nsISessionStartup.idl',
'nsISessionStore.idl',
'nsISessionStoreUtils.idl',
]
@ -19,8 +18,7 @@ XPIDL_MODULE = 'sessionstore'
EXTRA_COMPONENTS += [
'nsSessionStartup.js',
'nsSessionStore.js',
'nsSessionStore.manifest',
'nsSessionStartup.manifest',
]
EXTRA_JS_MODULES.sessionstore = [

View File

@ -1,220 +0,0 @@
/* 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/. */
#include "nsISupports.idl"
interface nsIDOMWindow;
interface nsIDOMNode;
/**
* nsISessionStore keeps track of the current browsing state - i.e.
* tab history, cookies, scroll state, form data, and window features
* - and allows to restore everything into one browser window.
*
* The nsISessionStore API operates mostly on browser windows and the tabbrowser
* tabs contained in them:
*
* * "Browser windows" are those DOM windows having loaded
* chrome://browser/content/browser.xul . From overlays you can just pass the
* global |window| object to the API, though (or |top| from a sidebar).
* From elsewhere you can get browser windows through the nsIWindowMediator
* by looking for "navigator:browser" windows.
*
* * "Tabbrowser tabs" are all the child nodes of a browser window's
* |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|.
*/
[scriptable, uuid(4580f5eb-693d-423d-b0ce-2cb20a962e4d)]
interface nsISessionStore : nsISupports
{
/**
* Is it possible to restore the previous session. Will always be false when
* in Private Browsing mode.
*/
attribute boolean canRestoreLastSession;
/**
* Restore the previous session if possible. This will not overwrite the
* current session. Instead the previous session will be merged into the
* current session. Current windows will be reused if they were windows that
* pinned tabs were previously restored into. New windows will be opened as
* needed.
*
* Note: This will throw if there is no previous state to restore. Check with
* canRestoreLastSession first to avoid thrown errors.
*/
void restoreLastSession();
/**
* Get the current browsing state.
* @returns a JSON string representing the session state.
*/
AString getBrowserState();
/**
* Set the browsing state.
* This will immediately restore the state of the whole application to the state
* passed in, *replacing* the current session.
*
* @param aState is a JSON string representing the session state.
*/
void setBrowserState(in AString aState);
/**
* @param aWindow is the browser window whose state is to be returned.
*
* @returns a JSON string representing a session state with only one window.
*/
AString getWindowState(in nsIDOMWindow aWindow);
/**
* @param aWindow is the browser window whose state is to be set.
* @param aState is a JSON string representing a session state.
* @param aOverwrite boolean overwrite existing tabs
*/
void setWindowState(in nsIDOMWindow aWindow, in AString aState, in boolean aOverwrite);
/**
* @param aTab is the tabbrowser tab whose state is to be returned.
*
* @returns a JSON string representing the state of the tab
* (note: doesn't contain cookies - if you need them, use getWindowState instead).
*/
AString getTabState(in nsIDOMNode aTab);
/**
* @param aTab is the tabbrowser tab whose state is to be set.
* @param aState is a JSON string representing a session state.
*/
void setTabState(in nsIDOMNode aTab, in AString aState);
/**
* Duplicates a given tab as thoroughly as possible.
*
* @param aWindow is the browser window into which the tab will be duplicated.
* @param aTab is the tabbrowser tab to duplicate (can be from a different window).
* @param aDelta is the offset to the history entry to load in the duplicated tab.
* @returns a reference to the newly created tab.
*/
nsIDOMNode duplicateTab(in nsIDOMWindow aWindow, in nsIDOMNode aTab,
[optional] in long aDelta);
/**
* Get the number of restore-able tabs for a browser window
*/
unsigned long getClosedTabCount(in nsIDOMWindow aWindow);
/**
* Get closed tab data
*
* @param aWindow is the browser window for which to get closed tab data
* @returns a JSON string representing the list of closed tabs.
*/
AString getClosedTabData(in nsIDOMWindow aWindow);
/**
* @param aWindow is the browser window to reopen a closed tab in.
* @param aIndex is the index of the tab to be restored (FIFO ordered).
* @returns a reference to the reopened tab.
*/
nsIDOMNode undoCloseTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
/**
* @param aWindow is the browser window associated with the closed tab.
* @param aIndex is the index of the closed tab to be removed (FIFO ordered).
*/
nsIDOMNode forgetClosedTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
/**
* Get the number of restore-able windows
*/
unsigned long getClosedWindowCount();
/**
* Get closed windows data
*
* @returns a JSON string representing the list of closed windows.
*/
AString getClosedWindowData();
/**
* @param aIndex is the index of the windows to be restored (FIFO ordered).
* @returns the nsIDOMWindow object of the reopened window
*/
nsIDOMWindow undoCloseWindow(in unsigned long aIndex);
/**
* @param aIndex is the index of the closed window to be removed (FIFO ordered).
*
* @throws NS_ERROR_INVALID_ARG
* when aIndex does not map to a closed window
*/
nsIDOMNode forgetClosedWindow(in unsigned long aIndex);
/**
* @param aWindow is the window to get the value for.
* @param aKey is the value's name.
*
* @returns A string value or an empty string if none is set.
*/
AString getWindowValue(in nsIDOMWindow aWindow, in AString aKey);
/**
* @param aWindow is the browser window to set the value for.
* @param aKey is the value's name.
* @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects).
*/
void setWindowValue(in nsIDOMWindow aWindow, in AString aKey, in jsval aStringValue);
/**
* @param aWindow is the browser window to get the value for.
* @param aKey is the value's name.
*/
void deleteWindowValue(in nsIDOMWindow aWindow, in AString aKey);
/**
* @param aTab is the tabbrowser tab to get the value for.
* @param aKey is the value's name.
*
* @returns A string value or an empty string if none is set.
*/
AString getCustomTabValue(in nsIDOMNode aTab, in AString aKey);
/**
* @param aTab is the tabbrowser tab to set the value for.
* @param aKey is the value's name.
* @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects).
*/
void setCustomTabValue(in nsIDOMNode aTab, in AString aKey, in jsval aStringValue);
/**
* @param aTab is the tabbrowser tab to get the value for.
* @param aKey is the value's name.
*/
void deleteCustomTabValue(in nsIDOMNode aTab, in AString aKey);
/**
* @param aKey is the value's name.
*
* @returns A string value or an empty string if none is set.
*/
AString getGlobalValue(in AString aKey);
/**
* @param aKey is the value's name.
* @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects).
*/
void setGlobalValue(in AString aKey, in jsval aStringValue);
/**
* @param aTab is the browser tab to get the value for.
* @param aKey is the value's name.
*/
void deleteGlobalValue(in AString aKey);
/**
* @param aName is the name of the attribute to save/restore for all tabbrowser tabs.
*/
void persistTabAttribute(in AString aName);
};

View File

@ -5,8 +5,6 @@
# browser: {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
component {5280606b-2510-4fe0-97ef-9b5a22eafe6b} nsSessionStore.js
contract @mozilla.org/browser/sessionstore;1 {5280606b-2510-4fe0-97ef-9b5a22eafe6b}
component {ec7a6c20-e081-11da-8ad9-0800200c9a66} nsSessionStartup.js
contract @mozilla.org/browser/sessionstartup;1 {ec7a6c20-e081-11da-8ad9-0800200c9a66}
category app-startup nsSessionStartup service,@mozilla.org/browser/sessionstartup;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110}

View File

@ -1,36 +0,0 @@
/* 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/. */
"use strict";
/**
* Session Storage and Restoration
*
* Overview
* This service keeps track of a user's session, storing the various bits
* required to return the browser to its current state. The relevant data is
* stored in memory, and is periodically saved to disk in a file in the
* profile directory. The service is started at first window load, in
* delayedStartup, and will restore the session from the data received from
* the nsSessionStartup service.
*/
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource:///modules/sessionstore/SessionStore.jsm");
function SessionStoreService() {}
// The SessionStore module's object is frozen. We need to modify our prototype
// and add some properties so let's just copy the SessionStore object.
Object.keys(SessionStore).forEach(function(aName) {
let desc = Object.getOwnPropertyDescriptor(SessionStore, aName);
Object.defineProperty(SessionStoreService.prototype, aName, desc);
});
SessionStoreService.prototype.classID =
Components.ID("{5280606b-2510-4fe0-97ef-9b5a22eafe6b}");
SessionStoreService.prototype.QueryInterface =
XPCOMUtils.generateQI([Ci.nsISessionStore]);
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStoreService]);

View File

@ -13,16 +13,16 @@ function test() {
/NS_ERROR_ILLEGAL_VALUE/,
"Invalid window for setWindowState throws");
Assert.throws(() => ss.getTabState({}),
/NS_ERROR_FAILURE/,
/NS_ERROR_ILLEGAL_VALUE/,
"Invalid tab for getTabState throws");
Assert.throws(() => ss.setTabState({}, "{}"),
/NS_ERROR_FAILURE/,
/NS_ERROR_ILLEGAL_VALUE/,
"Invalid tab state for setTabState throws");
Assert.throws(() => ss.setTabState({}, JSON.stringify({ entries: [] })),
/NS_ERROR_FAILURE/,
/NS_ERROR_ILLEGAL_VALUE/,
"Invalid tab for setTabState throws");
Assert.throws(() => ss.duplicateTab({}, {}),
/NS_ERROR_FAILURE/,
/NS_ERROR_ILLEGAL_VALUE/,
"Invalid tab for duplicateTab throws");
Assert.throws(() => ss.duplicateTab({}, gBrowser.selectedTab),
/NS_ERROR_ILLEGAL_VALUE/,

View File

@ -2,7 +2,7 @@
* 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/. */
// Mirrors WINDOW_ATTRIBUTES IN nsSessionStore.js
// Mirrors WINDOW_ATTRIBUTES IN SessionStore.jsm
const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"];
var stateBackup = ss.getBrowserState();
@ -38,7 +38,7 @@ function checkOSX34Generator(num) {
// should be in aCurState. So let's shape our expectations.
let expectedState = JSON.parse(aPreviousState);
expectedState[0].tabs.shift();
// size attributes are stripped out in _prepDataForDeferredRestore in nsSessionStore.
// size attributes are stripped out in _prepDataForDeferredRestore in SessionStore.jsm.
// This isn't the best approach, but neither is comparing JSON strings
WINDOW_ATTRIBUTES.forEach(attr => delete expectedState[0][attr]);

View File

@ -102,7 +102,6 @@ add_task(async function test() {
"work",
];
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
const { TabStateFlusher } = ChromeUtils.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
// Make sure userContext is enabled.
@ -133,7 +132,7 @@ add_task(async function test() {
gBrowser.removeTab(tab);
}
let state = JSON.parse(ss.getBrowserState());
let state = JSON.parse(SessionStore.getBrowserState());
is(state.cookies.length, USER_CONTEXTS.length,
"session restore should have each container's cookie");
});

View File

@ -25,13 +25,11 @@ registerCleanupFunction(() => {
}
});
const {SessionStore} = ChromeUtils.import("resource:///modules/sessionstore/SessionStore.jsm", {});
const {SessionSaver} = ChromeUtils.import("resource:///modules/sessionstore/SessionSaver.jsm", {});
const {SessionFile} = ChromeUtils.import("resource:///modules/sessionstore/SessionFile.jsm", {});
const {TabState} = ChromeUtils.import("resource:///modules/sessionstore/TabState.jsm", {});
const {TabStateFlusher} = ChromeUtils.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
const ss = SessionStore;
// Some tests here assume that all restored tabs are loaded without waiting for
// the user to bring them to the foreground. We ensure this by resetting the

View File

@ -246,9 +246,8 @@
@RESPATH@/components/ProcessSingleton.manifest
@RESPATH@/components/MainProcessSingleton.js
@RESPATH@/components/ContentProcessSingleton.js
@RESPATH@/browser/components/nsSessionStore.manifest
@RESPATH@/browser/components/nsSessionStartup.manifest
@RESPATH@/browser/components/nsSessionStartup.js
@RESPATH@/browser/components/nsSessionStore.js
@RESPATH@/components/nsURLFormatter.manifest
@RESPATH@/components/nsURLFormatter.js
@RESPATH@/components/txEXSLTRegExFunctions.manifest

View File

@ -17,6 +17,8 @@ ChromeUtils.defineModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
// Url to fetch snippets, in the urlFormatter service format.
const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
@ -108,10 +110,8 @@ var AboutHome = {
switch (aMessage.name) {
case "AboutHome:RestorePreviousSession":
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
if (ss.canRestoreLastSession) {
ss.restoreLastSession();
if (SessionStore.canRestoreLastSession) {
SessionStore.restoreLastSession();
}
break;
@ -156,14 +156,9 @@ var AboutHome = {
// Send all the chrome-privileged data needed by about:home. This
// gets re-sent when the search engine changes.
sendAboutHomeData(target) {
let wrapper = {};
ChromeUtils.import("resource:///modules/sessionstore/SessionStore.jsm",
wrapper);
let ss = wrapper.SessionStore;
ss.promiseInitialized.then(function() {
SessionStore.promiseInitialized.then(function() {
let data = {
showRestoreLastSession: ss.canRestoreLastSession,
showRestoreLastSession: SessionStore.canRestoreLastSession,
snippetsURL: AboutHomeUtils.snippetsURL,
showKnowYourRights: AboutHomeUtils.showKnowYourRights,
snippetsVersion: AboutHomeUtils.snippetsVersion,

View File

@ -10,8 +10,6 @@ const CONTAINER_URL = "chrome://devtools/content/responsive.html/index.xhtml";
const { TabStateFlusher } =
ChromeUtils.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
const SessionStore =
Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
const { OUTER_FRAME_LOADER_SYMBOL } =
require("devtools/client/responsive.html/browser/tunnel");

View File

@ -4,7 +4,6 @@
const {Utils} = ChromeUtils.import("resource://gre/modules/sessionstore/Utils.jsm", {});
const triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL;
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
const testState = {
windows: [{
@ -37,7 +36,7 @@ function test() {
Services.ww.registerNotification(windowObserver);
ss.setBrowserState(JSON.stringify(testState));
SessionStore.setBrowserState(JSON.stringify(testState));
}
function windowObserver(subject, topic, data) {