summaryrefslogtreecommitdiffstats
path: root/browser/components/attribution/test/browser/browser_AttributionCode_Mac_telemetry.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/attribution/test/browser/browser_AttributionCode_Mac_telemetry.js')
-rw-r--r--browser/components/attribution/test/browser/browser_AttributionCode_Mac_telemetry.js248
1 files changed, 248 insertions, 0 deletions
diff --git a/browser/components/attribution/test/browser/browser_AttributionCode_Mac_telemetry.js b/browser/components/attribution/test/browser/browser_AttributionCode_Mac_telemetry.js
new file mode 100644
index 0000000000..7523f0f930
--- /dev/null
+++ b/browser/components/attribution/test/browser/browser_AttributionCode_Mac_telemetry.js
@@ -0,0 +1,248 @@
+ChromeUtils.defineESModuleGetters(this, {
+ TelemetryTestUtils: "resource://testing-common/TelemetryTestUtils.sys.mjs",
+});
+const { MacAttribution } = ChromeUtils.importESModule(
+ "resource:///modules/MacAttribution.sys.mjs"
+);
+const { AttributionIOUtils } = ChromeUtils.importESModule(
+ "resource:///modules/AttributionCode.sys.mjs"
+);
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+async function assertCacheExistsAndIsEmpty() {
+ // We should have written to the cache, and be able to read back
+ // with no errors.
+ const histogram = Services.telemetry.getHistogramById(
+ "BROWSER_ATTRIBUTION_ERRORS"
+ );
+ histogram.clear();
+
+ ok(await AttributionIOUtils.exists(AttributionCode.attributionFile.path));
+ Assert.deepEqual(
+ "",
+ new TextDecoder().decode(
+ await AttributionIOUtils.read(AttributionCode.attributionFile.path)
+ )
+ );
+
+ AttributionCode._clearCache();
+ let result = await AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, {}, "Should be able to get cached result");
+
+ Assert.deepEqual({}, histogram.snapshot().values || {});
+}
+
+add_task(async function test_write_error() {
+ const sandbox = sinon.createSandbox();
+ let attributionSvc = Cc["@mozilla.org/mac-attribution;1"].getService(
+ Ci.nsIMacAttributionService
+ );
+ attributionSvc.setReferrerUrl(
+ MacAttribution.applicationPath,
+ "https://example.com?content=content",
+ true
+ );
+
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+
+ const histogram = Services.telemetry.getHistogramById(
+ "BROWSER_ATTRIBUTION_ERRORS"
+ );
+
+ let oldExists = AttributionIOUtils.exists;
+ let oldWrite = AttributionIOUtils.write;
+ try {
+ // Clear any existing telemetry
+ histogram.clear();
+
+ // Force the file to not exist and then cause a write error. This is delicate
+ // because various background tasks may invoke `IOUtils.writeAtomic` while
+ // this test is running. Be careful to only stub the one call.
+ AttributionIOUtils.exists = () => false;
+ AttributionIOUtils.write = () => {
+ throw new Error("write_error");
+ };
+
+ // Try to read the attribution code.
+ let result = await AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(
+ result,
+ { content: "content" },
+ "Should be able to get a result even if the file doesn't write"
+ );
+
+ TelemetryTestUtils.assertHistogram(histogram, INDEX_WRITE_ERROR, 1);
+ } finally {
+ AttributionIOUtils.exists = oldExists;
+ AttributionIOUtils.write = oldWrite;
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+ histogram.clear();
+ sandbox.restore();
+ }
+});
+
+add_task(async function test_unusual_referrer() {
+ // This referrer URL looks malformed, but the malformed bits are dropped, so
+ // it's actually ok. This is what allows extraneous bits like `fbclid` tags
+ // to be ignored.
+ let attributionSvc = Cc["@mozilla.org/mac-attribution;1"].getService(
+ Ci.nsIMacAttributionService
+ );
+ attributionSvc.setReferrerUrl(
+ MacAttribution.applicationPath,
+ "https://example.com?content=&=campaign",
+ true
+ );
+
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+
+ const histogram = Services.telemetry.getHistogramById(
+ "BROWSER_ATTRIBUTION_ERRORS"
+ );
+ try {
+ // Clear any existing telemetry
+ histogram.clear();
+
+ // Try to read the attribution code
+ await AttributionCode.getAttrDataAsync();
+
+ let result = await AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, {}, "Should be able to get empty result");
+
+ Assert.deepEqual({}, histogram.snapshot().values || {});
+
+ await assertCacheExistsAndIsEmpty();
+ } finally {
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+ histogram.clear();
+ }
+});
+
+add_task(async function test_blank_referrer() {
+ let attributionSvc = Cc["@mozilla.org/mac-attribution;1"].getService(
+ Ci.nsIMacAttributionService
+ );
+ attributionSvc.setReferrerUrl(MacAttribution.applicationPath, "", true);
+
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+
+ const histogram = Services.telemetry.getHistogramById(
+ "BROWSER_ATTRIBUTION_ERRORS"
+ );
+
+ try {
+ // Clear any existing telemetry
+ histogram.clear();
+
+ // Try to read the attribution code
+ let result = await AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, {}, "Should be able to get empty result");
+
+ Assert.deepEqual({}, histogram.snapshot().values || {});
+
+ await assertCacheExistsAndIsEmpty();
+ } finally {
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+ histogram.clear();
+ }
+});
+
+add_task(async function test_no_referrer() {
+ const sandbox = sinon.createSandbox();
+ let newApplicationPath = MacAttribution.applicationPath + ".test";
+ sandbox.stub(MacAttribution, "applicationPath").get(() => newApplicationPath);
+
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+
+ const histogram = Services.telemetry.getHistogramById(
+ "BROWSER_ATTRIBUTION_ERRORS"
+ );
+ try {
+ // Clear any existing telemetry
+ histogram.clear();
+
+ // Try to read the attribution code
+ await AttributionCode.getAttrDataAsync();
+
+ let result = await AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, {}, "Should be able to get empty result");
+
+ Assert.deepEqual({}, histogram.snapshot().values || {});
+
+ await assertCacheExistsAndIsEmpty();
+ } finally {
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+ histogram.clear();
+ sandbox.restore();
+ }
+});
+
+add_task(async function test_broken_referrer() {
+ let attributionSvc = Cc["@mozilla.org/mac-attribution;1"].getService(
+ Ci.nsIMacAttributionService
+ );
+ attributionSvc.setReferrerUrl(
+ MacAttribution.applicationPath,
+ "https://example.com?content=content",
+ true
+ );
+
+ // This uses macOS internals to change the GUID so that it will look like the
+ // application has quarantine data but nothing will be pressent in the
+ // quarantine database. This shouldn't happen in the wild.
+ function generateQuarantineGUID() {
+ let str = Services.uuid.generateUUID().toString().toUpperCase();
+ // Strip {}.
+ return str.substring(1, str.length - 1);
+ }
+
+ // These magic constants are macOS GateKeeper flags.
+ let string = [
+ "01c1",
+ "5991b778",
+ "Safari.app",
+ generateQuarantineGUID(),
+ ].join(";");
+ let bytes = new TextEncoder().encode(string);
+ await IOUtils.setMacXAttr(
+ MacAttribution.applicationPath,
+ "com.apple.quarantine",
+ bytes
+ );
+
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+
+ const histogram = Services.telemetry.getHistogramById(
+ "BROWSER_ATTRIBUTION_ERRORS"
+ );
+ try {
+ // Clear any existing telemetry
+ histogram.clear();
+
+ // Try to read the attribution code
+ await AttributionCode.getAttrDataAsync();
+
+ let result = await AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, {}, "Should be able to get empty result");
+
+ TelemetryTestUtils.assertHistogram(histogram, INDEX_QUARANTINE_ERROR, 1);
+ histogram.clear();
+
+ await assertCacheExistsAndIsEmpty();
+ } finally {
+ await AttributionCode.deleteFileAsync();
+ AttributionCode._clearCache();
+ histogram.clear();
+ }
+});