Bug 1615984 - Collect filelink usage: total file uploaded and ignored times. r=mkmelin
authorPing Chen <remotenonsense@gmail.com>
Wed, 27 May 2020 09:41:38 +0900
changeset 29690 26f9f28bdcccd575fb27bd0e4c5d6541904c9198
parent 29689 1897e9e2fecbcbbeb759c1514643f10ccc018eae
child 29691 4367af3d5a4a3fe200ab062e3b301885c6f6803c
push id17487
push usermkmelin@iki.fi
push dateThu, 28 May 2020 19:05:53 +0000
treeherdercomm-central@4b3e05877240 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs1615984
Bug 1615984 - Collect filelink usage: total file uploaded and ignored times. r=mkmelin
mail/components/compose/content/MsgComposeCommands.js
mail/components/telemetry/Scalars.yaml
mail/test/browser/cloudfile/browser.ini
mail/test/browser/cloudfile/browser_filelinkTelemetry.js
mail/test/browser/shared-modules/PromptHelpers.jsm
--- a/mail/components/compose/content/MsgComposeCommands.js
+++ b/mail/components/compose/content/MsgComposeCommands.js
@@ -1956,25 +1956,26 @@ function addConvertCloudMenuItems(aParen
 
 async function uploadCloudAttachment(attachment, file, cloudFileAccount) {
   // Notify the UI that we're starting the upload process: disable send commands
   // and show a "connecting" icon for the attachment.
   attachment.sendViaCloud = true;
   gNumUploadingAttachments++;
   updateSendCommands(true);
 
+  let displayName = cloudFileAccounts.getDisplayName(cloudFileAccount);
   let bucket = document.getElementById("attachmentBucket");
   let attachmentItem = bucket.findItemForAttachment(attachment);
   if (attachmentItem) {
     let itemIcon = attachmentItem.querySelector(".attachmentcell-icon");
     itemIcon.setAttribute("src", "chrome://global/skin/icons/loading.png");
     attachmentItem.setAttribute(
       "tooltiptext",
       getComposeBundle().getFormattedString("cloudFileUploadingTooltip", [
-        cloudFileAccounts.getDisplayName(cloudFileAccount),
+        displayName,
       ])
     );
     attachmentItem.uploading = true;
     attachmentItem.cloudFileAccount = cloudFileAccount;
   }
 
   let upload;
   let statusCode = Cr.NS_OK;
@@ -1992,17 +1993,17 @@ async function uploadCloudAttachment(att
       // Update relevant bits on the attachment list item.
       if (!attachmentItem.originalUrl) {
         attachmentItem.originalUrl = originalUrl;
       }
       attachmentItem.cloudFileUpload = upload;
       attachmentItem.setAttribute(
         "tooltiptext",
         getComposeBundle().getFormattedString("cloudFileUploadedTooltip", [
-          cloudFileAccounts.getDisplayName(cloudFileAccount),
+          displayName,
         ])
       );
       attachmentItem.uploading = false;
 
       // Set the icon for the attachment.
       let iconURL = cloudFileAccount.iconURL;
       let itemIcon = attachmentItem.querySelector(".attachmentcell-icon");
       if (iconURL) {
@@ -2015,20 +2016,24 @@ async function uploadCloudAttachment(att
 
       attachmentItem.dispatchEvent(
         new CustomEvent("attachment-uploaded", {
           bubbles: true,
           cancelable: true,
         })
       );
     }
+    Services.telemetry.keyedScalarAdd(
+      "tb.filelink.uploaded_size",
+      cloudFileAccount.type,
+      attachment.size
+    );
   } else {
     let title;
     let msg;
-    let displayName = cloudFileAccounts.getDisplayName(cloudFileAccount);
     let bundle = getComposeBundle();
     let displayError = true;
     switch (statusCode) {
       case cloudFileAccounts.constants.authErr:
         title = bundle.getString("errorCloudFileAuth.title");
         msg = bundle.getFormattedString("errorCloudFileAuth.message", [
           displayName,
         ]);
@@ -4487,16 +4492,37 @@ function CompleteGenericSendMessage(msgT
     );
   } catch (ex) {
     Cu.reportError("GenericSendMessage FAILED: " + ex);
     ToggleWindowLock(false);
   }
   if (gMsgCompose && originalCharset != gMsgCompose.compFields.characterSet) {
     SetDocumentCharacterSet(gMsgCompose.compFields.characterSet);
   }
+
+  if (
+    msgType == Ci.nsIMsgCompDeliverMode.Now ||
+    msgType == Ci.nsIMsgCompDeliverMode.Later ||
+    msgType == Ci.nsIMsgCompDeliverMode.Background
+  ) {
+    let maxSize =
+      Services.prefs.getIntPref("mail.compose.big_attachments.threshold_kb") *
+      1024;
+    let items = [...document.getElementById("attachmentBucket").itemChildren];
+
+    // When any big attachment is not sent via filelink, increment
+    // `tb.filelink.ignored`.
+    if (
+      items.some(
+        item => item.attachment.size >= maxSize && !item.attachment.sendViaCloud
+      )
+    ) {
+      Services.telemetry.scalarAdd("tb.filelink.ignored", 1);
+    }
+  }
 }
 
 /**
  * Check if the given address is valid (contains a @).
  *
  * @param aAddress  The address string to check.
  */
 function isValidAddress(aAddress) {
--- a/mail/components/telemetry/Scalars.yaml
+++ b/mail/components/telemetry/Scalars.yaml
@@ -73,16 +73,43 @@ tb.compose:
     products:
       - 'thunderbird'
     kind: uint
     notification_emails:
       - "telemetry-client-dev@thunderbird.net"
     record_in_processes:
       - 'main'
 
+tb.filelink:
+  uploaded_size:
+    bug_numbers:
+      - 1615984
+    description: Accumulated file size (bytes) uploaded to filelink services, keyed by filelink provider type.
+    expires: never
+    products:
+      - 'thunderbird'
+    keyed: true
+    kind: uint
+    notification_emails:
+      - "telemetry-client-dev@thunderbird.net"
+    record_in_processes:
+      - 'main'
+
+  ignored:
+    bug_numbers:
+      - 1615984
+    description: How many times filelink suggestion are ignored.
+    expires: never
+    products:
+      - 'thunderbird'
+    kind: uint
+    notification_emails:
+      - "telemetry-client-dev@thunderbird.net"
+    record_in_processes:
+      - 'main'
 tb.mails:
   sent:
     bug_numbers:
       - 1615989
     description: How many emails are sent.
     expires: never
     products:
       - 'thunderbird'
--- a/mail/test/browser/cloudfile/browser.ini
+++ b/mail/test/browser/cloudfile/browser.ini
@@ -44,8 +44,9 @@ prefs =
 subsuite = thunderbird
 support-files =
   data/**
   html/**
 
 [browser_attachmentItem.js]
 [browser_attachmentUrls.js]
 [browser_notifications.js]
+[browser_filelinkTelemetry.js]
new file mode 100644
--- /dev/null
+++ b/mail/test/browser/cloudfile/browser_filelinkTelemetry.js
@@ -0,0 +1,123 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test telemetry related to filelink.
+ */
+
+let { TelemetryTestUtils } = ChromeUtils.import(
+  "resource://testing-common/TelemetryTestUtils.jsm"
+);
+let { gMockFilePicker, gMockFilePickReg } = ChromeUtils.import(
+  "resource://testing-common/mozmill/AttachmentHelpers.jsm"
+);
+let { gMockCloudfileManager } = ChromeUtils.import(
+  "resource://testing-common/mozmill/CloudfileHelpers.jsm"
+);
+let {
+  add_attachments,
+  add_cloud_attachments,
+  close_compose_window,
+  open_compose_new_mail,
+  setup_msg_contents,
+} = ChromeUtils.import("resource://testing-common/mozmill/ComposeHelpers.jsm");
+let { mc } = ChromeUtils.import(
+  "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+let {
+  get_notification_button,
+  wait_for_notification_to_stop,
+} = ChromeUtils.import(
+  "resource://testing-common/mozmill/NotificationBoxHelpers.jsm"
+);
+let { cloudFileAccounts } = ChromeUtils.import(
+  "resource:///modules/cloudFileAccounts.jsm"
+);
+let { close_window } = ChromeUtils.import(
+  "resource://testing-common/mozmill/WindowHelpers.jsm"
+);
+
+let cloudType = "default";
+let kInsertNotificationPref =
+  "mail.compose.big_attachments.insert_notification";
+
+let maxSize =
+  Services.prefs.getIntPref("mail.compose.big_attachments.threshold_kb") * 1024;
+
+add_task(function setupModule(module) {
+  requestLongerTimeout(2);
+
+  gMockCloudfileManager.register(cloudType);
+  gMockFilePickReg.register();
+
+  Services.prefs.setBoolPref(kInsertNotificationPref, true);
+});
+
+registerCleanupFunction(function teardownModule(module) {
+  gMockCloudfileManager.unregister(cloudType);
+  gMockFilePickReg.unregister();
+  Services.prefs.clearUserPref(kInsertNotificationPref);
+});
+
+let kBoxId = "compose-notification-bottom";
+let kNotificationValue = "bigAttachment";
+
+/**
+ * Check that we're counting file size uploaded.
+ */
+add_task(async function test_filelink_uploaded_size() {
+  Services.telemetry.clearScalars();
+  let testFile1Size = 495;
+  let testFile2Size = 637;
+  let totalSize = testFile1Size + testFile2Size;
+
+  gMockFilePicker.returnFiles = collectFiles([
+    "./data/testFile1",
+    "./data/testFile2",
+  ]);
+
+  let provider = cloudFileAccounts.getProviderForType(cloudType);
+  let cwc = open_compose_new_mail(mc);
+  let account = cloudFileAccounts.createAccount(cloudType);
+
+  add_cloud_attachments(cwc, account, false);
+  gMockCloudfileManager.resolveUploads();
+  wait_for_notification_to_stop(cwc, kBoxId, "bigAttachmentUploading");
+
+  let scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+  Assert.equal(
+    scalars["tb.filelink.uploaded_size"][provider.displayName],
+    totalSize,
+    "Count of uploaded size must be correct."
+  );
+  close_compose_window(cwc);
+});
+
+/**
+ * Check that we're counting filelink suggestion ignored.
+ */
+add_task(async function test_filelink_ignored() {
+  Services.telemetry.clearScalars();
+
+  let cwc = open_compose_new_mail(mc);
+  setup_msg_contents(
+    cwc,
+    "test@example.org",
+    "Testing ignoring filelink suggestion",
+    "Hello! "
+  );
+
+  // Multiple big attachments should be counted as one ignoring.
+  add_attachments(cwc, "http://www.example.com/1", maxSize);
+  add_attachments(cwc, "http://www.example.com/2", maxSize + 10);
+  add_attachments(cwc, "http://www.example.com/3", maxSize - 1);
+  cwc.click(cwc.eid("button-send"));
+  let scalars = TelemetryTestUtils.getProcessScalars("parent");
+  Assert.equal(
+    scalars["tb.filelink.ignored"],
+    1,
+    "Count of ignored times must be correct."
+  );
+  close_compose_window(cwc, true);
+  close_window(cwc);
+});
--- a/mail/test/browser/shared-modules/PromptHelpers.jsm
+++ b/mail/test/browser/shared-modules/PromptHelpers.jsm
@@ -84,16 +84,29 @@ var gMockPromptService = {
       text: aText,
     };
 
     this.fireCb();
 
     return this._will_return;
   },
 
+  confirmCheck(aParent, aDialogTitle, aText) {
+    this._promptState = {
+      method: "confirmCheck",
+      parent: aParent,
+      dialogTitle: aDialogTitle,
+      text: aText,
+    };
+
+    this.fireCb();
+
+    return this._will_return;
+  },
+
   confirmEx(
     aParent,
     aDialogTitle,
     aText,
     aButtonFlags,
     aButton0Title,
     aButton1Title,
     aButton2Title,
@@ -134,17 +147,17 @@ var gMockPromptService = {
     if (this._inout_value != null) {
       aValue.value = this._inout_value;
     }
 
     return this._will_return;
   },
 
   // Other dialogs should probably be mocked here, including alert,
-  // alertCheck, confirmCheck, etc.
+  // alertCheck, etc.
   // See:  http://mxr.mozilla.org/mozilla-central/source/embedding/components/
   //       windowwatcher/public/nsIPromptService.idl
 
   /* Sets the value that the alert, confirm, etc dialog will return to
    * the caller.
    */
   set returnValue(aReturn) {
     this._will_return = aReturn;