summaryrefslogtreecommitdiffstats
path: root/mobile/android/modules/geckoview/test/xpcshell/test_ChildCrashHandler.js
blob: 0e07937ed32345e0dc168b9cd82ad1ca075eaa36 (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
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
  ChildCrashHandler: "resource://gre/modules/ChildCrashHandler.sys.mjs",
  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
});

const { makeFakeAppDir } = ChromeUtils.importESModule(
  "resource://testing-common/AppData.sys.mjs"
);

add_setup(async function () {
  await makeFakeAppDir();
  // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables crash
  // reports. This test needs them enabled.
  const noReport = Services.env.get("MOZ_CRASHREPORTER_NO_REPORT");
  Services.env.set("MOZ_CRASHREPORTER_NO_REPORT", "");

  registerCleanupFunction(function () {
    Services.env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
  });
});

add_task(async function test_remoteType() {
  const childID = 123;
  const remoteType = "webIsolated=https://example.com";
  // Force-set a remote type for the process that we are going to "crash" next.
  lazy.ChildCrashHandler.childMap.set(childID, remoteType);

  // Mock a process crash being notified.
  const propertyBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
    Ci.nsIWritablePropertyBag2
  );
  propertyBag.setPropertyAsBool("abnormal", true);
  propertyBag.setPropertyAsAString("dumpID", "a-dump-id");

  // Set up a listener to receive the crash report event emitted by the handler.
  let listener;
  const crashReportPromise = new Promise(resolve => {
    listener = {
      onEvent(aEvent, aData, aCallback) {
        resolve([aEvent, aData]);
      },
    };
  });
  lazy.EventDispatcher.instance.registerListener(listener, [
    "GeckoView:ChildCrashReport",
  ]);

  // Simulate a crash.
  lazy.ChildCrashHandler.observe(propertyBag, "ipc:content-shutdown", childID);

  const [aEvent, aData] = await crashReportPromise;
  Assert.equal(
    "GeckoView:ChildCrashReport",
    aEvent,
    "expected a child crash report"
  );
  Assert.equal("webIsolated", aData?.remoteType, "expected remote type prefix");
});

add_task(async function test_extensions_process_crash() {
  const childID = 123;
  const remoteType = "extension";
  // Force-set a remote type for the process that we are going to "crash" next.
  lazy.ChildCrashHandler.childMap.set(childID, remoteType);

  // Mock a process crash being notified.
  const propertyBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
    Ci.nsIWritablePropertyBag2
  );
  propertyBag.setPropertyAsBool("abnormal", true);
  propertyBag.setPropertyAsAString("dumpID", "a-dump-id");

  // Set up a listener to receive the crash report event emitted by the handler.
  let listener;
  const crashReportPromise = new Promise(resolve => {
    listener = {
      onEvent(aEvent, aData, aCallback) {
        resolve([aEvent, aData]);
      },
    };
  });
  lazy.EventDispatcher.instance.registerListener(listener, [
    "GeckoView:ChildCrashReport",
  ]);

  // Simulate a crash.
  lazy.ChildCrashHandler.observe(propertyBag, "ipc:content-shutdown", childID);

  const [aEvent, aData] = await crashReportPromise;
  Assert.equal(
    "GeckoView:ChildCrashReport",
    aEvent,
    "expected a child crash report"
  );
  Assert.equal("extension", aData?.remoteType, "expected remote type");
  Assert.equal("BACKGROUND_CHILD", aData?.processType, "expected process type");
});