summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/test/browser_net_cause.js
blob: 2e73965d0d6fcd89d945a3979bb941efd6dab22b (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

/**
 * Tests if request cause is reported correctly.
 */

const CAUSE_FILE_NAME = "html_cause-test-page.html";
const CAUSE_URL = EXAMPLE_URL + CAUSE_FILE_NAME;

const EXPECTED_REQUESTS = [
  {
    method: "GET",
    url: CAUSE_URL,
    causeType: "document",
    causeUri: "",
    // The document load has internal privileged JS code on the stack
    stack: true
  },
  {
    method: "GET",
    url: EXAMPLE_URL + "stylesheet_request",
    causeType: "stylesheet",
    causeUri: CAUSE_URL,
    stack: false
  },
  {
    method: "GET",
    url: EXAMPLE_URL + "img_request",
    causeType: "img",
    causeUri: CAUSE_URL,
    stack: false
  },
  {
    method: "GET",
    url: EXAMPLE_URL + "xhr_request",
    causeType: "xhr",
    causeUri: CAUSE_URL,
    stack: [{ fn: "performXhrRequest", file: CAUSE_FILE_NAME, line: 22 }]
  },
  {
    method: "GET",
    url: EXAMPLE_URL + "fetch_request",
    causeType: "fetch",
    causeUri: CAUSE_URL,
    stack: [{ fn: "performFetchRequest", file: CAUSE_FILE_NAME, line: 26 }]
  },
  {
    method: "GET",
    url: EXAMPLE_URL + "promise_fetch_request",
    causeType: "fetch",
    causeUri: CAUSE_URL,
    stack: [
      { fn: "performPromiseFetchRequest", file: CAUSE_FILE_NAME, line: 38 },
      { fn: null, file: CAUSE_FILE_NAME, line: 37, asyncCause: "promise callback" },
    ]
  },
  {
    method: "GET",
    url: EXAMPLE_URL + "timeout_fetch_request",
    causeType: "fetch",
    causeUri: CAUSE_URL,
    stack: [
      { fn: "performTimeoutFetchRequest", file: CAUSE_FILE_NAME, line: 40 },
      { fn: "performPromiseFetchRequest", file: CAUSE_FILE_NAME, line: 39,
        asyncCause: "setTimeout handler" },
    ]
  },
  {
    method: "POST",
    url: EXAMPLE_URL + "beacon_request",
    causeType: "beacon",
    causeUri: CAUSE_URL,
    stack: [{ fn: "performBeaconRequest", file: CAUSE_FILE_NAME, line: 30 }]
  },
];

add_task(function* () {
  // Async stacks aren't on by default in all builds
  yield SpecialPowers.pushPrefEnv({ set: [["javascript.options.asyncstack", true]] });

  // the initNetMonitor function clears the network request list after the
  // page is loaded. That's why we first load a bogus page from SIMPLE_URL,
  // and only then load the real thing from CAUSE_URL - we want to catch
  // all the requests the page is making, not only the XHRs.
  // We can't use about:blank here, because initNetMonitor checks that the
  // page has actually made at least one request.
  let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
  let { $, NetMonitorView } = monitor.panelWin;
  let { RequestsMenu } = NetMonitorView;
  RequestsMenu.lazyUpdate = false;

  let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
  tab.linkedBrowser.loadURI(CAUSE_URL);
  yield wait;

  is(RequestsMenu.itemCount, EXPECTED_REQUESTS.length,
    "All the page events should be recorded.");

  EXPECTED_REQUESTS.forEach((spec, i) => {
    let { method, url, causeType, causeUri, stack } = spec;

    let requestItem = RequestsMenu.getItemAtIndex(i);
    verifyRequestItemTarget(requestItem,
      method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
    );

    let { stacktrace } = requestItem.attachment.cause;
    let stackLen = stacktrace ? stacktrace.length : 0;

    if (stack) {
      ok(stacktrace, `Request #${i} has a stacktrace`);
      ok(stackLen > 0,
        `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);

      // if "stack" is array, check the details about the top stack frames
      if (Array.isArray(stack)) {
        stack.forEach((frame, j) => {
          is(stacktrace[j].functionName, frame.fn,
            `Request #${i} has the correct function on JS stack frame #${j}`);
          is(stacktrace[j].filename.split("/").pop(), frame.file,
            `Request #${i} has the correct file on JS stack frame #${j}`);
          is(stacktrace[j].lineNumber, frame.line,
            `Request #${i} has the correct line number on JS stack frame #${j}`);
          is(stacktrace[j].asyncCause, frame.asyncCause,
            `Request #${i} has the correct async cause on JS stack frame #${j}`);
        });
      }
    } else {
      is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
    }
  });

  // Sort the requests by cause and check the order
  EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-cause-button"));
  let expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort();
  expectedOrder.forEach((expectedCause, i) => {
    let { target } = RequestsMenu.getItemAtIndex(i);
    let causeLabel = target.querySelector(".requests-menu-cause-label");
    let cause = causeLabel.getAttribute("value");
    is(cause, expectedCause, `The request #${i} has the expected cause after sorting`);
  });

  yield teardown(monitor);
});