summaryrefslogtreecommitdiffstats
path: root/browser/components/sessionstore/test/browser_send_async_message_oom.js
blob: 6afd771db3f8f36420fec8163d6662cfa0f0afe3 (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});

const HISTOGRAM_NAME = "FX_SESSION_RESTORE_SEND_UPDATE_CAUSED_OOM";

/**
 * Test that an OOM in sendAsyncMessage in a framescript will be reported
 * to Telemetry.
 */

add_task(function* init() {
  Services.telemetry.canRecordExtended = true;
});

function frameScript() {
  // Make send[A]syncMessage("SessionStore:update", ...) simulate OOM.
  // Other operations are unaffected.
  let mm = docShell.sameTypeRootTreeItem.
    QueryInterface(Ci.nsIDocShell).
    QueryInterface(Ci.nsIInterfaceRequestor).
    getInterface(Ci.nsIContentFrameMessageManager);

  let wrap = function(original) {
    return function(name, ...args) {
      if (name != "SessionStore:update") {
        return original(name, ...args);
      }
      throw new Components.Exception("Simulated OOM", Cr.NS_ERROR_OUT_OF_MEMORY);
    }
  }

  mm.sendAsyncMessage = wrap(mm.sendAsyncMessage);
  mm.sendSyncMessage = wrap(mm.sendSyncMessage);
}

add_task(function*() {
  // Capture original state.
  let snapshot = Services.telemetry.getHistogramById(HISTOGRAM_NAME).snapshot();

  // Open a browser, configure it to cause OOM.
  let newTab = gBrowser.addTab("about:robots");
  let browser = newTab.linkedBrowser;
  yield ContentTask.spawn(browser, null, frameScript);


  let promiseReported = new Promise(resolve => {
    browser.messageManager.addMessageListener("SessionStore:error", resolve);
  });

  // Attempt to flush. This should fail.
  let promiseFlushed = TabStateFlusher.flush(browser);
  promiseFlushed.then((success) => {
    if (success) {
      throw new Error("Flush should have failed")
    }
  });

  // The frame script should report an error.
  yield promiseReported;

  // Give us some time to handle that error.
  yield new Promise(resolve => setTimeout(resolve, 10));

  // By now, Telemetry should have been updated.
  let snapshot2 = Services.telemetry.getHistogramById(HISTOGRAM_NAME).snapshot();
  gBrowser.removeTab(newTab);

  Assert.ok(snapshot2.sum > snapshot.sum);
});

add_task(function* cleanup() {
  Services.telemetry.canRecordExtended = false;
});