Browse Source

utf8 backports

Frank-Rainer Grahl 3 months ago
parent
commit
52f3a9c4bf

+ 508 - 0
comm-release/patches/1389083-NewMessage.patch

@@ -0,0 +1,508 @@
+# HG changeset patch
+# User Sean Burke <sean@thunderbird.net>
+# Date 1666520198 -39600
+# Node ID 37f32ce1863bbee3639d6a0a75cbbc7c225b9dc7
+# Parent  07b10a62a5d560b3f6d4499180bf27c2fcc5c6c3
+Bug 1685414 - switch Thunderbird OAuth2 to desktop client auth. r=darktrojan,sancus  a=wsmwk
+
+Differential Revision: https://phabricator.services.mozilla.com/D158588
+
+diff --git a/calendar/providers/caldav/calDavCalendar.js b/calendar/providers/caldav/calDavCalendar.js
+--- a/calendar/providers/caldav/calDavCalendar.js
++++ b/calendar/providers/caldav/calDavCalendar.js
+@@ -1593,23 +1593,22 @@ calDavCalendar.prototype = {
+         if (this.mUri.host == "apidata.googleusercontent.com") {
+             if (!this.oauth) {
+                 let sessionId = this.id;
+                 let pwMgrId = "Google CalDAV v2";
+                 let authTitle = cal.l10n.getAnyString(
+                     "global", "commonDialogs", "EnterUserPasswordFor2", [this.name]
+                 );
+                 this.oauth =
+-                  new OAuth2(
+-                    OAUTH_BASE_URI + "oauth2/auth",
+-                    OAUTH_BASE_URI + "oauth2/token",
+-                    OAUTH_SCOPE,
+-                    OAUTH_CLIENT_ID,
+-                    OAUTH_HASH
+-                  );
++                    new OAuth2(OAUTH_SCOPE, {
++                        authorizationEndpoint: OAUTH_BASE_URI + "oauth2/auth",
++                        tokenEndpoint:  OAUTH_BASE_URI + "oauth2/token",
++                        clientId: OAUTH_CLIENT_ID,
++                        clientSecret: OAUTH_HASH,
++                    });
+ 
+                 this.oauth.requestWindowTitle = authTitle;
+                 this.oauth.requestWindowFeatures = "chrome,private,centerscreen,width=430,height=750";
+ 
+                 Object.defineProperty(this.oauth, "refreshToken", {
+                     get: function() {
+                         if (!this.mRefreshToken) {
+                             let pass = { value: null };
+diff --git a/calendar/providers/caldav/modules/CalDavSession.jsm.1685414.later b/calendar/providers/caldav/modules/CalDavSession.jsm.1685414.later
+new file mode 100644
+--- /dev/null
++++ b/calendar/providers/caldav/modules/CalDavSession.jsm.1685414.later
+@@ -0,0 +1,76 @@
++--- CalDavSession.jsm
+++++ CalDavSession.jsm
++@@ -25,23 +25,22 @@ class CalDavGoogleOAuth extends OAuth2 {
++   /**
++    * Constructs a new Google OAuth authentication provider
++    *
++    * @param {String} sessionId    The session id, used in the password manager
++    * @param {String} name         The user-readable description of this session
++    */
++   constructor(sessionId, name) {
++     /* eslint-disable no-undef */
++-    super(
++-      "https://accounts.google.com/o/oauth2/auth",
++-      "https://www.googleapis.com/oauth2/v3/token",
++-      "https://www.googleapis.com/auth/calendar",
++-      OAUTH_CLIENT_ID,
++-      OAUTH_HASH
++-    );
+++    super("https://www.googleapis.com/auth/calendar", {
+++      authorizationEndpoint: "https://accounts.google.com/o/oauth2/auth",
+++      tokenEndpoint: "https://www.googleapis.com/oauth2/v3/token",
+++      clientId: OAUTH_CLIENT_ID,
+++      clientSecret: OAUTH_HASH,
+++    });
++     /*  eslint-enable no-undef */
++ 
++     this.id = sessionId;
++     this.origin = "oauth:" + sessionId;
++     this.pwMgrId = "Google CalDAV v2";
++ 
++     this._maybeUpgrade(name);
++ 
++@@ -56,19 +55,19 @@ class CalDavGoogleOAuth extends OAuth2 {
++ 
++   /**
++    * If no token is found for "Google CalDAV v2", this is either a new session (in which case
++    * it should use Thunderbird's credentials) or it's already using Thunderbird's credentials.
++    * Detect those situations and switch credentials if necessary.
++    */
++   _maybeUpgrade() {
++     if (!this.refreshToken) {
++-      [this.clientId, this.consumerSecret] = OAuth2Providers.getIssuerDetails(
++-        "accounts.google.com"
++-      );
+++      const issuerDetails = OAuth2Providers.getIssuerDetails("accounts.google.com");
+++      this.clientId = issuerDetails.clientId;
+++      this.consumerSecret = issuerDetails.clientSecret;
++       this.origin = "oauth://accounts.google.com";
++       this.pwMgrId = "https://www.googleapis.com/auth/calendar";
++     }
++   }
++ 
++   /**
++    * Returns true if the token has expired, or will expire within the grace time.
++    */
++@@ -262,17 +261,19 @@ class CalDavTestOAuth extends CalDavGoog
++     // I don't know why, but tests refuse to work with a plain HTTP endpoint
++     // (the request is redirected to HTTPS, which we're not listening to).
++     // Just use an HTTPS endpoint.
++     this.redirectionEndpoint = "https://localhost";
++   }
++ 
++   _maybeUpgrade() {
++     if (!this.refreshToken) {
++-      [this.clientId, this.consumerSecret] = OAuth2Providers.getIssuerDetails("mochi.test");
+++      const issuerDetails = OAuth2Providers.getIssuerDetails("mochi.test");
+++      this.clientId = issuerDetails.clientId;
+++      this.consumerSecret = issuerDetails.clientSecret;
++       this.origin = "oauth://mochi.test";
++       this.pwMgrId = "test_scope";
++     }
++   }
++ }
++ 
++ /**
++  * A session for the caldav provider. Two or more calendars can share a session if they have the
+diff --git a/calendar/test/unit/test_caldav_requests.js.1685414.later b/calendar/test/unit/test_caldav_requests.js.1685414.later
+new file mode 100644
+--- /dev/null
++++ b/calendar/test/unit/test_caldav_requests.js.1685414.later
+@@ -0,0 +1,23 @@
++--- test_caldav_requests.js
+++++ test_caldav_requests.js
++@@ -949,8 +949,20 @@ add_task(async function test_caldav_clie
++ add_task(async function test_caldav_sync() {
++   gServer.reset();
++   let uri = gServer.uri("/calendars/xpcshell/events/");
++   gMockCalendar.session = gServer.session;
++   let webDavSync = new CalDavWebDavSyncHandler(gMockCalendar, uri);
++   await webDavSync.doWebDAVSync();
++   ok(webDavSync.logXML.includes("イベント"), "Non-ASCII text should be parsed correctly");
++ });
+++
+++add_task(function test_can_get_google_adapter() {
+++  // Initialize a session with bogus values
+++  const session = new CalDavSession("xpcshell@example.com", "xpcshell");
+++
+++  // We don't have a facility for actually testing our Google CalDAV requests,
+++  // but we can at least verify that the adapter looks okay at a glance
+++  equal(
+++    session.authAdapters["apidata.googleusercontent.com"].authorizationEndpoint,
+++    "https://accounts.google.com/o/oauth2/auth"
+++  );
+++});
+diff --git a/mailnews/addrbook/modules/CardDAVUtils.jsm.1685414.later b/mailnews/addrbook/modules/CardDAVUtils.jsm.1685414.later
+new file mode 100644
+--- /dev/null
++++ b/mailnews/addrbook/modules/CardDAVUtils.jsm.1685414.later
+@@ -0,0 +1,35 @@
++--- CardDAVUtils.jsm
+++++ CardDAVUtils.jsm
++@@ -326,30 +326,19 @@ var CardDAVUtils = {
++             <current-user-privilege-set/>
++           </prop>
++         </propfind>`,
++     };
++ 
++     let details = OAuth2Providers.getHostnameDetails(url.host);
++     if (details) {
++       let [issuer, scope] = details;
++-      let [
++-        clientId,
++-        clientSecret,
++-        authorizationEndpoint,
++-        tokenEndpoint,
++-      ] = OAuth2Providers.getIssuerDetails(issuer);
+++      let issuerDetails = OAuth2Providers.getIssuerDetails(issuer);
++ 
++-      oAuth = new OAuth2(
++-        authorizationEndpoint,
++-        tokenEndpoint,
++-        scope,
++-        clientId,
++-        clientSecret
++-      );
+++      oAuth = new OAuth2(scope, issuerDetails);
++       oAuth._isNew = true;
++       oAuth._loginOrigin = `oauth://${issuer}`;
++       oAuth._scope = scope;
++       for (let login of Services.logins.findLogins(
++         oAuth._loginOrigin,
++         null,
++         ""
++       )) {
+diff --git a/mailnews/base/src/msgOAuth2Module.js b/mailnews/base/src/msgOAuth2Module.js
+--- a/mailnews/base/src/msgOAuth2Module.js
++++ b/mailnews/base/src/msgOAuth2Module.js
+@@ -49,41 +49,30 @@ OAuth2Module.prototype = {
+       }
+       else
+         return false;
+     }
+ 
+     // Find the app key we need for the OAuth2 string. Eventually, this should
+     // be using dynamic client registration, but there are no current
+     // implementations that we can test this with.
+-    let [
+-      clientId,
+-      clientSecret,
+-      authorizationEndpoint,
+-      tokenEndpoint,
+-    ] = OAuth2Providers.getIssuerDetails(issuer);
+-    if (!clientId) {
++    const issuerDetails = OAuth2Providers.getIssuerDetails(issuer);
++    if (!issuerDetails.clientId) {
+       return false;
+     }
+ 
+     // Username is needed to generate the XOAUTH2 string.
+     this._username = aUsername;
+     // loginOrigin is needed to save the refresh token in the password manager.
+     this._loginOrigin = "oauth://" + issuer;
+     // We use the scope to indicate realm when storing in the password manager.
+     this._scope = scope;
+ 
+     // Define the OAuth property and store it.
+-    this._oauth = new OAuth2(
+-      authorizationEndpoint,
+-      tokenEndpoint,
+-      scope,
+-      clientId,
+-      clientSecret
+-    );
++    this._oauth = new OAuth2(scope, issuerDetails);
+ 
+     // Try hinting the username...
+     this._oauth.extraAuthParams = [
+       ["login_hint", aUsername]
+     ];
+ 
+     // Set the window title to something more useful than "Unnamed"
+     this._oauth.requestWindowTitle =
+diff --git a/mailnews/base/util/OAuth2.jsm b/mailnews/base/util/OAuth2.jsm
+--- a/mailnews/base/util/OAuth2.jsm
++++ b/mailnews/base/util/OAuth2.jsm
+@@ -16,49 +16,46 @@ Cu.importGlobalProperties(["fetch"]);
+ 
+ // Only allow one connecting window per endpoint.
+ var gConnecting = {};
+ 
+ /**
+  * Constructor for the OAuth2 object.
+  *
+  * @constructor
+- * @param {string} authorizationEndpoint - The authorization endpoint as
+- *   defined by RFC 6749 Section 3.1.
+- * @param {string} tokenEndpoint - The token endpoint as defined by
+- *   RFC 6749 Section 3.2.
+  * @param {?string} scope - The scope as specified by RFC 6749 Section 3.3.
+  *   Will not be included in the requests if falsy.
+- * @param {string} clientId - The client_id as specified by RFC 6749 Section
+- *   2.3.1.
+- * @param {string} [clientSecret=null] - The client_secret as specified in
+- *    RFC 6749 section 2.3.1. Will not be included in the requests if null.
++ * @param {string} issuerDetails.authorizationEndpoint - The authorization
++ *   endpoint as defined by RFC 6749 Section 3.1.
++ * @param {string} issuerDetails.clientId - The client_id as specified by RFC
++ *   6749 Section 2.3.1.
++ * @param {string} issuerDetails.clientSecret - The client_secret as specified
++ *   in RFC 6749 section 2.3.1. Will not be included in the requests if null.
++ * @param {string} issuerDetails.redirectionEndpoint - The redirect_uri as
++ *   specified by RFC 6749 section 3.1.2.
++ * @param {string} issuerDetails.tokenEndpoint - The token endpoint as defined
++ *   by RFC 6749 Section 3.2.
+  */
+-function OAuth2(
+-  authorizationEndpoint,
+-  tokenEndpoint,
+-  scope,
+-  clientId,
+-  clientSecret = null
+-) {
+-  this.authorizationEndpoint = authorizationEndpoint;
+-  this.tokenEndpoint = tokenEndpoint;
++function OAuth2(scope, issuerDetails) {
+   this.scope = scope;
+-  this.clientId = clientId;
+-  this.consumerSecret = clientSecret;
++  this.authorizationEndpoint = issuerDetails.authorizationEndpoint;
++  this.clientId = issuerDetails.clientId;
++  this.consumerSecret = issuerDetails.clientSecret || null;
++  this.redirectionEndpoint =
++    issuerDetails.redirectionEndpoint || "http://localhost";
++  this.tokenEndpoint = issuerDetails.tokenEndpoint;
+ 
+   this.extraAuthParams = [];
+ 
+   this.log = Log4Moz.getConfiguredLogger("TBOAuth");
+ }
+ 
+ OAuth2.prototype = {
+   clientId: null,
+   consumerSecret: null,
+-  redirectionEndpoint: "http://localhost",
+   requestWindowURI: "chrome://messenger/content/browserRequest.xul",
+   requestWindowFeatures: "chrome,private,centerscreen,width=980,height=750",
+   requestWindowTitle: "",
+   scope: null,
+ 
+   accessToken: null,
+   refreshToken: null,
+   tokenExpires: 0,
+@@ -184,19 +181,19 @@ OAuth2.prototype = {
+       this._browserRequest._listener._cleanUp();
+     }
+     delete this._browserRequest;
+   },
+ 
+   // @see RFC 6749 section 4.1.2: Authorization Response
+   onAuthorizationReceived(aURL) {
+     this.log.info("OAuth2 authorization received: url=" + aURL);
+-    let params = new URLSearchParams(aURL.split("?", 2)[1]);
+-    if (params.has("code")) {
+-      this.requestAccessToken(params.get("code"), false);
++    const url = new URL(aURL);
++    if (url.searchParams.has("code")) {
++      this.requestAccessToken(url.searchParams.get("code"), false);
+     } else {
+       this.onAuthorizationFailed(null, aURL);
+     }
+   },
+ 
+   onAuthorizationFailed(aError, aData) {
+     this.connectFailureCallback(aData);
+   },
+diff --git a/mailnews/base/util/OAuth2Providers.jsm b/mailnews/base/util/OAuth2Providers.jsm
+--- a/mailnews/base/util/OAuth2Providers.jsm
++++ b/mailnews/base/util/OAuth2Providers.jsm
+@@ -31,80 +31,96 @@ var kHostnames = new Map([
+   ["imap.aol.com", ["login.aol.com", "mail-w"]],
+   ["pop.aol.com", ["login.aol.com", "mail-w"]],
+   ["smtp.aol.com", ["login.aol.com", "mail-w"]],
+ 
+   [
+     "outlook.office365.com",
+     [
+       "login.microsoftonline.com",
+-      "https://outlook.office365.com/IMAP.AccessAsUser.All https://outlook.office365.com/POP.AccessAsUser.All https://outlook.office365.com/SMTP.Send offline_access",
++      "https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send offline_access",
+     ],
+   ],
+   [
+     "smtp.office365.com",
+     [
+       "login.microsoftonline.com",
+-      "https://outlook.office365.com/IMAP.AccessAsUser.All https://outlook.office365.com/POP.AccessAsUser.All https://outlook.office365.com/SMTP.Send offline_access",
++      "https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send offline_access",
+     ],
+   ],
+ ]);
+ 
+ /**
+  * Map of issuers to clientId, clientSecret, authorizationEndpoint, tokenEndpoint.
+  * Issuer is a unique string for the organization that a Thunderbird account
+  * was registered at.
+  *
+  * For the moment these details are hard-coded, since dynamic client
+  * registration is not yet supported. Don't copy these values for your
+  * own application - register one for yourself! This code (and possibly even the
+  * registration itself) will disappear when this is switched to dynamic
+  * client registration.
+  */
+ var kIssuers = new Map ([
+-  ["accounts.google.com", [
+-    '406964657835-aq8lmia8j95dhl1a2bvharmfk3t1hgqj.apps.googleusercontent.com',
+-    'kSmqreRr0qwBWJgbf5Y-PjSU',
+-    'https://accounts.google.com/o/oauth2/auth',
+-    'https://www.googleapis.com/oauth2/v3/token'
+-  ]],
+-  ["o2.mail.ru", [
+-    'thunderbird',
+-    'I0dCAXrcaNFujaaY',
+-    'https://o2.mail.ru/login',
+-    'https://o2.mail.ru/token'
+-  ]],
+-  ["oauth.yandex.com", [
+-    "2a00bba7374047a6ab79666485ffce31",
+-    "3ded85b4ec574c2187a55dc49d361280",
+-    "https://oauth.yandex.com/authorize",
+-    "https://oauth.yandex.com/token",
+-  ]],
+-  ["login.yahoo.com", [
+-    'dj0yJmk9NUtCTWFMNVpTaVJmJmQ9WVdrOVJ6UjVTa2xJTXpRbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD0yYw--',
+-    'f2de6a30ae123cdbc258c15e0812799010d589cc',
+-    'https://api.login.yahoo.com/oauth2/request_auth',
+-    'https://api.login.yahoo.com/oauth2/get_token'
+-  ]],
+-  ["login.aol.com", [
+-    'dj0yJmk9OXRHc1FqZHRQYzVvJmQ9WVdrOU1UQnJOR0pvTjJrbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD02NQ--',
+-    '79c1c11991d148ddd02a919000d69879942fc278',
+-    'https://api.login.aol.com/oauth2/request_auth',
+-    'https://api.login.aol.com/oauth2/get_token'
+-  ]],
+-
++  [
++    "accounts.google.com",
++    {
++      clientId:
++        "406964657835-aq8lmia8j95dhl1a2bvharmfk3t1hgqj.apps.googleusercontent.com",
++      clientSecret: "kSmqreRr0qwBWJgbf5Y-PjSU",
++      authorizationEndpoint: "https://accounts.google.com/o/oauth2/auth",
++      tokenEndpoint: "https://www.googleapis.com/oauth2/v3/token",
++    },
++  ],
++  [
++    "o2.mail.ru",
++    {
++      clientId: "thunderbird",
++      clientSecret: "I0dCAXrcaNFujaaY",
++      authorizationEndpoint: "https://o2.mail.ru/login",
++      tokenEndpoint: "https://o2.mail.ru/token",
++    },
++  ],
++  [
++    "oauth.yandex.com",
++    {
++      clientId: "2a00bba7374047a6ab79666485ffce31",
++      clientSecret: "3ded85b4ec574c2187a55dc49d361280",
++      authorizationEndpoint: "https://oauth.yandex.com/authorize",
++      tokenEndpoint: "https://oauth.yandex.com/token",
++    },
++  ],
++  [
++    "login.yahoo.com",
++    {
++      clientId: "dj0yJmk9NUtCTWFMNVpTaVJmJmQ9WVdrOVJ6UjVTa2xJTXpRbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD0yYw--",
++      clientSecret: "f2de6a30ae123cdbc258c15e0812799010d589cc",
++      authorizationEndpoint: "https://api.login.yahoo.com/oauth2/request_auth",
++      tokenEndpoint: "https://api.login.yahoo.com/oauth2/get_token",
++    },
++  ],
++  [
++    "login.aol.com",
++    {
++      clientId:
++        "dj0yJmk9OXRHc1FqZHRQYzVvJmQ9WVdrOU1UQnJOR0pvTjJrbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD02NQ--",
++      clientSecret: "79c1c11991d148ddd02a919000d69879942fc278",
++      authorizationEndpoint: "https://api.login.aol.com/oauth2/request_auth",
++      tokenEndpoint: "https://api.login.aol.com/oauth2/get_token",
++    },
++  ],
+   [
+     "login.microsoftonline.com",
+-    [
+-      "08162f7c-0fd2-4200-a84a-f25a4db0b584", // Application (client) ID
+-      "TxRBilcHdC6WGBee]fs?QR:SJ8nI[g82", // @see App registrations | Certificates & secrets
++    {
++      clientId: "9e5f94bc-e8a4-4e73-b8be-63364c29d753", // Application (client) ID
+       // https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints
+-      "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
+-      "https://login.microsoftonline.com/common/oauth2/v2.0/token",
+-    ],
++      authorizationEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
++      tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
++      redirectionEndpoint: "https://localhost",
++    },
+   ],
+ ]);
+ 
+ /**
+  * OAuth2Providers: Methods to lookup OAuth2 parameters for supported OAuth2
+  * providers.
+  */
+ var OAuth2Providers = {
+diff --git a/mailnews/base/util/OAuth2Providers.jsm.1685414.later b/mailnews/base/util/OAuth2Providers.jsm.1685414.later
+new file mode 100644
+--- /dev/null
++++ b/mailnews/base/util/OAuth2Providers.jsm.1685414.later
+@@ -0,0 +1,32 @@
++--- OAuth2Providers.jsm
+++++ OAuth2Providers.jsm
++@@ -74,80 +74,91 @@ var kHostnames = new Map([
++   // For testing purposes.
++   [
++     "mochi.test",
++-    [
++-      "test_client_id",
++-      "test_secret",
++-      "http://mochi.test:8888/browser/comm/mail/components/addrbook/test/browser/data/redirect_auto.sjs",
++-      "http://mochi.test:8888/browser/comm/mail/components/addrbook/test/browser/data/token.sjs",
++-    ],
+++    {
+++      clientId: "test_client_id",
+++      clientSecret: "test_secret",
+++      authorizationEndpoint:
+++        "http://mochi.test:8888/browser/comm/mail/components/addrbook/test/browser/data/redirect_auto.sjs",
+++      tokenEndpoint:
+++        "http://mochi.test:8888/browser/comm/mail/components/addrbook/test/browser/data/token.sjs",
+++      // I don't know why, but tests refuse to work with a plain HTTP endpoint
+++      // (the request is redirected to HTTPS, which we're not listening to).
+++      // Just use an HTTPS endpoint.
+++      redirectionEndpoint: "https://localhost",
+++    },
++   ],
++ ]);
++ 
++ /**
++  * OAuth2Providers: Methods to lookup OAuth2 parameters for supported OAuth2
++  * providers.
++  */
++ var OAuth2Providers = {

+ 35 - 0
comm-release/patches/1685414-2-1027.patch

@@ -0,0 +1,35 @@
+# HG changeset patch
+# User Frank-Rainer Grahl <frgrahl@gmx.net>
+# Date 1720638787 -7200
+# Parent  efbfc5592504be71e46776dd7556776a04d7b746
+Bug 1685414 - Restore old code. r=me a=me
+
+new URL(foo) is currently disliked for whatever reason.
+
+diff --git a/mailnews/base/util/OAuth2.jsm b/mailnews/base/util/OAuth2.jsm
+--- a/mailnews/base/util/OAuth2.jsm
++++ b/mailnews/base/util/OAuth2.jsm
+@@ -181,19 +181,20 @@ OAuth2.prototype = {
+       this._browserRequest._listener._cleanUp();
+     }
+     delete this._browserRequest;
+   },
+ 
+   // @see RFC 6749 section 4.1.2: Authorization Response
+   onAuthorizationReceived(aURL) {
+     this.log.info("OAuth2 authorization received: url=" + aURL);
+-    const url = new URL(aURL);
+-    if (url.searchParams.has("code")) {
+-      this.requestAccessToken(url.searchParams.get("code"), false);
++    // Services.console.logStringMessage("OAuth2 authorization received: url=" + aURL);
++    let params = new URLSearchParams(aURL.split("?", 2)[1]);
++    if (params.has("code")) {
++      this.requestAccessToken(params.get("code"), false);
+     } else {
+       this.onAuthorizationFailed(null, aURL);
+     }
+   },
+ 
+   onAuthorizationFailed(aError, aData) {
+     this.connectFailureCallback(aData);
+   },

+ 182 - 0
comm-release/patches/1810760-1-1027.patch

@@ -0,0 +1,182 @@
+# HG changeset patch
+# User Sean Burke <sean@thunderbird.net>
+# Date 1674262742 0
+# Node ID 88d80acadbab4acb4d81cb2934caa807cd59e85a
+# Parent  fd107379a6d2466cda36004f6853233650e29a94
+Bug 1810760 - don't use CORS with client requests. r=sancus a=wsmwk
+
+Differential Revision: https://phabricator.services.mozilla.com/D167449
+
+diff --git a/mailnews/base/util/OAuth2.jsm b/mailnews/base/util/OAuth2.jsm
+--- a/mailnews/base/util/OAuth2.jsm
++++ b/mailnews/base/util/OAuth2.jsm
+@@ -34,39 +34,40 @@ var gConnecting = {};
+  * @param {string} issuerDetails.tokenEndpoint - The token endpoint as defined
+  *   by RFC 6749 Section 3.2.
+  */
+ function OAuth2(scope, issuerDetails) {
+   this.scope = scope;
+   this.authorizationEndpoint = issuerDetails.authorizationEndpoint;
+   this.clientId = issuerDetails.clientId;
+   this.consumerSecret = issuerDetails.clientSecret || null;
++  this.useCORS = issuerDetails.useCORS;
+   this.redirectionEndpoint =
+     issuerDetails.redirectionEndpoint || "http://localhost";
+   this.tokenEndpoint = issuerDetails.tokenEndpoint;
+ 
+   this.extraAuthParams = [];
+ 
+   this.log = Log4Moz.getConfiguredLogger("TBOAuth");
+ }
+ 
+ OAuth2.prototype = {
+   clientId: null,
+   consumerSecret: null,
+   requestWindowURI: "chrome://messenger/content/browserRequest.xul",
+   requestWindowFeatures: "chrome,private,centerscreen,width=980,height=750",
+   requestWindowTitle: "",
+   scope: null,
++  useCORS: true,
+ 
+   accessToken: null,
+   refreshToken: null,
+   tokenExpires: 0,
+ 
+   connect(aSuccess, aFailure, aWithUI, aRefresh) {
+-
+     this.connectSuccessCallback = aSuccess;
+     this.connectFailureCallback = aFailure;
+ 
+     if (!aRefresh && this.accessToken) {
+       aSuccess();
+     } else if (this.refreshToken) {
+       this.requestAccessToken(this.refreshToken, true);
+     } else {
+@@ -226,21 +227,27 @@ OAuth2.prototype = {
+       this.log.info(
+         `Making access token request to the token endpoint: ${this.tokenEndpoint}`
+       );
+       data.append("grant_type", "authorization_code");
+       data.append("code", aCode);
+       data.append("redirect_uri", this.redirectionEndpoint);
+     }
+ 
+-    fetch(this.tokenEndpoint, {
++    const fetchOptions = {
+       method: "POST",
+       cache: "no-cache",
+       body: data,
+-    })
++    };
++
++    if (!this.useCORS) {
++      fetchOptions.mode = "no-cors";
++    }
++
++    fetch(this.tokenEndpoint, fetchOptions)
+       .then(response => response.json())
+       .then(result => {
+         let resultStr = JSON.stringify(result, null, 2);
+         if ("error" in result) {
+           // RFC 6749 section 5.2. Error Response
+           this.log.info(
+             `The authorization server returned an error response: ${resultStr}`
+           );
+diff --git a/mailnews/base/util/OAuth2Providers.jsm b/mailnews/base/util/OAuth2Providers.jsm
+--- a/mailnews/base/util/OAuth2Providers.jsm
++++ b/mailnews/base/util/OAuth2Providers.jsm
+@@ -63,63 +63,69 @@ var kIssuers = new Map ([
+   [
+     "accounts.google.com",
+     {
+       clientId:
+         "406964657835-aq8lmia8j95dhl1a2bvharmfk3t1hgqj.apps.googleusercontent.com",
+       clientSecret: "kSmqreRr0qwBWJgbf5Y-PjSU",
+       authorizationEndpoint: "https://accounts.google.com/o/oauth2/auth",
+       tokenEndpoint: "https://www.googleapis.com/oauth2/v3/token",
++      useCORS: true,
+     },
+   ],
+   [
+     "o2.mail.ru",
+     {
+       clientId: "thunderbird",
+       clientSecret: "I0dCAXrcaNFujaaY",
+       authorizationEndpoint: "https://o2.mail.ru/login",
+       tokenEndpoint: "https://o2.mail.ru/token",
++      useCORS: true,
+     },
+   ],
+   [
+     "oauth.yandex.com",
+     {
+       clientId: "2a00bba7374047a6ab79666485ffce31",
+       clientSecret: "3ded85b4ec574c2187a55dc49d361280",
+       authorizationEndpoint: "https://oauth.yandex.com/authorize",
+       tokenEndpoint: "https://oauth.yandex.com/token",
++      useCORS: true,
+     },
+   ],
+   [
+     "login.yahoo.com",
+     {
+       clientId: "dj0yJmk9NUtCTWFMNVpTaVJmJmQ9WVdrOVJ6UjVTa2xJTXpRbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD0yYw--",
+       clientSecret: "f2de6a30ae123cdbc258c15e0812799010d589cc",
+       authorizationEndpoint: "https://api.login.yahoo.com/oauth2/request_auth",
+       tokenEndpoint: "https://api.login.yahoo.com/oauth2/get_token",
++      useCORS: true,
+     },
+   ],
+   [
+     "login.aol.com",
+     {
+       clientId:
+         "dj0yJmk9OXRHc1FqZHRQYzVvJmQ9WVdrOU1UQnJOR0pvTjJrbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD02NQ--",
+       clientSecret: "79c1c11991d148ddd02a919000d69879942fc278",
+       authorizationEndpoint: "https://api.login.aol.com/oauth2/request_auth",
+       tokenEndpoint: "https://api.login.aol.com/oauth2/get_token",
++      useCORS: true,
+     },
+   ],
+   [
+     "login.microsoftonline.com",
+     {
+       clientId: "9e5f94bc-e8a4-4e73-b8be-63364c29d753", // Application (client) ID
+       // https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints
+       authorizationEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
+       tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
+       redirectionEndpoint: "https://localhost",
++      useCORS: true,
+     },
+   ],
+ ]);
+ 
+ /**
+  * OAuth2Providers: Methods to lookup OAuth2 parameters for supported OAuth2
+  * providers.
+  */
+diff --git a/mailnews/base/util/OAuth2Providers.jsm.1810760-1.later b/mailnews/base/util/OAuth2Providers.jsm.1810760-1.later
+new file mode 100644
+--- /dev/null
++++ b/mailnews/base/util/OAuth2Providers.jsm.1810760-1.later
+@@ -0,0 +1,20 @@
++--- OAuth2Providers.jsm
+++++ OAuth2Providers.jsm
++@@ -148,16 +154,17 @@ var kIssuers = new Map([
++       authorizationEndpoint:
++         "http://mochi.test:8888/browser/comm/mail/components/addrbook/test/browser/data/redirect_auto.sjs",
++       tokenEndpoint:
++         "http://mochi.test:8888/browser/comm/mail/components/addrbook/test/browser/data/token.sjs",
++       // I don't know why, but tests refuse to work with a plain HTTP endpoint
++       // (the request is redirected to HTTPS, which we're not listening to).
++       // Just use an HTTPS endpoint.
++       redirectionEndpoint: "https://localhost",
+++      useCORS: true,
++     },
++   ],
++ ]);
++ 
++ /**
++  * OAuth2Providers: Methods to lookup OAuth2 parameters for supported OAuth2
++  * providers.
++  */

+ 335 - 0
comm-release/patches/1810760-2-1027.patch

@@ -0,0 +1,335 @@
+# HG changeset patch
+# User Sean Burke <sean@thunderbird.net>
+# Date 1674778794 28800
+# Node ID bd348a7dd1a9a55cb65afe08f39022fbcd032f84
+# Parent  10081d9cc235c1d91baa16dc33b65a81e089932c
+Bug 1810760 - use HTTP channels for Microsoft oauth. r=darktrojan a=wsmwk
+
+Differential Revision: https://phabricator.services.mozilla.com/D168024
+
+diff --git a/mailnews/base/util/OAuth2.jsm b/mailnews/base/util/OAuth2.jsm
+--- a/mailnews/base/util/OAuth2.jsm
++++ b/mailnews/base/util/OAuth2.jsm
+@@ -34,34 +34,34 @@ var gConnecting = {};
+  * @param {string} issuerDetails.tokenEndpoint - The token endpoint as defined
+  *   by RFC 6749 Section 3.2.
+  */
+ function OAuth2(scope, issuerDetails) {
+   this.scope = scope;
+   this.authorizationEndpoint = issuerDetails.authorizationEndpoint;
+   this.clientId = issuerDetails.clientId;
+   this.consumerSecret = issuerDetails.clientSecret || null;
+-  this.useCORS = issuerDetails.useCORS;
+   this.redirectionEndpoint =
+     issuerDetails.redirectionEndpoint || "http://localhost";
+   this.tokenEndpoint = issuerDetails.tokenEndpoint;
++  this.useHttpChannel = issuerDetails.useHttpChannel || false;
+ 
+   this.extraAuthParams = [];
+ 
+   this.log = Log4Moz.getConfiguredLogger("TBOAuth");
+ }
+ 
+ OAuth2.prototype = {
+   clientId: null,
+   consumerSecret: null,
+   requestWindowURI: "chrome://messenger/content/browserRequest.xul",
+   requestWindowFeatures: "chrome,private,centerscreen,width=980,height=750",
+   requestWindowTitle: "",
+   scope: null,
+-  useCORS: true,
++  useHttpChannel: false,
+ 
+   accessToken: null,
+   refreshToken: null,
+   tokenExpires: 0,
+ 
+   connect(aSuccess, aFailure, aWithUI, aRefresh) {
+     this.connectSuccessCallback = aSuccess;
+     this.connectFailureCallback = aFailure;
+@@ -227,58 +227,143 @@ OAuth2.prototype = {
+       this.log.info(
+         `Making access token request to the token endpoint: ${this.tokenEndpoint}`
+       );
+       data.append("grant_type", "authorization_code");
+       data.append("code", aCode);
+       data.append("redirect_uri", this.redirectionEndpoint);
+     }
+ 
+-    const fetchOptions = {
+-      method: "POST",
+-      cache: "no-cache",
+-      body: data,
+-    };
++    // Microsoft's OAuth explicitly breaks on receiving an Origin header, and
++    // we don't have control over whether fetch() sends Origin. Later versions
++    // of Gecko don't send it in this instance, but we have to work around it in
++    // this one.
++    if (this.useHttpChannel) {
++      // Get the request body as a string-based stream
++      let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
++        Ci.nsIStringInputStream
++      );
++
++      let body = data.toString();
++      stream.setUTF8Data(body, body.length);
++
++      // Set up an HTTP channel in order to make our request
++      let channel = Services.io.newChannelFromURI(
++        Services.io.newURI(this.tokenEndpoint),
++        null,
++        Services.scriptSecurityManager.getSystemPrincipal(),
++        null,
++        Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
++        Ci.nsIContentPolicy.TYPE_OTHER
++      );
++
++      channel.QueryInterface(Ci.nsIHttpChannel);
++      channel.setRequestHeader(
++        "Content-Type",
++        "application/x-www-form-urlencoded",
++        false
++      );
++
++      channel.QueryInterface(Ci.nsIUploadChannel);
++      channel.setUploadStream(stream, "application/x-www-form-urlencoded", -1);
++      channel.requestMethod = "POST";
++
++      // Set up a response handler for our request
++      let listener = Cc["@mozilla.org/network/stream-loader;1"].createInstance(
++        Ci.nsIStreamLoader
++      );
++
++      const oauth = this;
++
++      listener.init({
++        onStreamComplete(loader, context, status, resultLength, resultBytes) {
++          try {
++            let resultStr = new TextDecoder().decode(
++              Uint8Array.from(resultBytes)
++            );
++            let result = JSON.parse(resultStr);
+ 
+-    if (!this.useCORS) {
+-      fetchOptions.mode = "no-cors";
+-    }
++            if ("error" in result) {
++              // RFC 6749 section 5.2. Error Response
++              oauth.log.info(
++                `The authorization server returned an error response: ${resultStr}`
++              );
++              // Typically in production this would be {"error": "invalid_grant"}.
++              // That is, the token expired or was revoked (user changed password?).
++              // Reset the tokens we have and call success so that the auth flow
++              // will be re-triggered.
++              oauth.accessToken = null;
++              oauth.refreshToken = null;
++              oauth.connectSuccessCallback();
++              return;
++            }
++
++            // RFC 6749 section 5.1. Successful Response
++            oauth.log.info(
++              `Successful response from the authorization server: ${resultStr}`
++            );
++            oauth.accessToken = result.access_token;
++            if ("refresh_token" in result) {
++              oauth.refreshToken = result.refresh_token;
++            }
++            if ("expires_in" in result) {
++              oauth.tokenExpires =
++                new Date().getTime() + result.expires_in * 1000;
++            } else {
++              oauth.tokenExpires = Number.MAX_VALUE;
++            }
+ 
+-    fetch(this.tokenEndpoint, fetchOptions)
+-      .then(response => response.json())
+-      .then(result => {
+-        let resultStr = JSON.stringify(result, null, 2);
+-        if ("error" in result) {
+-          // RFC 6749 section 5.2. Error Response
++            oauth.connectSuccessCallback();
++          } catch (err) {
++            oauth.log.info(`Connection to authorization server failed: ${err}`);
++            oauth.connectFailureCallback(err);
++          }
++        },
++      });
++
++      // Make the request
++      channel.asyncOpen(listener, channel);
++    } else {
++      fetch(this.tokenEndpoint, {
++        method: "POST",
++        cache: "no-cache",
++        body: data,
++      })
++        .then(response => response.json())
++        .then(result => {
++          let resultStr = JSON.stringify(result, null, 2);
++          if ("error" in result) {
++            // RFC 6749 section 5.2. Error Response
++            this.log.info(
++              `The authorization server returned an error response: ${resultStr}`
++            );
++            // Typically in production this would be {"error": "invalid_grant"}.
++            // That is, the token expired or was revoked (user changed password?).
++            // Reset the tokens we have and call success so that the auth flow
++            // will be re-triggered.
++            this.accessToken = null;
++            this.refreshToken = null;
++            this.connectSuccessCallback();
++            return;
++          }
++
++          // RFC 6749 section 5.1. Successful Response
+           this.log.info(
+-            `The authorization server returned an error response: ${resultStr}`
++            `Successful response from the authorization server: ${resultStr}`
+           );
+-          // Typically in production this would be {"error": "invalid_grant"}.
+-          // That is, the token expired or was revoked (user changed password?).
+-          // Reset the tokens we have and call success so that the auth flow
+-          // will be re-triggered.
+-          this.accessToken = null;
+-          this.refreshToken = null;
++          this.accessToken = result.access_token;
++          if ("refresh_token" in result) {
++            this.refreshToken = result.refresh_token;
++          }
++          if ("expires_in" in result) {
++            this.tokenExpires = new Date().getTime() + result.expires_in * 1000;
++          } else {
++            this.tokenExpires = Number.MAX_VALUE;
++          }
+           this.connectSuccessCallback();
+-          return;
+-        }
+-
+-        // RFC 6749 section 5.1. Successful Response
+-        this.log.info(
+-          `Successful response from the authorization server: ${resultStr}`
+-        );
+-        this.accessToken = result.access_token;
+-        if ("refresh_token" in result) {
+-          this.refreshToken = result.refresh_token;
+-        }
+-        if ("expires_in" in result) {
+-          this.tokenExpires = new Date().getTime() + result.expires_in * 1000;
+-        } else {
+-          this.tokenExpires = Number.MAX_VALUE;
+-        }
+-        this.connectSuccessCallback();
+-      })
+-      .catch(err => {
+-        this.log.info(`Connection to authorization server failed: ${err}`);
+-        this.connectFailureCallback(err);
+-      });
++        })
++        .catch(err => {
++          this.log.info(`Connection to authorization server failed: ${err}`);
++          this.connectFailureCallback(err);
++        });
++    }
+   },
+ };
+diff --git a/mailnews/base/util/OAuth2Providers.jsm b/mailnews/base/util/OAuth2Providers.jsm
+--- a/mailnews/base/util/OAuth2Providers.jsm
++++ b/mailnews/base/util/OAuth2Providers.jsm
+@@ -63,69 +63,64 @@ var kIssuers = new Map ([
+   [
+     "accounts.google.com",
+     {
+       clientId:
+         "406964657835-aq8lmia8j95dhl1a2bvharmfk3t1hgqj.apps.googleusercontent.com",
+       clientSecret: "kSmqreRr0qwBWJgbf5Y-PjSU",
+       authorizationEndpoint: "https://accounts.google.com/o/oauth2/auth",
+       tokenEndpoint: "https://www.googleapis.com/oauth2/v3/token",
+-      useCORS: true,
+     },
+   ],
+   [
+     "o2.mail.ru",
+     {
+       clientId: "thunderbird",
+       clientSecret: "I0dCAXrcaNFujaaY",
+       authorizationEndpoint: "https://o2.mail.ru/login",
+       tokenEndpoint: "https://o2.mail.ru/token",
+-      useCORS: true,
+     },
+   ],
+   [
+     "oauth.yandex.com",
+     {
+       clientId: "2a00bba7374047a6ab79666485ffce31",
+       clientSecret: "3ded85b4ec574c2187a55dc49d361280",
+       authorizationEndpoint: "https://oauth.yandex.com/authorize",
+       tokenEndpoint: "https://oauth.yandex.com/token",
+-      useCORS: true,
+     },
+   ],
+   [
+     "login.yahoo.com",
+     {
+       clientId: "dj0yJmk9NUtCTWFMNVpTaVJmJmQ9WVdrOVJ6UjVTa2xJTXpRbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD0yYw--",
+       clientSecret: "f2de6a30ae123cdbc258c15e0812799010d589cc",
+       authorizationEndpoint: "https://api.login.yahoo.com/oauth2/request_auth",
+       tokenEndpoint: "https://api.login.yahoo.com/oauth2/get_token",
+-      useCORS: true,
+     },
+   ],
+   [
+     "login.aol.com",
+     {
+       clientId:
+         "dj0yJmk9OXRHc1FqZHRQYzVvJmQ9WVdrOU1UQnJOR0pvTjJrbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD02NQ--",
+       clientSecret: "79c1c11991d148ddd02a919000d69879942fc278",
+       authorizationEndpoint: "https://api.login.aol.com/oauth2/request_auth",
+       tokenEndpoint: "https://api.login.aol.com/oauth2/get_token",
+-      useCORS: true,
+     },
+   ],
+   [
+     "login.microsoftonline.com",
+     {
+       clientId: "9e5f94bc-e8a4-4e73-b8be-63364c29d753", // Application (client) ID
+       // https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints
+       authorizationEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
+       tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
+       redirectionEndpoint: "https://localhost",
+-      useCORS: true,
++      useHttpChannel: true,
+     },
+   ],
+ ]);
+ 
+ /**
+  * OAuth2Providers: Methods to lookup OAuth2 parameters for supported OAuth2
+  * providers.
+  */
+diff --git a/mailnews/base/util/OAuth2Providers.jsm.1810760-2.later b/mailnews/base/util/OAuth2Providers.jsm.1810760-2.later
+new file mode 100644
+--- /dev/null
++++ b/mailnews/base/util/OAuth2Providers.jsm.1810760-2.later
+@@ -0,0 +1,20 @@
++--- OAuth2Providers.jsm
+++++ OAuth2Providers.jsm
++@@ -154,17 +149,16 @@ var kIssuers = new Map([
++       authorizationEndpoint:
++         "http://mochi.test:8888/browser/comm/mail/components/addrbook/test/browser/data/redirect_auto.sjs",
++       tokenEndpoint:
++         "http://mochi.test:8888/browser/comm/mail/components/addrbook/test/browser/data/token.sjs",
++       // I don't know why, but tests refuse to work with a plain HTTP endpoint
++       // (the request is redirected to HTTPS, which we're not listening to).
++       // Just use an HTTPS endpoint.
++       redirectionEndpoint: "https://localhost",
++-      useCORS: true,
++     },
++   ],
++ ]);
++ 
++ /**
++  * OAuth2Providers: Methods to lookup OAuth2 parameters for supported OAuth2
++  * providers.
++  */

+ 4 - 0
comm-release/patches/series

@@ -2120,6 +2120,10 @@ NOBUG-fixcalbackend-25318.patch
 1453403-2-61a1.patch
 1466297-62a1.patch
 1353704-69a1.patch
+1685414-1-1027.patch
+1685414-2-1027.patch
+1810760-1-1027.patch
+1810760-2-1027.patch
 1871992-122a1.patch
 815638-125a1.patch
 1864287-punycode-25319.patch

+ 64 - 0
mozilla-release/patches/1559403-1-69a1.patch

@@ -0,0 +1,64 @@
+# HG changeset patch
+# User Masatoshi Kimura <VYV03354@nifty.ne.jp>
+# Date 1560527321 0
+# Node ID cc7e68d7565f8383a487b56cdc99d7f562366501
+# Parent  38a3c0fa91f52a0b468c5d3734cb1989f5938acc
+Bug 1559403 - Implement nsIStringInputStream.setUTF8Data. r=froydnj
+
+Differential Revision: https://phabricator.services.mozilla.com/D35027
+
+diff --git a/xpcom/io/nsIStringStream.idl b/xpcom/io/nsIStringStream.idl
+--- a/xpcom/io/nsIStringStream.idl
++++ b/xpcom/io/nsIStringStream.idl
+@@ -32,16 +32,26 @@ interface nsIStringInputStream : nsIInpu
+      * NOTE: For JS callers, the given data must not contain null characters
+      * (other than a null terminator) because a null character in the middle of
+      * the data string will be seen as a terminator when the data is converted
+      * from a JS string to a C++ character array.
+      */
+     void setData(in string data, in long dataLen);
+ 
+     /**
++     * SetUTF8Data - encode input data to UTF-8 and assign it to the input
++     * stream.
++     *
++     * @param data    - stream data
++     *
++     * NOTE: This method is meant to be used by JS callers,
++     */
++    void setUTF8Data(in AUTF8String data);
++
++    /**
+      * NOTE: the following methods are designed to give C++ code added control
+      * over the ownership and lifetime of the stream data.  Use with care :-)
+      */
+ 
+     /**
+      * AdoptData - assign data to the input stream.  the input stream takes
+      * ownership of the given data buffer and will free it when
+      * the input stream is destroyed.
+diff --git a/xpcom/io/nsStringStream.cpp b/xpcom/io/nsStringStream.cpp
+--- a/xpcom/io/nsStringStream.cpp
++++ b/xpcom/io/nsStringStream.cpp
+@@ -165,16 +165,21 @@ nsStringInputStream::SetData(const char*
+     return NS_ERROR_OUT_OF_MEMORY;
+   }
+ 
+   mOffset = 0;
+   return NS_OK;
+ }
+ 
+ NS_IMETHODIMP
++nsStringInputStream::SetUTF8Data(const nsACString& aData) {
++  return nsStringInputStream::SetData(aData);
++}
++
++NS_IMETHODIMP
+ nsStringInputStream::AdoptData(char* aData, int32_t aDataLen) {
+   ReentrantMonitorAutoEnter lock(mMon);
+ 
+   if (NS_WARN_IF(!aData)) {
+     return NS_ERROR_INVALID_ARG;
+   }
+   mData.Adopt(aData, aDataLen);
+   mOffset = 0;

+ 34 - 0
mozilla-release/patches/1559403-2-69a1.patch

@@ -0,0 +1,34 @@
+# HG changeset patch
+# User Masatoshi Kimura <VYV03354@nifty.ne.jp>
+# Date 1560531287 0
+# Node ID 34f118fcfa1ce9c6b0d7d2b2140d8dbe1883b0a4
+# Parent  cc7e68d7565f8383a487b56cdc99d7f562366501
+Bug 1559403 - Use nsIStringInputStream.setUTF8Data in BookmarkJSONUtils. r=mak
+
+Depends on D35027
+
+Differential Revision: https://phabricator.services.mozilla.com/D35028
+
+diff --git a/toolkit/components/places/BookmarkJSONUtils.jsm b/toolkit/components/places/BookmarkJSONUtils.jsm
+--- a/toolkit/components/places/BookmarkJSONUtils.jsm
++++ b/toolkit/components/places/BookmarkJSONUtils.jsm
+@@ -31,17 +31,17 @@ const OLD_BOOKMARK_QUERY_TRANSLATIONS = 
+  * is case-sensitive if you are going to reuse this code.
+  */
+ function generateHash(aString) {
+   let cryptoHash = Cc["@mozilla.org/security/hash;1"]
+                      .createInstance(Ci.nsICryptoHash);
+   cryptoHash.init(Ci.nsICryptoHash.MD5);
+   let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
+                        .createInstance(Ci.nsIStringInputStream);
+-  stringStream.data = aString;
++  stringStream.setUTF8Data(aString);
+   cryptoHash.updateFromStream(stringStream, -1);
+   // base64 allows the '/' char, but we can't use it for filenames.
+   return cryptoHash.finish(true).replace(/\//g, "-");
+ }
+ 
+ var BookmarkJSONUtils = Object.freeze({
+   /**
+    * Import bookmarks from a url.
+

+ 2 - 0
mozilla-release/patches/series

@@ -7116,3 +7116,5 @@ TOP-1906540-mozdevice-removal-mozilla-25320.patch
 1457401-61a1.patch
 1465585-3a-std-62a1.patch
 1566482-70a1.patch
+1559403-1-69a1.patch
+1559403-2-69a1.patch