summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/general/browser_decoderDoctor.js
blob: 29f761b6cac59d2b69bba6e28acf65a79511ed9a (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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
"use strict";

// 'data' contains the notification data object:
// - data.type must be provided.
// - data.isSolved and data.decoderDoctorReportId will be added if not provided
//   (false and "testReportId" resp.)
// - Other fields (e.g.: data.formats) may be provided as needed.
// 'notificationMessage': Expected message in the notification bar.
//   Falsy if nothing is expected after the notification is sent, in which case
//   we won't have further checks, so the following parameters are not needed.
// 'label': Expected button label. Falsy if no button is expected, in which case
//   we won't have further checks, so the following parameters are not needed.
// 'accessKey': Expected access key for the button.
// 'tabChecker': function(openedTab) called with the opened tab that resulted
//   from clicking the button.
async function test_decoder_doctor_notification(
  data,
  notificationMessage,
  label,
  accessKey,
  tabChecker
) {
  const TEST_URL = "https://example.org";
  // A helper closure to test notifications in same or different origins.
  // 'test_cross_origin' is used to determine if the observers used in the test
  // are notified in the same frame (when false) or in a cross origin iframe
  // (when true).
  async function create_tab_and_test(test_cross_origin) {
    await BrowserTestUtils.withNewTab(
      { gBrowser, url: TEST_URL },
      async function(browser) {
        let awaitNotificationBar = BrowserTestUtils.waitForNotificationBar(
          gBrowser,
          browser,
          "decoder-doctor-notification"
        );

        await SpecialPowers.spawn(
          browser,
          [data, test_cross_origin],
          /* eslint-disable-next-line no-shadow */
          async function(data, test_cross_origin) {
            if (!test_cross_origin) {
              // Notify in the same origin.
              Services.obs.notifyObservers(
                content.window,
                "decoder-doctor-notification",
                JSON.stringify(data)
              );
              return;
              // Done notifying in the same origin.
            }

            // Notify in a different origin.
            const CROSS_ORIGIN_URL = "https://example.com";
            let frame = content.document.createElement("iframe");
            frame.src = CROSS_ORIGIN_URL;
            await new Promise(resolve => {
              frame.addEventListener("load", () => {
                resolve();
              });
              content.document.body.appendChild(frame);
            });

            await content.SpecialPowers.spawn(frame, [data], async function(
              /* eslint-disable-next-line no-shadow */
              data
            ) {
              Services.obs.notifyObservers(
                content.window,
                "decoder-doctor-notification",
                JSON.stringify(data)
              );
            });
            // Done notifying in a different origin.
          }
        );

        if (!notificationMessage) {
          ok(
            true,
            "Tested notifying observers with a nonsensical message, no effects expected"
          );
          return;
        }

        let notification;
        try {
          notification = await awaitNotificationBar;
        } catch (ex) {
          ok(false, ex);
          return;
        }
        ok(notification, "Got decoder-doctor-notification notification");

        is(
          notification.messageText.textContent,
          notificationMessage,
          "notification message should match expectation"
        );

        let button = notification.querySelector("button");
        if (!label) {
          ok(!button, "There should not be button");
          return;
        }

        is(
          button.getAttribute("label"),
          label,
          `notification button should be '${label}'`
        );
        is(
          button.getAttribute("accesskey"),
          accessKey,
          "notification button should have accesskey"
        );

        if (!tabChecker) {
          ok(false, "Test implementation error: Missing tabChecker");
          return;
        }
        let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
        button.click();
        let openedTab = await awaitNewTab;
        tabChecker(openedTab);
        BrowserTestUtils.removeTab(openedTab);
      }
    );
  }

  if (typeof data.type === "undefined") {
    ok(false, "Test implementation error: data.type must be provided");
    return;
  }
  data.isSolved = data.isSolved || false;
  if (typeof data.decoderDoctorReportId === "undefined") {
    data.decoderDoctorReportId = "testReportId";
  }

  // Test same origin.
  await create_tab_and_test(false);
  // Test cross origin.
  await create_tab_and_test(true);
}

function tab_checker_for_sumo(expectedPath) {
  return function(openedTab) {
    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
    let url = baseURL + expectedPath;
    is(
      openedTab.linkedBrowser.currentURI.spec,
      url,
      `Expected '${url}' in new tab`
    );
  };
}

function tab_checker_for_webcompat(expectedParams) {
  return function(openedTab) {
    let urlString = openedTab.linkedBrowser.currentURI.spec;
    let endpoint = Services.prefs.getStringPref(
      "media.decoder-doctor.new-issue-endpoint",
      ""
    );
    ok(
      urlString.startsWith(endpoint),
      `Expected URL starting with '${endpoint}', got '${urlString}'`
    );
    let params = new URL(urlString).searchParams;
    for (let k in expectedParams) {
      if (!params.has(k)) {
        ok(false, `Expected ${k} in webcompat URL`);
      } else {
        is(
          params.get(k),
          expectedParams[k],
          `Expected ${k}='${expectedParams[k]}' in webcompat URL`
        );
      }
    }
  };
}

add_task(async function test_platform_decoder_not_found() {
  let message = "";
  let isLinux = AppConstants.platform == "linux";
  if (isLinux) {
    message = gNavigatorBundle.getString("decoder.noCodecsLinux.message");
  } else if (AppConstants.platform == "win") {
    message = gNavigatorBundle.getString("decoder.noHWAcceleration.message");
  }

  await test_decoder_doctor_notification(
    { type: "platform-decoder-not-found", formats: "testFormat" },
    message,
    isLinux ? "" : gNavigatorBundle.getString("decoder.noCodecs.button"),
    isLinux ? "" : gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
    tab_checker_for_sumo("fix-video-audio-problems-firefox-windows")
  );
});

add_task(async function test_cannot_initialize_pulseaudio() {
  let message = "";
  // This is only sent on Linux.
  if (AppConstants.platform == "linux") {
    message = gNavigatorBundle.getString("decoder.noPulseAudio.message");
  }

  await test_decoder_doctor_notification(
    { type: "cannot-initialize-pulseaudio", formats: "testFormat" },
    message,
    gNavigatorBundle.getString("decoder.noCodecs.button"),
    gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
    tab_checker_for_sumo("fix-common-audio-and-video-issues")
  );
});

add_task(async function test_unsupported_libavcodec() {
  let message = "";
  // This is only sent on Linux.
  if (AppConstants.platform == "linux") {
    message = gNavigatorBundle.getString(
      "decoder.unsupportedLibavcodec.message"
    );
  }

  await test_decoder_doctor_notification(
    { type: "unsupported-libavcodec", formats: "testFormat" },
    message
  );
});

add_task(async function test_decode_error() {
  await SpecialPowers.pushPrefEnv({
    set: [
      [
        "media.decoder-doctor.new-issue-endpoint",
        "http://example.com/webcompat",
      ],
      ["browser.fixup.fallback-to-https", false],
    ],
  });
  let message = gNavigatorBundle.getString("decoder.decodeError.message");
  await test_decoder_doctor_notification(
    {
      type: "decode-error",
      decodeIssue: "DecodeIssue",
      docURL: "DocURL",
      resourceURL: "ResURL",
    },
    message,
    gNavigatorBundle.getString("decoder.decodeError.button"),
    gNavigatorBundle.getString("decoder.decodeError.accesskey"),
    tab_checker_for_webcompat({
      url: "DocURL",
      label: "type-media",
      problem_type: "video_bug",
      details: JSON.stringify({
        "Technical Information:": "DecodeIssue",
        "Resource:": "ResURL",
      }),
    })
  );
});

add_task(async function test_decode_warning() {
  await SpecialPowers.pushPrefEnv({
    set: [
      [
        "media.decoder-doctor.new-issue-endpoint",
        "http://example.com/webcompat",
      ],
    ],
  });
  let message = gNavigatorBundle.getString("decoder.decodeWarning.message");
  await test_decoder_doctor_notification(
    {
      type: "decode-warning",
      decodeIssue: "DecodeIssue",
      docURL: "DocURL",
      resourceURL: "ResURL",
    },
    message,
    gNavigatorBundle.getString("decoder.decodeError.button"),
    gNavigatorBundle.getString("decoder.decodeError.accesskey"),
    tab_checker_for_webcompat({
      url: "DocURL",
      label: "type-media",
      problem_type: "video_bug",
      details: JSON.stringify({
        "Technical Information:": "DecodeIssue",
        "Resource:": "ResURL",
      }),
    })
  );
});