summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/permissions/browser_permission_delegate_geo.js
blob: 45e78d451938be374bc154987b53952c5bfb84c0 (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
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const ORIGIN = "https://example.com";
const CROSS_SUBFRAME_PAGE =
  getRootDirectory(gTestPath).replace("chrome://mochitests/content", ORIGIN) +
  "temporary_permissions_subframe.html";

const CROSS_FRAME_PAGE =
  getRootDirectory(gTestPath).replace("chrome://mochitests/content", ORIGIN) +
  "temporary_permissions_frame.html";

const PromptResult = {
  ALLOW: "allow",
  DENY: "deny",
  PROMPT: "prompt",
};

var Perms = Services.perms;
var uri = NetUtil.newURI(ORIGIN);
var principal = Services.scriptSecurityManager.createContentPrincipal(uri, {});

async function checkNotificationBothOrigins(
  firstPartyOrigin,
  thirdPartyOrigin
) {
  // Notification is shown, check label and deny to clean
  let popuphidden = BrowserTestUtils.waitForEvent(
    PopupNotifications.panel,
    "popuphidden"
  );

  let notification = PopupNotifications.panel.firstElementChild;
  // Check the label of the notificaiton should be the first party
  is(
    PopupNotifications.getNotification("geolocation").options.name,
    firstPartyOrigin,
    "Use first party's origin"
  );

  // Check the second name of the notificaiton should be the third party
  is(
    PopupNotifications.getNotification("geolocation").options.secondName,
    thirdPartyOrigin,
    "Use third party's origin"
  );

  // Check remember checkbox is hidden
  let checkbox = notification.checkbox;
  ok(!!checkbox, "checkbox is present");
  ok(checkbox.hidden, "checkbox is not visible");
  ok(!checkbox.checked, "checkbox not checked");

  EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});
  await popuphidden;
}

async function checkGeolocation(browser, frameId, expect) {
  let isPrompt = expect == PromptResult.PROMPT;
  let waitForPrompt;
  if (isPrompt) {
    waitForPrompt = BrowserTestUtils.waitForEvent(
      PopupNotifications.panel,
      "popupshown"
    );
  }

  await SpecialPowers.spawn(
    browser,
    [{ frameId, expect, isPrompt }],
    async args => {
      let frame = content.document.getElementById(args.frameId);

      let waitForNoPrompt = new Promise(resolve => {
        function onMessage(event) {
          // Check the result right here because there's no notification
          Assert.equal(
            event.data,
            args.expect,
            "Correct expectation for third party"
          );
          content.window.removeEventListener("message", onMessage);
          resolve();
        }

        if (!args.isPrompt) {
          content.window.addEventListener("message", onMessage);
        }
      });

      await content.SpecialPowers.spawn(frame, [], async () => {
        const { E10SUtils } = ChromeUtils.importESModule(
          "resource://gre/modules/E10SUtils.sys.mjs"
        );

        E10SUtils.wrapHandlingUserInput(this.content, true, function () {
          let frameDoc = this.content.document;
          frameDoc.getElementById("geo").click();
        });
      });

      if (!args.isPrompt) {
        await waitForNoPrompt;
      }
    }
  );

  if (isPrompt) {
    await waitForPrompt;
  }
}

add_setup(async function () {
  await new Promise(r => {
    SpecialPowers.pushPrefEnv(
      {
        set: [
          ["dom.security.featurePolicy.header.enabled", true],
          ["dom.security.featurePolicy.webidl.enabled", true],
          ["permissions.delegation.enabled", true],
          // This is the amount of time before the repeating
          // NetworkGeolocationProvider timer is stopped.
          // It needs to be less than 5000ms, or the timer will be
          // reported as left behind by the test.
          ["geo.timeout", 4000],
        ],
      },
      r
    );
  });
});

// Test that temp blocked permissions in first party affect the third party
// iframe.
add_task(async function testUseTempPermissionsFirstParty() {
  await BrowserTestUtils.withNewTab(
    CROSS_SUBFRAME_PAGE,
    async function (browser) {
      SitePermissions.setForPrincipal(
        principal,
        "geo",
        SitePermissions.BLOCK,
        SitePermissions.SCOPE_TEMPORARY,
        browser
      );

      await checkGeolocation(browser, "frame", PromptResult.DENY);

      SitePermissions.removeFromPrincipal(principal, "geo", browser);
    }
  );
});

// Test that persistent permissions in first party affect the third party
// iframe.
add_task(async function testUsePersistentPermissionsFirstParty() {
  await BrowserTestUtils.withNewTab(
    CROSS_SUBFRAME_PAGE,
    async function (browser) {
      async function checkPermission(aPermission, aExpect) {
        PermissionTestUtils.add(uri, "geo", aPermission);
        await checkGeolocation(browser, "frame", aExpect);

        if (aExpect == PromptResult.PROMPT) {
          // Notification is shown, check label and deny to clean
          let popuphidden = BrowserTestUtils.waitForEvent(
            PopupNotifications.panel,
            "popuphidden"
          );

          let notification = PopupNotifications.panel.firstElementChild;
          // Check the label of the notificaiton should be the first party
          is(
            PopupNotifications.getNotification("geolocation").options.name,
            uri.host,
            "Use first party's origin"
          );

          EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});

          await popuphidden;
          SitePermissions.removeFromPrincipal(null, "geo", browser);
        }

        PermissionTestUtils.remove(uri, "geo");
      }

      await checkPermission(Perms.PROMPT_ACTION, PromptResult.PROMPT);
      await checkPermission(Perms.DENY_ACTION, PromptResult.DENY);
      await checkPermission(Perms.ALLOW_ACTION, PromptResult.ALLOW);
    }
  );
});

// Test that we do not prompt for maybe unsafe permission delegation if the
// origin of the page is the original src origin.
add_task(async function testPromptInMaybeUnsafePermissionDelegation() {
  await BrowserTestUtils.withNewTab(
    CROSS_SUBFRAME_PAGE,
    async function (browser) {
      // Persistent allow top level origin
      PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION);

      await checkGeolocation(browser, "frameAllowsAll", PromptResult.ALLOW);

      SitePermissions.removeFromPrincipal(null, "geo", browser);
      PermissionTestUtils.remove(uri, "geo");
    }
  );
});

// Test that we should prompt if we are in unsafe permission delegation and
// change location to origin which is not explicitly trusted. The prompt popup
// should include both first and third party origin.
add_task(async function testPromptChangeLocationUnsafePermissionDelegation() {
  await BrowserTestUtils.withNewTab(
    CROSS_SUBFRAME_PAGE,
    async function (browser) {
      // Persistent allow top level origin
      PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION);

      let iframe = await SpecialPowers.spawn(browser, [], () => {
        return content.document.getElementById("frameAllowsAll")
          .browsingContext;
      });

      let otherURI =
        "https://test1.example.com/browser/browser/base/content/test/permissions/permissions.html";
      let loaded = BrowserTestUtils.browserLoaded(browser, true, otherURI);
      await SpecialPowers.spawn(iframe, [otherURI], async function (_otherURI) {
        content.location = _otherURI;
      });
      await loaded;

      await checkGeolocation(browser, "frameAllowsAll", PromptResult.PROMPT);
      await checkNotificationBothOrigins(uri.host, "test1.example.com");

      SitePermissions.removeFromPrincipal(null, "geo", browser);
      PermissionTestUtils.remove(uri, "geo");
    }
  );
});

// If we are in unsafe permission delegation and the origin is explicitly
// trusted in ancestor chain. Do not need prompt
add_task(async function testExplicitlyAllowedInChain() {
  await BrowserTestUtils.withNewTab(CROSS_FRAME_PAGE, async function (browser) {
    // Persistent allow top level origin
    PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION);

    let iframeAncestor = await SpecialPowers.spawn(browser, [], () => {
      return content.document.getElementById("frameAncestor").browsingContext;
    });

    let iframe = await SpecialPowers.spawn(iframeAncestor, [], () => {
      return content.document.getElementById("frameAllowsAll").browsingContext;
    });

    // Change location to check that we actually look at the ancestor chain
    // instead of just considering the "same origin as src" rule.
    let otherURI =
      "https://test2.example.com/browser/browser/base/content/test/permissions/permissions.html";
    let loaded = BrowserTestUtils.browserLoaded(browser, true, otherURI);
    await SpecialPowers.spawn(iframe, [otherURI], async function (_otherURI) {
      content.location = _otherURI;
    });
    await loaded;

    await checkGeolocation(
      iframeAncestor,
      "frameAllowsAll",
      PromptResult.ALLOW
    );

    PermissionTestUtils.remove(uri, "geo");
  });
});