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

"use strict";

const TEST_PATH = getRootDirectory(gTestPath).replace(
  "chrome://mochitests/content",
  "http://example.com"
);

const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
const nullP = Services.scriptSecurityManager.createNullPrincipal({});

// We need 3 levels of nesting just to get to run something against a content
// page, so the devtools limit of 4 levels of nesting don't help:
/* eslint max-nested-callbacks: 0 */

/**
 * Check that we don't expose a JSONView object on data: URI windows where
 * we block the load.
 */
add_task(async function test_blocked_data_exposure() {
  await SpecialPowers.pushPrefEnv({
    set: [["security.data_uri.block_toplevel_data_uri_navigations", true]],
  });
  await BrowserTestUtils.withNewTab(TEST_PATH + "empty.html", async browser => {
    const tabCount = gBrowser.tabs.length;
    await SpecialPowers.spawn(browser, [], function () {
      content.w = content.window.open(
        "data:application/vnd.mozilla.json.view,1",
        "_blank"
      );
      ok(
        !Cu.waiveXrays(content.w).JSONView,
        "Should not have created a JSON View object"
      );
      // We need to wait for the JSON view machinery to actually have a chance to run.
      // We have no way to detect that it has or hasn't, so a setTimeout is the best we
      // can do, unfortunately.
      return new Promise(resolve => {
        content.setTimeout(function () {
          // Putting the resolve before the check to avoid JS errors potentially causing
          // the test to time out.
          resolve();
          ok(
            !Cu.waiveXrays(content.w).JSONView,
            "Should still not have a JSON View object"
          );
        }, 1000);
      });
    });
    // Without this, if somehow the data: protocol blocker stops working, the
    // test would just keep passing.
    is(
      tabCount,
      gBrowser.tabs.length,
      "Haven't actually opened a new window/tab"
    );
  });
});

/**
 * Check that aborted channels also abort sending data from the stream converter.
 */
add_task(async function test_converter_abort_should_stop_data_sending() {
  const loadInfo = NetUtil.newChannel({
    uri: Services.io.newURI("data:text/plain,"),
    loadingPrincipal: nullP,
    securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
    contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
  }).loadInfo;
  // Stub all the things.
  const chan = {
    QueryInterface: ChromeUtils.generateQI([
      "nsIChannel",
      "nsIWritablePropertyBag",
    ]),
    URI: Services.io.newURI("data:application/json,{}"),
    // loadinfo is builtinclass, need to actually have one:
    loadInfo,
    notificationCallbacks: {
      QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
      getInterface() {
        // We want a loadcontext here, which is also builtinclass, can't stub.
        return docShell;
      },
    },
    status: Cr.NS_OK,
    setProperty() {},
  };
  let onStartFired = false;
  const listener = {
    QueryInterface: ChromeUtils.generateQI(["nsIStreamListener"]),
    onStartRequest() {
      onStartFired = true;
      // This should force the converter to abort, too:
      chan.status = Cr.NS_BINDING_ABORTED;
    },
    onDataAvailable() {
      ok(false, "onDataAvailable should never fire");
    },
  };
  const conv = Cc[
    "@mozilla.org/streamconv;1?from=" + JSON_VIEW_MIME_TYPE + "&to=*/*"
  ].createInstance(Ci.nsIStreamConverter);
  conv.asyncConvertData(
    "application/vnd.mozilla.json.view",
    "text/html",
    listener,
    null
  );
  conv.onStartRequest(chan);
  ok(onStartFired, "Should have fired onStartRequest");
});

/**
 * Check that principal mismatches break things. Note that we're stubbing
 * the window associated with the channel to be a browser window; the
 * converter should be bailing out because the window's principal won't
 * match the null principal to which the converter tries to reset the
 * inheriting principal of the channel.
 */
add_task(async function test_converter_principal_needs_matching() {
  const loadInfo = NetUtil.newChannel({
    uri: Services.io.newURI("data:text/plain,"),
    loadingPrincipal: nullP,
    securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
    contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
  }).loadInfo;
  // Stub all the things.
  const chan = {
    QueryInterface: ChromeUtils.generateQI([
      "nsIChannel",
      "nsIWritablePropertyBag",
    ]),
    URI: Services.io.newURI("data:application/json,{}"),
    // loadinfo is builtinclass, need to actually have one:
    loadInfo,
    notificationCallbacks: {
      QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
      getInterface() {
        // We want a loadcontext here, which is also builtinclass, can't stub.
        return docShell;
      },
    },
    status: Cr.NS_OK,
    setProperty() {},
    cancel(arg) {
      this.status = arg;
    },
  };
  let onStartFired = false;
  const listener = {
    QueryInterface: ChromeUtils.generateQI(["nsIStreamListener"]),
    onStartRequest() {
      onStartFired = true;
    },
    onDataAvailable() {
      ok(false, "onDataAvailable should never fire");
    },
  };
  const conv = Cc[
    "@mozilla.org/streamconv;1?from=" + JSON_VIEW_MIME_TYPE + "&to=*/*"
  ].createInstance(Ci.nsIStreamConverter);
  conv.asyncConvertData(
    "application/vnd.mozilla.json.view",
    "text/html",
    listener,
    null
  );
  conv.onStartRequest(chan);
  ok(onStartFired, "Should have fired onStartRequest");
  is(chan.status, Cr.NS_BINDING_ABORTED, "Should have been aborted.");
});