Bug 1729437 - Use the full body to pick encoding in MimeEncoder.jsm. r=mkmelin a=wsmwk
authorPing Chen <remotenonsense@gmail.com>
Fri, 10 Sep 2021 13:01:57 +0300
changeset 43404 ee5e8e4ad4308b94c28e672d8c7c082d0239d281
parent 43403 54778d6522344f040966867f2bed6920633cbb3e
child 43405 6a2f149f633567c2f38ff00f764ad58b04fe2429
push id30
push userthunderbird@calypsoblue.org
push dateFri, 24 Sep 2021 21:34:38 +0000
treeherdercomm-esr91@f6bebff25dd4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin, wsmwk
bugs1729437
Bug 1729437 - Use the full body to pick encoding in MimeEncoder.jsm. r=mkmelin a=wsmwk Differential Revision: https://phabricator.services.mozilla.com/D125136
mailnews/compose/src/MimeEncoder.jsm
mailnews/compose/test/unit/data/binary-after-plain.txt
mailnews/compose/test/unit/test_attachment.js
--- a/mailnews/compose/src/MimeEncoder.jsm
+++ b/mailnews/compose/src/MimeEncoder.jsm
@@ -29,20 +29,16 @@ class MimeEncoder {
     this._isMainBody = isMainBody;
     this._body = content;
     this._bodySize = content.length;
 
     // The encoding value will be used to set Content-Transfer-Encoding header
     // and encode this._body.
     this._encoding = "";
 
-    // Use no more than 1024 chars to pick encoding.
-    this._chunkSize = Math.min(1024, this._bodySize);
-    this._chunk = content.slice(0, this._chunkSize);
-
     // Flags used to pick encoding.
     this._highBitCount = 0;
     this._unPrintableCount = 0;
     this._ctrlCount = 0;
     this._nullCount = 0;
     this._hasCr = 0;
     this._hasLf = 0;
     this._hasCrLf = 0;
@@ -52,21 +48,21 @@ class MimeEncoder {
   /**
    * @type {string}
    */
   get encoding() {
     return this._encoding;
   }
 
   /**
-   * Use the combination of charset, content type and scanning this._chunk to
+   * Use the combination of charset, content type and scanning this._body to
    * decide what encoding it should have.
    */
   pickEncoding() {
-    this._analyzeChunk();
+    this._analyzeBody();
 
     let strictlyMime = Services.prefs.getBoolPref("mail.strictly_mime");
     let needsB64 = false;
     let isUsingQP = false;
 
     // Allow users to override our percentage-wise guess on whether
     // the file is text or binary.
     let forceB64 = Services.prefs.getBoolPref("mail.file_attach_binary");
@@ -132,17 +128,17 @@ class MimeEncoder {
         isCharsetMultiByte &&
         (this._contentType.startsWith("text") ||
           // text/vcard synonym
           this._contentType == "application/directory")
       ) {
         needsB64 = true;
       } else if (this._charset == "ISO-2022-JP") {
         this._encoding = "7bit";
-      } else if (encodeP && this._unPrintableCount > this._chunkSize / 10) {
+      } else if (encodeP && this._unPrintableCount > this._bodySize / 10) {
         // If the document contains more than 10% unprintable characters,
         // then that seems like a good candidate for base64 instead of
         // quoted-printable.
         needsB64 = true;
       } else if (encodeP) {
         this._encoding = "quoted-printable";
         isUsingQP = true;
       } else if (this._highBitCount > 0) {
@@ -188,32 +184,32 @@ class MimeEncoder {
     }
     if (!output.endsWith("\r\n")) {
       output += "\r\n";
     }
     return output;
   }
 
   /**
-   * Scan this._chunk to set flags that will be used by pickEncoding.
+   * Scan this._body to set flags that will be used by pickEncoding.
    */
-  _analyzeChunk() {
+  _analyzeBody() {
     let currentColumn = 0;
     let prevCharWasCr = false;
 
-    for (let i = 0; i < this._chunkSize; i++) {
-      let ch = this._chunk.charAt(i);
-      let charCode = this._chunk.charCodeAt(i);
+    for (let i = 0; i < this._bodySize; i++) {
+      let ch = this._body.charAt(i);
+      let charCode = this._body.charCodeAt(i);
       if (charCode > 126) {
         this._highBitCount++;
         this._unPrintableCount++;
       } else if (ch < " " && !"\t\r\n".includes(ch)) {
         this._unPrintableCount++;
         this._ctrlCount++;
-        if (ch == 0) {
+        if (ch == "\0") {
           this._nullCount++;
         }
       }
 
       if ("\r\n".includes(ch)) {
         if (ch == "\r") {
           if (prevCharWasCr) {
             this._hasCr = 1;
new file mode 100644
index 0000000000000000000000000000000000000000..cec0697428d00c1e23d7c301c082a7f2a4df669d
GIT binary patch
literal 1527
zc$}Tov5wO~5XSMjaN>{~=xMe@0->GV_3k<qLW1gO5EY%{_+(4LKE*zr=qOU~2s{K0
zk3+#@P=}5D&E8#06l)^e`6)`~pUZw|6|dE&uAkIZziZVv?7pgI9Ok+oCUw2<)2mH7
zUr$ZntE+LZ=B`b*?x$I?HtwZrx_UpoE-p`$S*y1<vzof`X17(FR&DC3^-i@zGj7|h
zy6xIQP3<rzPt}8}=ecgWFX;pTy9~FCav6RZVHt55)iPLPGN!?V#Gn`!17m0mj^P=2
z2A+Xu;2C%Zo`GlJ8F&Vsg=gVecov?8XW?0R7M_J?;W>B?o`dJ)n}g@zId~49gXiFt
z@Je_kyb@k1KLxxJUJ0*+SHkn~JUkE2!}IXG{I>8sJP*&q3-AKG058A`@B+Lb%K|UJ
z3-BVm2rt5m@FKhjFT#tmeefc@3SI@Tf>*(-;8pM{con=#);aI9EVP!5*0R!Cc3R6)
zYuRcoYprFkwJf$4`7RZab1b=*oJ;N{2a}7*$w)IS(Xh!CiY(o*gu_w}OFAs=u*Abs
z4@*8Q{jkk}i!2GTG{h1SOGPXhv2?@|5=%)eDY47{9j+*fC-mlUPS1(x8QncTrsMjI
zC_U-#@p!y<si4D}=<s>DxJ$>cblj%n&jr!%d&<x6D|+>KMe5y$_ous0?+K5HF6iOo
VTRIWXiJq>AUYtF=_TxW3;6EHKcP;<`
--- a/mailnews/compose/test/unit/test_attachment.js
+++ b/mailnews/compose/test/unit/test_attachment.js
@@ -146,8 +146,26 @@ async function testInput1() {
 
 var tests = [testInput0, testInput1];
 
 function run_test() {
   localAccountUtils.loadLocalMailAccount();
   tests.forEach(x => add_task(x));
   run_next_test();
 }
+
+/**
+ * Test that the full attachment content is used to pick the CTE.
+ */
+add_task(async function testBinaryAfterPlainTextAttachment() {
+  let testFile = do_get_file("data/binary-after-plain.txt");
+  await createMessage(testFile);
+  let msgData = mailTestUtils.loadMessageToString(
+    gDraftFolder,
+    mailTestUtils.firstMsgHdr(gDraftFolder)
+  );
+  // If only the first few chars are used, encoding will be incorrectly 7bit.
+  Assert.ok(
+    msgData.includes(
+      'Content-Disposition: attachment; filename="binary-after-plain.txt"\r\nContent-Transfer-Encoding: base64\r\n'
+    )
+  );
+});