summaryrefslogtreecommitdiffstats
path: root/toolkit/components/antitracking/test/browser/browser_urlQueryStrippingListService.js
blob: ee791e3cea93fb660ea8ade18ae2b85ac22f01f2 (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
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const { RemoteSettings } = ChromeUtils.importESModule(
  "resource://services-settings/remote-settings.sys.mjs"
);

XPCOMUtils.defineLazyServiceGetter(
  this,
  "urlQueryStrippingListService",
  "@mozilla.org/query-stripping-list-service;1",
  "nsIURLQueryStrippingListService"
);

const COLLECTION_NAME = "query-stripping";

const TEST_URI = TEST_DOMAIN + TEST_PATH + "empty.html";
const TEST_THIRD_PARTY_URI = TEST_DOMAIN_2 + TEST_PATH + "empty.html";

// The Update Event here is used to listen the observer from the
// URLQueryStrippingListService. We need to use the event here so that the same
// observer can be called multiple times.
class UpdateEvent extends EventTarget {}

// The Observer that is registered needs to implement onQueryStrippingListUpdate
// like a nsIURLQueryStrippingListObserver does
class ListObserver {
  updateEvent = new UpdateEvent();

  onQueryStrippingListUpdate(stripList, allowList) {
    let event = new CustomEvent("update", { detail: { stripList, allowList } });
    this.updateEvent.dispatchEvent(event);
  }
}

function waitForEvent(element, eventName) {
  return BrowserTestUtils.waitForEvent(element, eventName).then(e => e.detail);
}

async function verifyQueryString(browser, expected) {
  await SpecialPowers.spawn(browser, [expected], expected => {
    // Strip the first question mark.
    let search = content.location.search.slice(1);

    is(search, expected, "The query string is correct.");
  });
}

async function check(query, expected) {
  // Open a tab with the query string.
  let testURI = TEST_URI + "?" + query;

  // Test for stripping in parent process.
  await BrowserTestUtils.withNewTab(testURI, async browser => {
    // Verify if the query string is expected in the new tab.
    await verifyQueryString(browser, expected);
  });

  testURI = TEST_URI + "?" + query;
  let expectedURI;
  if (expected != "") {
    expectedURI = TEST_URI + "?" + expected;
  } else {
    expectedURI = TEST_URI;
  }

  // Test for stripping in content processes. This will first open a third-party
  // page and create a link to the test uri. And then, click the link to
  // navigate the page, which will trigger the stripping in content processes.
  await BrowserTestUtils.withNewTab(TEST_THIRD_PARTY_URI, async browser => {
    // Create the promise to wait for the location change.
    let locationChangePromise = BrowserTestUtils.waitForLocationChange(
      gBrowser,
      expectedURI
    );

    // Create a link and click it to navigate.
    await SpecialPowers.spawn(browser, [testURI], async uri => {
      let link = content.document.createElement("a");
      link.setAttribute("href", uri);
      link.textContent = "Link";
      content.document.body.appendChild(link);
      link.click();
    });

    await locationChangePromise;

    // Verify the query string in the content window.
    await verifyQueryString(browser, expected);
  });
}

registerCleanupFunction(() => {
  Cc["@mozilla.org/query-stripping-list-service;1"]
    .getService(Ci.nsIURLQueryStrippingListService)
    .clearLists();
});

add_task(async function testPrefSettings() {
  // Enable query stripping and clear the prefs at the beginning.
  await SpecialPowers.pushPrefEnv({
    set: [
      ["privacy.query_stripping.enabled", true],
      ["privacy.query_stripping.strip_list", ""],
      ["privacy.query_stripping.allow_list", ""],
      ["privacy.query_stripping.testing", true],
    ],
  });

  // Test if the observer been called when adding to the service.
  let obs = new ListObserver();
  let promise = waitForEvent(obs.updateEvent, "update");
  urlQueryStrippingListService.registerAndRunObserver(obs);
  let lists = await promise;
  is(lists.stripList, "", "No strip list at the beginning.");
  is(lists.allowList, "", "No allow list at the beginning.");

  // Verify that no query stripping happens.
  await check("pref_query1=123", "pref_query1=123");
  await check("pref_query2=456", "pref_query2=456");

  // Set pref for strip list
  promise = waitForEvent(obs.updateEvent, "update");
  await SpecialPowers.pushPrefEnv({
    set: [["privacy.query_stripping.strip_list", "pref_query1 pref_query2"]],
  });
  lists = await promise;

  is(
    lists.stripList,
    "pref_query1 pref_query2",
    "There should be strip list entries."
  );
  is(lists.allowList, "", "There should be no allow list entries.");

  // The query string should be stripped.
  await check("pref_query1=123", "");
  await check("pref_query2=456", "");

  // Set the pref for allow list.
  promise = waitForEvent(obs.updateEvent, "update");
  await SpecialPowers.pushPrefEnv({
    set: [["privacy.query_stripping.allow_list", "example.net"]],
  });
  lists = await promise;

  is(
    lists.stripList,
    "pref_query1 pref_query2",
    "There should be strip list entires."
  );
  is(lists.allowList, "example.net", "There should be one allow list entry.");

  // The query string shouldn't be stripped because this host is in allow list.
  await check("pref_query1=123", "pref_query1=123");
  await check("pref_query2=123", "pref_query2=123");

  urlQueryStrippingListService.unregisterObserver(obs);

  // Clear prefs.
  SpecialPowers.flushPrefEnv();
});

add_task(async function testRemoteSettings() {
  // Enable query stripping and clear the prefs at the beginning.
  await SpecialPowers.pushPrefEnv({
    set: [
      ["privacy.query_stripping.enabled", true],
      ["privacy.query_stripping.strip_list", ""],
      ["privacy.query_stripping.allow_list", ""],
      ["privacy.query_stripping.testing", true],
    ],
  });

  // Add initial empty record.
  let db = RemoteSettings(COLLECTION_NAME).db;
  await db.importChanges({}, Date.now(), []);

  // Test if the observer been called when adding to the service.
  let obs = new ListObserver();
  let promise = waitForEvent(obs.updateEvent, "update");
  urlQueryStrippingListService.registerAndRunObserver(obs);
  let lists = await promise;
  is(lists.stripList, "", "No strip list at the beginning.");
  is(lists.allowList, "", "No allow list at the beginning.");

  // Verify that no query stripping happens.
  await check("remote_query1=123", "remote_query1=123");
  await check("remote_query2=456", "remote_query2=456");

  // Set record for strip list.
  promise = waitForEvent(obs.updateEvent, "update");
  await RemoteSettings(COLLECTION_NAME).emit("sync", {
    data: {
      current: [
        {
          id: "1",
          last_modified: 1000000000000001,
          stripList: ["remote_query1", "remote_query2"],
          allowList: [],
        },
      ],
    },
  });
  lists = await promise;

  is(
    lists.stripList,
    "remote_query1 remote_query2",
    "There should be strip list entries."
  );
  is(lists.allowList, "", "There should be no allow list entries.");

  // The query string should be stripped.
  await check("remote_query1=123", "");
  await check("remote_query2=456", "");

  // Set record for strip list and allow list.
  promise = waitForEvent(obs.updateEvent, "update");
  await RemoteSettings(COLLECTION_NAME).emit("sync", {
    data: {
      current: [
        {
          id: "2",
          last_modified: 1000000000000002,
          stripList: ["remote_query1", "remote_query2"],
          allowList: ["example.net"],
        },
      ],
    },
  });
  lists = await promise;

  is(
    lists.stripList,
    "remote_query1 remote_query2",
    "There should be strip list entries."
  );
  is(lists.allowList, "example.net", "There should be one allow list entry.");

  // The query string shouldn't be stripped because this host is in allow list.
  await check("remote_query1=123", "remote_query1=123");
  await check("remote_query2=123", "remote_query2=123");

  urlQueryStrippingListService.unregisterObserver(obs);

  // Clear the remote settings.
  await db.clear();
});