Bug 1171708 - [e10s] Stop using CPOWs for window closing
TabState.flushWindow is sending down a sync CPOW message.

  1. Collect parent process info right away when window is closing (collect window data - TabState.collect)
billm added code that makes is to that the frameloaders are kept alive until the last message gets sent up

Be careful not to remove the message listeners until the last messages are sent up.

Ohhhhhh man - it’s possible to pass a third argument to addMessageListener on a frameloader that makes it so that we can hear about messages from a frameloader that has GONE AWAY.

Very interesting.

So here’s the challenge:

onClose collects a bunch of things from the window that is closing. Some of that stuff also involves collecting the individual tab states for each browser tab. Since that’s now asynchronous, we have to return to the event loop and the frameloaders are kept alive until the last tab state update comes up and the TabStateFlusher promise gets resolved.

Here’s the kicker though - by the time that occurs, the DOM for the window has disappeared. So any information gathering that relies on the DOM from the window has to occur _before_ the async stuff gets kicked off, and then sewn together.

Things that need to be collected or run before DOM goes away:

_collectWindowData
The winData.title

Things that need to be collected from content asynchronously:
The state for each tab in the window

So here’s the problem - _collectWindowData relies on DOM stuff, and needs to be run before the DOM for the window goes away. One of the things it does, however, is to update the “winData” stashed in this._windows with tabData… but that tabData is likely out of date at that point.

Ideas:

Collect tabData twice
Collect the window state without flushing, with the understanding that the tabData might be out of date. Then, flush the tabs asynchronously. When the promise resolves, update the tabData for each window. Finally then detach the message listeners and add the window to the DyingWindowCache.

Pros: Straight-forward solution that doesn’t look like it'll rock the boat too much with what SessionStore does in other code-paths.

Cons: Some redundancy - I end up collecting the tabData once, and then again once the flush is complete.

Change _collectWindowData

Collect the sync window data only, and perhaps have some kind of Promise-y

This seems risky. I’d have to audit all of the consumers of this function to make sure they’re still going to behave correctly after this change. And then the consumers of those functions. It sounds like a fragile strategy.

It also doesn’t look like any effort has gone into making sure that _collectWindowData returns the most up-to-date information, so making this Promise-y and async doesn’t sound like the right choice.

Write _collectWindowDataAsync

Write an alternative _collectWindowData function that will do all of the DOM related collection first, and then do the flush before getting the tabData. This gets rid of the redundancy if we collect the tabData twice, but then we have the redundancy of having two _collectWindowData methods.


What is the DyingWindowCache?
"// A map storing a closed window's state data until it goes aways (is GC'ed).
// This ensures that API clients can still read (but not write) states of
// windows they still hold a reference to but we don’t."

Translation : a WeakMap for consumers of this window data to make sure they can still read window data while they still hold a reference.

Problem: The update message after the flush seems to be pretty unreliable. It doesn’t look as if we’re holding the frameloaders alive until those messages come up.




GUHHH.. A new problem.

It looks like, at least for groupMessageManagers… we’re not dispatching the SessionStore:update to the listener, because… because it looks like the listener is gone? It’s kinda hard to tell, since there are several message managers and they all look the same. :/

Plan: Add an optional “name” for group message managers that I can see while debugging - and that way, in the handlers for SessionStore:update, I can see whether or not the message managers that deal with it have that name. If so, then I need to somehow figure out when that message manager gets its listeners cleared!

Let’s see if I can prove this with a regression test.

The code to keep sending messages after the frameloader has been removed from the DOM is here: https://bugzilla.mozilla.org/show_bug.cgi?id=1126089

dom/base/test/browser_messagemanager_unload.js is where the original testing for that stuff went.

Fuuuuuuuuu - … if I modify that test to use the group message manager, things be busted. I think I need a better understanding the group message manager.

From https://dxr.mozilla.org/mozilla-central/rev/6077f51254c69a1e14e1b61acba4af451bf1783e/dom/base/nsIMessageManager.idl#50

*
*  Parent side                         Child side
* -------------                       ------------
*  global MMg
*   |
*   +-->window MMw1
*   |    |
*   |    +-->frame MMp1_1<------------>frame MMc1_1
*   |    |
*   |    +-->frame MMp1_2<------------>frame MMc1_2
*   |    |
*   |    +-->group MMgr1
*   |    |    |
*   |    |    +-->frame MMp2_1<------->frame MMc2_1
*   |    |    |
*   |    |    +-->frame MMp2_2<------->frame MMc2_2
*   |    |
*   |    +-->group MMgr2
*   |    |    ...
*   |    |
*   |    ...
*   |

Right, so that matches my mental model...

AH wait. I was wrong! The parent is receiving the message! The problem is that we’re bailing out at this chunk of code:

case "SessionStore:update" :
// |browser.frameLoader| might be empty if the browser was already
// destroyed and its tab removed. In that case we still have the last
// frameLoader we know about to compare.
let frameLoader = browser.frameLoader ||
this ._lastKnownFrameLoader.get(browser.permanentKey);

// If the message isn't targeting the latest frameLoader discard it.
if (frameLoader != aMessage.targetFrameLoader) {
return ;
}

So… so why is that the case?

Ah cripes

It looks like XULFrameLoaderCreated, when it’s fired, compares target.tagName with “browser”, which is fine with dynamically created browsers - but for the initial browser that’s defined in the markup, this will fail to match, since the tagname will be “xul:browser”. :(( We should use localname instead.

Ok, so that works.

Now I’m back where I wanted to be - I’ve got these flushings occurring… and I want to save the final message from the child. How will I do it?

For closed tabs, there’s this _closedTabs WeakMap on SessionStore that maps permanentKey’s to an Object containing tab information that can then be saved.

I should probably do something similar.

Gather the window information, and stash it in a _closedWindows WeakSet, keyed on windows (?).

That should map to an object with two things: window information we were able to gather synchronously, and a WeakSet of browsers, along with a count of how many :update’s we’re expecting?

As the :update’s come in, decrement the ...

OH WAIT, let me get this down before I call it a day.

We already collect this winData thing, and shove it in _closedWindows. Let’s map browser.permanentKey’s to each tab’s entry in the tabs part of that _closedWindows thing.

As the messages from each tab come in, update those entries, and queue up a write. I guess.

Okay! I think this kinda works! Maybe!

But now I’ve get test failures (non-e10s). Let’s take a look:

31 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_broadcast.js | Uncaught exception - at chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_broadcast.js:79 - TypeError: can't convert undefined to object

BLARGH. So I guess I do need to wait until the final messages come up. Shit.

So, here’s what I want.

I think I want something like TabStateFlusher.waitForFinalFlushes(aWindow)...

WAIT. No, I was understanding this wrong. I was sending the flush request down to the child, but before it could react to it, it sent the final update message up. I need to make my _closedWindowTabs thing handle that case.

Aaaaaand we’re passing!

363 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_cleaner.js | Uncaught exception - at chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_cleaner.js:80 - TypeError: state._closedWindows[0] is undefined
Stack trace:
test_open_and_close@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_cleaner.js:80:3
Handler.prototype.process@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:934:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:813:7
Promise*this.PromiseWalker.scheduleWalkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:744:11
this.PromiseWalker.schedulePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:776:7
Promise.prototype.then@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:451:5
Tester_execTest@chrome://mochikit/content/browser-test.js:757:9
Tester.prototype.nextTest</<@chrome://mochikit/content/browser-test.js:677:7
SimpleTest.waitForFocus/waitForFocusInner/focusedOrLoaded/<@chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:735:59
364 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_cleaner.js | Uncaught exception - [Exception... "Invalid index: not in the closed windows"  nsresult: "0x80070057 (NS_ERROR_ILLEGAL_VALUE)"  location: "JS frame :: resource:///modules/sessionstore/SessionStore.jsm :: ssi_undoCloseWindow :: line 2026"  data: no]
365 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_cleaner.js | Uncaught exception - at chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_cleaner.js:116 - TypeError: state._closedWindows[0] is undefined
Stack trace:
test_old_data@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_cleaner.js:116:3
Handler.prototype.process@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:937:21
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:813:7
Promise*this.PromiseWalker.scheduleWalkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:744:11
this.PromiseWalker.schedulePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:776:7
this.PromiseWalker.completePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:711:7
testScope/test_executeSoon/<.run@chrome://mochikit/content/browser-test.js:969:9
Tester_execTest@chrome://mochikit/content/browser-test.js:757:9
Tester.prototype.nextTest</<@chrome://mochikit/content/browser-test.js:677:7
SimpleTest.waitForFocus/waitForFocusInner/focusedOrLoaded/<@chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:735:59
366 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_cleaner.js | Uncaught exception - at chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_cleaner.js:140 - TypeError: state._closedWindows[0] is undefined
Stack trace:
test_cleanup@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_cleaner.js:140:3
Handler.prototype.process@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:937:21
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:813:7
Promise*this.PromiseWalker.scheduleWalkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:744:11
this.PromiseWalker.schedulePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:776:7
this.PromiseWalker.completePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:711:7
testScope/test_executeSoon/<.run@chrome://mochikit/content/browser-test.js:969:9
Tester_execTest@chrome://mochikit/content/browser-test.js:757:9
Tester.prototype.nextTest</<@chrome://mochikit/content/browser-test.js:677:7
SimpleTest.waitForFocus/waitForFocusInner/focusedOrLoaded/<@chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:735:59

367 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_dying_cache.js | sessionstore does no longer track our window -
Stack trace:
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_dying_cache.js:test:38
Tester_execTest@chrome://mochikit/content/browser-test.js:757:9
Tester.prototype.nextTest</<@chrome://mochikit/content/browser-test.js:677:7
SimpleTest.waitForFocus/waitForFocusInner/focusedOrLoaded/<@chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:735:59
368 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_dying_cache.js | Uncaught exception - [Exception... "[JavaScript Error: "tabs is undefined" {file: "resource:///modules/sessionstore/SessionStore.jsm" line: 2629}]'[JavaScript Error: "tabs is undefined" {file: "resource:///modules/sessionstore/SessionStore.jsm" line: 2629}]' when calling method: [nsISessionStore::getWindowState]"  nsresult: "0x80570021 (NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS)"  location: "JS frame :: chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_dying_cache.js :: checkWindowState :: line 49"  data: yes]
Stack trace:
checkWindowState@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_dying_cache.js:49:40
test@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_dying_cache.js:39:3
Tester_execTest@chrome://mochikit/content/browser-test.js:757:9
Tester.prototype.nextTest</<@chrome://mochikit/content/browser-test.js:677:7
SimpleTest.waitForFocus/waitForFocusInner/focusedOrLoaded/<@chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:735:59
Tester_execTest@chrome://mochikit/content/browser-test.js:757:9
Tester.prototype.nextTest</<@chrome://mochikit/content/browser-test.js:677:7
SimpleTest.waitForFocus/waitForFocusInner/focusedOrLoaded/<@chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:735:59

The following tests failed:

302 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_394759_perwindowpb.js | Check that the closed window count hasn't changed - Got 0, expected 1
Stack trace:
chrome://mochikit/content/browser-test.js:test_is:943
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_perwindowpb.js:promiseTestOnWindow/<:33
promiseTestOnWindow@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_perwindowpb.js:30:1
main@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_perwindowpb.js:50:9
TaskImpl_run@resource://gre/modules/Task.jsm:314:40
Handler.prototype.process@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:934:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:813:7
Promise*this.PromiseWalker.scheduleWalkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:744:11
this.PromiseWalker.schedulePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:776:7
this.PromiseWalker.completePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:711:7
obs@chrome://mochitests/content/browser/browser/components/sessionstore/test/head.js:460:9
303 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_394759_perwindowpb.js | Uncaught exception - at chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_perwindowpb.js:34 - TypeError: JSON.stringify(...) is undefined
Stack trace:
promiseTestOnWindow/<@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_perwindowpb.js:34:8
promiseTestOnWindow@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_perwindowpb.js:30:1
main@chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_perwindowpb.js:50:9
Handler.prototype.process@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:934:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:813:7
Promise*this.PromiseWalker.scheduleWalkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:744:11
this.PromiseWalker.schedulePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:776:7
Promise.prototype.then@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:451:5
Tester_execTest@chrome://mochikit/content/browser-test.js:757:9
Tester.prototype.nextTest</<@chrome://mochikit/content/browser-test.js:677:7
SimpleTest.waitForFocus/waitForFocusInner/focusedOrLoaded/<@chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:735:59
304 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_394759_perwindowpb.js | Found an unexpected browser window at the end of test run -

307 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_464199.js | The correct amout of tabs was removed - Got 11, expected 5
Stack trace:
chrome://mochikit/content/browser-test.js:test_is:943
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_464199.js:test/</<:73
308 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_464199.js | All tabs to be forgotten were indeed removed - Got 6, expected 0
Stack trace:
chrome://mochikit/content/browser-test.js:test_is:943
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_464199.js:test/</<:75
SUITE-END | took 21s

440 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_528776.js | number of open browser windows according to getBrowserState - Got 2, expected 1
Stack trace:
chrome://mochikit/content/browser-test.js:test_is:943
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_528776.js:browserWindowsCount:10
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_528776.js:test/<:23
resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:Handler.prototype.process:934
resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:this.PromiseWalker.walkerLoop:813
SUITE-END | took 126s

262 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_819510_perwindowpb.js | sessionstore state: 2 windows in data being written to disk - Got 3, expected 2
Stack trace:
chrome://mochikit/content/browser-test.js:test_is:943
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_819510_perwindowpb.js:test_3:96
Handler.prototype.process@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:934:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:813:7
Promise*this.PromiseWalker.scheduleWalkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:744:11
this.PromiseWalker.schedulePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:776:7
this.PromiseWalker.completePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:711:7
obs@chrome://mochitests/content/browser/browser/components/sessionstore/test/head.js:460:9
263 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_819510_perwindowpb.js | Selected window is updated to match one of the saved windows - Got 3, expected 2
Stack trace:
chrome://mochikit/content/browser-test.js:test_is:943
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_819510_perwindowpb.js:test_3:98
Handler.prototype.process@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:934:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:813:7
Promise*this.PromiseWalker.scheduleWalkerLoop@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:744:11
this.PromiseWalker.schedulePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:776:7
this.PromiseWalker.completePromise@resource://gre/modules/Promise.jsm -> resource://gre/modules/Promise-backend.js:711:7
obs@chrome://mochitests/content/browser/browser/components/sessionstore/test/head.js:460:9
SUITE-END | took 19s

300 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_394759_behavior.js | There were 3 popup windows to repoen - Got 0, expected 3
Stack trace:
chrome://mochikit/content/browser-test.js:test_is:943
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_behavior.js:openWindowRec:25
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_behavior.js:test/openWindowRec/onTestURLLoaded/tabReady/<:46
chrome://mochikit/content/browser-test.js:testScope/test_executeSoon/<.run:969
301 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_394759_behavior.js | There were 3 normal windows to repoen - Got 0, expected 3
Stack trace:
chrome://mochikit/content/browser-test.js:test_is:943
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_behavior.js:openWindowRec:27
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_behavior.js:test/openWindowRec/onTestURLLoaded/tabReady/<:46
chrome://mochikit/content/browser-test.js:testScope/test_executeSoon/<.run:969
305 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_394759_purge.js | oldState in test_purge has 2 windows instead of 1 -
Stack trace:
chrome://mochitests/content/browser/browser/components/sessionstore/test/browser_394759_purge.js:test:32
chrome://mochikit/content/browser-test.js:Tester_execTest:786
chrome://mochikit/content/browser-test.js:Tester.prototype.nextTest</<:677
chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:SimpleTest.waitForFocus/waitForFocusInner/focusedOrLoaded/<:735
306 INFO TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_394759_purge.js | Found an unexpected browser window at the end of test run -

Remove promiseWindowClosed
Rewrite browser/components/sessionstore/test/browser_394759_behavior.js
Rewrite browser/components/sessionstore/test/browser_394759_purge.js

https://treeherder.mozilla.org/#/jobs?repo=try&revision=3e9c5c2367e0

I _think_ I’ve got this working, and I _think_ I’ve also addressed billm’s feedback. billm was concerned that the decision over whether or not the window data should go into the _closedWindows dict / Object was being made too early before we’d finished flushing all tabs in the window. I think moving that logic into the TabStateFlusher.flushWindow callback has addressed this concern, although I’d really like to test it!

4:42 PM < & billm > mconley: ok. the other complication is that we still want to be able to restore the most recently closed window if not every tab has answered back yet. I think we just want to use whatever the lastest info we got back from the tabs was.
4:42 PM < mconley > also
4:42 PM < & billm > mconley: (I was thinking about this last night. it's kind of annoying.)
4:42 PM < mconley > oh
4:42 PM < mconley > billm: wait, that sounds like it goes against what you'd advised against earlier... is that right? Like, we want to inspect the tab data from the cache, and not wait until the tabs have all responded?
4:43 PM < mconley > just trying to make sure I understand
4:43 PM < & billm > mconley: we sort of want both...
4:43 PM < & billm > until the point where every tab has answered, we want to use the most recent data in the cache
4:43 PM < & billm > once they have all answered back, we want to use that data
4:43 PM < mconley > ah, I see
4:43 PM < & billm > does that make sense?
4:43 PM < mconley > yes

Ahhhh… interesting.

Okay, so we’ll check twice - once synchronously with the cached tab data, and then again after the flush is complete. In both cases, we can either move the closed window data in to _closedWindows or out again if it’s already in there.

Done.

And wrote a test!

Some feedback from billm now… and I’ve got a mostly-perma orange on e10s bc6. :/

TEST-UNEXPECTED-FAIL | browser/components/sessionstore/test/browser_354894_perwindowpb.js | Got right number of stored window states - Got 2, expected 1

I suspect this is because of some test changes he suggested. Let’s smoke out which one.

All changes reverted (tests should pass): https://treeherder.mozilla.org/#/jobs?repo=try&revision=211f7f82531f

browser_394759_behavior.js reverted: https://treeherder.mozilla.org/#/jobs?repo=try&revision=60116694aad4

browser_464199.js reverted: https://treeherder.mozilla.org/#/jobs?repo=try&revision=f9ae9d5b5686

browser_597071.js reverted: https://treeherder.mozilla.org/#/jobs?repo=try&revision=0f599151cb45

browser_broadcast.js reverted: https://treeherder.mozilla.org/#/jobs?repo=try&revision=c54626509c1a

browser_cleaner.js reverted (that’s all): https://treeherder.mozilla.org/#/jobs?repo=try&revision=af6a071029a7

Ok, replaced a lot of window.close stuff in sessionstore tests: https://treeherder.mozilla.org/#/jobs?repo=try&revision=c1013c4f0875

Hrm… still busted. Instrumentation build:

https://treeherder.mozilla.org/#/jobs?repo=try&revision=6ae3ba6128e2

Okay… so I think maybe BrowserTestUtils.closeWindow doesn’t give TabStateFlusher a chance to resolve first?

https://treeherder.mozilla.org/#/jobs?repo=try&revision=40c46e8c097c


WAT. Backed out. WTF.

Ah…. ah, I think I know what’s happening:

Maybe fixed?: https://treeherder.mozilla.org/#/jobs?repo=try&revision=066901e7c042

File a follow-up about the Forget feature maybe being broken. Bug 1225921
File a follow-up about the scroll position not syncing on window flush. Bug 1228381
File a follow-up to clean up for billm "
The design is cleaner than what we have for tab closing. We should have a follow-up to make the tab closing code look more like this code."
Filed bug 1228383