summaryrefslogtreecommitdiffstats
path: root/browser/components/sessionstore/test/browser_remoteness_flip_on_restore.js
blob: 8674664ede6813c50a25e349e3806cfe50cb1b95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
"use strict";

/**
 * This set of tests checks that the remoteness is properly
 * set for each browser in a window when that window has
 * session state loaded into it.
 */

/**
 * Takes a SessionStore window state object for a single
 * window, sets the selected tab for it, and then returns
 * the object to be passed to SessionStore.setWindowState.
 *
 * @param state (object)
 *        The state to prepare to be sent to a window. This is
 *        state should just be for a single window.
 * @param selected (int)
 *        The 1-based index of the selected tab. Note that
 *        If this is 0, then the selected tab will not change
 *        from what's already selected in the window that we're
 *        sending state to.
 * @returns (object)
 *        The JSON encoded string to call
 *        SessionStore.setWindowState with.
 */
function prepareState(state, selected) {
  // We'll create a copy so that we don't accidentally
  // modify the caller's selected property.
  let copy = {};
  Object.assign(copy, state);
  copy.selected = selected;

  return {
    windows: [copy],
  };
}

const SIMPLE_STATE = {
  tabs: [
    {
      entries: [
        {
          url: "http://example.com/",
          triggeringPrincipal_base64,
          title: "title",
        },
      ],
    },
    {
      entries: [
        {
          url: "http://example.com/",
          triggeringPrincipal_base64,
          title: "title",
        },
      ],
    },
    {
      entries: [
        {
          url: "http://example.com/",
          triggeringPrincipal_base64,
          title: "title",
        },
      ],
    },
  ],
  title: "",
  _closedTabs: [],
};

const PINNED_STATE = {
  tabs: [
    {
      entries: [
        {
          url: "http://example.com/",
          triggeringPrincipal_base64,
          title: "title",
        },
      ],
      pinned: true,
    },
    {
      entries: [
        {
          url: "http://example.com/",
          triggeringPrincipal_base64,
          title: "title",
        },
      ],
      pinned: true,
    },
    {
      entries: [
        {
          url: "http://example.com/",
          triggeringPrincipal_base64,
          title: "title",
        },
      ],
    },
  ],
  title: "",
  _closedTabs: [],
};

/**
 * This is where most of the action is happening. This function takes
 * an Array of "test scenario" Objects and runs them. For each scenario, a
 * window is opened, put into some state, and then a new state is
 * loaded into that window. We then check to make sure that the
 * right things have happened in that window wrt remoteness flips.
 *
 * The schema for a testing scenario Object is as follows:
 *
 * initialRemoteness:
 *   an Array that represents the starting window. Each bool
 *   in the Array represents the window tabs in order. A "true"
 *   indicates that that tab should be remote. "false" if the tab
 *   should be non-remote.
 *
 * initialSelectedTab:
 *   The 1-based index of the tab that we want to select for the
 *   restored window. This is 1-based to avoid confusion with the
 *   selectedTab property described down below, though you probably
 *   want to set this to be greater than 0, since the initial window
 *   needs to have a defined initial selected tab. Because of this,
 *   the test will throw if initialSelectedTab is 0.
 *
 * stateToRestore:
 *   A JS Object for the state to send down to the window.
 *
 * selectedTab:
 *   The 1-based index of the tab that we want to select for the
 *   restored window. Leave this at 0 if you don't want to change
 *   the selection from the initial window state.
 *
 * expectedRemoteness:
 *   an Array that represents the window that we end up with after
 *   restoring state. Each bool in the Array represents the window
 *   tabs in order. A "true" indicates that the tab be remote, and
 *   a "false" indicates that the tab should be "non-remote". We
 *   need this Array in order to test pinned tabs which will also
 *   be loaded by default, and therefore should end up remote.
 *
 */
async function runScenarios(scenarios) {
  for (let [scenarioIndex, scenario] of scenarios.entries()) {
    info("Running scenario " + scenarioIndex);
    Assert.ok(
      scenario.initialSelectedTab > 0,
      "You must define an initially selected tab"
    );

    // First, we need to create the initial conditions, so we
    // open a new window to put into our starting state...
    let win = await BrowserTestUtils.openNewBrowserWindow();
    let tabbrowser = win.gBrowser;
    Assert.ok(
      tabbrowser.selectedBrowser.isRemoteBrowser,
      "The initial browser should be remote."
    );
    // Now put the window into the expected initial state.
    for (let i = 0; i < scenario.initialRemoteness.length; ++i) {
      let tab;
      if (i > 0) {
        // The window starts with one tab, so we need to create
        // any of the additional ones required by this test.
        info("Opening a new tab");
        tab = await BrowserTestUtils.openNewForegroundTab(tabbrowser);
      } else {
        info("Using the selected tab");
        tab = tabbrowser.selectedTab;
      }
      let browser = tab.linkedBrowser;
      let remotenessState = scenario.initialRemoteness[i]
        ? E10SUtils.DEFAULT_REMOTE_TYPE
        : E10SUtils.NOT_REMOTE;
      tabbrowser.updateBrowserRemoteness(browser, {
        remoteType: remotenessState,
      });
    }

    // And select the requested tab.
    let tabToSelect = tabbrowser.tabs[scenario.initialSelectedTab - 1];
    if (tabbrowser.selectedTab != tabToSelect) {
      await BrowserTestUtils.switchTab(tabbrowser, tabToSelect);
    }

    // Okay, time to test!
    let state = prepareState(scenario.stateToRestore, scenario.selectedTab);

    await setWindowState(win, state, true);

    for (let i = 0; i < scenario.expectedRemoteness.length; ++i) {
      let expectedRemoteness = scenario.expectedRemoteness[i];
      let tab = tabbrowser.tabs[i];

      Assert.equal(
        tab.linkedBrowser.isRemoteBrowser,
        expectedRemoteness,
        "Should have gotten the expected remoteness " +
          `for the tab at index ${i}`
      );
    }

    await BrowserTestUtils.closeWindow(win);
  }
}

/**
 * Tests that if we restore state to browser windows with
 * a variety of initial remoteness states. For this particular
 * set of tests, we assume that tabs are restoring on demand.
 */
add_task(async function () {
  // This test opens and closes windows, which might bog down
  // a debug build long enough to time out the test, so we
  // extend the tolerance on timeouts.
  requestLongerTimeout(5);

  await SpecialPowers.pushPrefEnv({
    set: [["browser.sessionstore.restore_on_demand", true]],
  });

  const TEST_SCENARIOS = [
    // Only one tab in the new window, and it's remote. This
    // is the common case, since this is how restoration occurs
    // when the restored window is being opened.
    {
      initialRemoteness: [true],
      initialSelectedTab: 1,
      stateToRestore: SIMPLE_STATE,
      selectedTab: 3,
      // All tabs should now be remote.
      expectedRemoteness: [true, true, true],
    },

    // A single remote tab, and this is the one that's going
    // to be selected once state is restored.
    {
      initialRemoteness: [true],
      initialSelectedTab: 1,
      stateToRestore: SIMPLE_STATE,
      selectedTab: 1,
      // All tabs should now be remote.
      expectedRemoteness: [true, true, true],
    },

    // A single remote tab which starts selected. We set the
    // selectedTab to 0 which is equivalent to "don't change
    // the tab selection in the window".
    {
      initialRemoteness: [true],
      initialSelectedTab: 1,
      stateToRestore: SIMPLE_STATE,
      selectedTab: 0,
      // All tabs should now be remote.
      expectedRemoteness: [true, true, true],
    },

    // An initially remote tab, but we're going to load
    // some pinned tabs now, and the pinned tabs should load
    // right away.
    {
      initialRemoteness: [true],
      initialSelectedTab: 1,
      stateToRestore: PINNED_STATE,
      selectedTab: 3,
      // Both pinned tabs and the selected tabs should all
      // end up being remote.
      expectedRemoteness: [true, true, true],
    },

    // A single non-remote tab.
    {
      initialRemoteness: [false],
      initialSelectedTab: 1,
      stateToRestore: SIMPLE_STATE,
      selectedTab: 2,
      // All tabs should now be remote.
      expectedRemoteness: [true, true, true],
    },

    // A mixture of remote and non-remote tabs.
    {
      initialRemoteness: [true, false, true],
      initialSelectedTab: 1,
      stateToRestore: SIMPLE_STATE,
      selectedTab: 3,
      // All tabs should now be remote.
      expectedRemoteness: [true, true, true],
    },

    // An initially non-remote tab, but we're going to load
    // some pinned tabs now, and the pinned tabs should load
    // right away.
    {
      initialRemoteness: [false],
      initialSelectedTab: 1,
      stateToRestore: PINNED_STATE,
      selectedTab: 3,
      // All tabs should now be remote.
      expectedRemoteness: [true, true, true],
    },
  ];

  await runScenarios(TEST_SCENARIOS);
});