summaryrefslogtreecommitdiffstats
path: root/browser/extensions/search-detection/tests/browser/browser_server_side_redirection.js
blob: ea235406a45bdb5011ab581ce0a91f520948dbbe (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
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

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

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

AddonTestUtils.initMochitest(this);

const TELEMETRY_EVENTS_FILTERS = {
  category: "addonsSearchDetection",
  method: "etld_change",
};

// The search-detection built-in add-on registers dynamic events.
const TELEMETRY_TEST_UTILS_OPTIONS = { clear: true, process: "dynamic" };

const REDIRECT_SJS =
  "browser/browser/extensions/search-detection/tests/browser/redirect.sjs?q={searchTerms}";
// This URL will redirect to `example.net`, which is different than
// `*.example.com`. That will be the final URL of a redirect chain:
// www.example.com -> example.net
const SEARCH_URL_WWW = `https://www.example.com/${REDIRECT_SJS}`;
// This URL will redirect to `www.example.com`, which will create a redirect
// chain with two hops:
// test2.example.com -> www.example.com -> example.net
const SEARCH_URL_TEST2 = `https://test2.example.com/${REDIRECT_SJS}`;
// This URL will redirect to `test2.example.com`, which will create a redirect
// chain with three hops:
// test1.example.com -> test2.example.com -> www.example.com -> example.net
const SEARCH_URL_TEST1 = `https://test1.example.com/${REDIRECT_SJS}`;

const TEST_SEARCH_ENGINE_ADDON_ID = "some@addon-id";
const TEST_SEARCH_ENGINE_ADDON_VERSION = "4.5.6";

const testServerSideRedirect = async ({
  searchURL,
  expectedEvents,
  tabURL,
}) => {
  Services.telemetry.clearEvents();

  const searchEngineName = "test search engine";
  // Load a default search engine because the add-on we are testing here
  // monitors the search engines.
  const searchEngine = ExtensionTestUtils.loadExtension({
    manifest: {
      version: TEST_SEARCH_ENGINE_ADDON_VERSION,
      browser_specific_settings: {
        gecko: { id: TEST_SEARCH_ENGINE_ADDON_ID },
      },
      chrome_settings_overrides: {
        search_provider: {
          name: searchEngineName,
          keyword: "test",
          search_url: searchURL,
        },
      },
    },
    useAddonManager: "temporary",
  });

  await searchEngine.startup();
  ok(
    Services.search.getEngineByName(searchEngineName),
    "test search engine registered"
  );
  await AddonTestUtils.waitForSearchProviderStartup(searchEngine);

  // Simulate a search (with the test search engine) by navigating to it.
  const url = tabURL || searchURL.replace("{searchTerms}", "some terms");
  await BrowserTestUtils.withNewTab("about:blank", async browser => {
    // Wait for the tab to be fully loaded.
    let loaded = BrowserTestUtils.browserLoaded(browser);
    BrowserTestUtils.loadURIString(browser, url);
    await loaded;
  });

  await searchEngine.unload();
  ok(
    !Services.search.getEngineByName(searchEngineName),
    "test search engine unregistered"
  );

  TelemetryTestUtils.assertEvents(
    expectedEvents,
    TELEMETRY_EVENTS_FILTERS,
    TELEMETRY_TEST_UTILS_OPTIONS
  );
};

add_task(function test_redirect_final() {
  return testServerSideRedirect({
    // www.example.com -> example.net
    searchURL: SEARCH_URL_WWW,
    expectedEvents: [
      {
        object: "other",
        value: "server",
        extra: {
          addonId: TEST_SEARCH_ENGINE_ADDON_ID,
          addonVersion: TEST_SEARCH_ENGINE_ADDON_VERSION,
          from: "example.com",
          to: "example.net",
        },
      },
    ],
  });
});

add_task(function test_redirect_two_hops() {
  return testServerSideRedirect({
    // test2.example.com -> www.example.com -> example.net
    searchURL: SEARCH_URL_TEST2,
    expectedEvents: [
      {
        object: "other",
        value: "server",
        extra: {
          addonId: TEST_SEARCH_ENGINE_ADDON_ID,
          addonVersion: TEST_SEARCH_ENGINE_ADDON_VERSION,
          from: "example.com",
          to: "example.net",
        },
      },
    ],
  });
});

add_task(function test_redirect_three_hops() {
  return testServerSideRedirect({
    // test1.example.com -> test2.example.com -> www.example.com -> example.net
    searchURL: SEARCH_URL_TEST1,
    expectedEvents: [
      {
        object: "other",
        value: "server",
        extra: {
          addonId: TEST_SEARCH_ENGINE_ADDON_ID,
          addonVersion: TEST_SEARCH_ENGINE_ADDON_VERSION,
          from: "example.com",
          to: "example.net",
        },
      },
    ],
  });
});

add_task(function test_no_event_when_search_engine_not_used() {
  return testServerSideRedirect({
    // www.example.com -> example.net
    searchURL: SEARCH_URL_WWW,
    // We do not expect any events because the user is not using the search
    // engine that was registered.
    tabURL: "http://mochi.test:8888/search?q=foobar",
    expectedEvents: [],
  });
});

add_task(function test_redirect_chain_does_not_start_on_first_request() {
  return testServerSideRedirect({
    // www.example.com -> example.net
    searchURL: SEARCH_URL_WWW,
    // User first navigates to an URL that isn't monitored and will be
    // redirected to another URL that is monitored.
    tabURL: `http://mochi.test:8888/browser/browser/extensions/search-detection/tests/browser/redirect.sjs?q={searchTerms}`,
    expectedEvents: [
      {
        object: "other",
        value: "server",
        extra: {
          addonId: TEST_SEARCH_ENGINE_ADDON_ID,
          addonVersion: TEST_SEARCH_ENGINE_ADDON_VERSION,
          // We expect this and not `mochi.test` because we do not monitor
          // `mochi.test`, only `example.com`, which is coming from the search
          // engine registered in the test setup.
          from: "example.com",
          to: "example.net",
        },
      },
    ],
  });
});

add_task(async function test_two_extensions_reported() {
  Services.telemetry.clearEvents();

  const searchEngines = [];
  for (const [addonId, addonVersion, isDefault] of [
    ["1-addon@guid", "1.2", false],
    ["2-addon@guid", "3.4", true],
  ]) {
    const searchEngine = ExtensionTestUtils.loadExtension({
      manifest: {
        version: addonVersion,
        browser_specific_settings: {
          gecko: { id: addonId },
        },
        chrome_settings_overrides: {
          search_provider: {
            is_default: isDefault,
            name: `test search engine - ${addonId}`,
            keyword: "test",
            search_url: `${SEARCH_URL_WWW}&id=${addonId}`,
          },
        },
      },
      useAddonManager: "temporary",
    });

    await searchEngine.startup();
    await AddonTestUtils.waitForSearchProviderStartup(searchEngine);

    searchEngines.push(searchEngine);
  }

  // Simulate a search by navigating to it.
  const url = SEARCH_URL_WWW.replace("{searchTerms}", "some terms");
  await BrowserTestUtils.withNewTab("about:blank", async browser => {
    // Wait for the tab to be fully loaded.
    let loaded = BrowserTestUtils.browserLoaded(browser);
    BrowserTestUtils.loadURIString(browser, url);
    await loaded;
  });

  await Promise.all(searchEngines.map(engine => engine.unload()));

  TelemetryTestUtils.assertEvents(
    [
      {
        object: "other",
        value: "server",
        extra: {
          addonId: "1-addon@guid",
          addonVersion: "1.2",
          from: "example.com",
          to: "example.net",
        },
      },
      {
        object: "other",
        value: "server",
        extra: {
          addonId: "2-addon@guid",
          addonVersion: "3.4",
          from: "example.com",
          to: "example.net",
        },
      },
    ],
    TELEMETRY_EVENTS_FILTERS,
    TELEMETRY_TEST_UTILS_OPTIONS
  );
});