Unverified Commit a9c8a0ca authored by Kathleen Brade's avatar Kathleen Brade Committed by Matthew Finkel
Browse files

Bug 16940: After update, load local change notes.

Add an about:tbupdate page that displays the first section from
TorBrowser/Docs/ChangeLog.txt and includes a link to the remote
post-update page (typically our blog entry for the release).

Always load about:tbupdate in a content process, but implement the
code that reads the file system (changelog) in the chrome process
for compatibility with future sandboxing efforts.

Also fix bug 29440. Now about:tbupdate is styled as a fairly simple
changelog page that is designed to be displayed via a link that is on
about:tor.
parent b3cd9d49
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
// Copyright (c) 2020, The Tor Project, Inc.
// See LICENSE for licensing information.
//
// vim: set sw=2 sts=2 ts=8 et syntax=javascript:

var EXPORTED_SYMBOLS = ["AboutTBUpdateChild"];

const { RemotePageChild } = ChromeUtils.import(
  "resource://gre/actors/RemotePageChild.jsm"
);

class AboutTBUpdateChild extends RemotePageChild {}
+120 −0
Original line number Diff line number Diff line
// Copyright (c) 2020, The Tor Project, Inc.
// See LICENSE for licensing information.
//
// vim: set sw=2 sts=2 ts=8 et syntax=javascript:

"use strict";

this.EXPORTED_SYMBOLS = ["AboutTBUpdateParent"];

const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
const { AppConstants } = ChromeUtils.import(
  "resource://gre/modules/AppConstants.jsm"
);

const kRequestUpdateMessageName = "FetchUpdateData";

/**
 * This code provides services to the about:tbupdate page. Whenever
 * about:tbupdate needs to do something chrome-privileged, it sends a
 * message that's handled here. It is modeled after Mozilla's about:home
 * implementation.
 */
class AboutTBUpdateParent extends JSWindowActorParent {
  receiveMessage(aMessage) {
    if (aMessage.name == kRequestUpdateMessageName) {
      return this.releaseNoteInfo;
    }
    return undefined;
  }

  get moreInfoURL() {
    try {
      return Services.prefs.getCharPref("torbrowser.post_update.url");
    } catch (e) {}

    // Use the default URL as a fallback.
    return Services.urlFormatter.formatURLPref("startup.homepage_override_url");
  }

  // Read the text from the beginning of the changelog file that is located
  // at TorBrowser/Docs/ChangeLog.txt and return an object that contains
  // the following properties:
  //   version        e.g., Tor Browser 8.5
  //   releaseDate    e.g., March 31 2019
  //   releaseNotes   details of changes (lines 2 - end of ChangeLog.txt)
  // We attempt to parse the first line of ChangeLog.txt to extract the
  // version and releaseDate. If parsing fails, we return the entire first
  // line in version and omit releaseDate.
  //
  // On Mac OS, when building with --enable-tor-browser-data-outside-app-dir
  // to support Gatekeeper signing, the ChangeLog.txt file is located in
  // TorBrowser.app/Contents/Resources/TorBrowser/Docs/.
  get releaseNoteInfo() {
    let info = { moreInfoURL: this.moreInfoURL };

    try {
      let f;
      if (AppConstants.TOR_BROWSER_DATA_OUTSIDE_APP_DIR) {
        // "XREExeF".parent is the directory that contains firefox, i.e.,
        // Browser/ or, on Mac OS, TorBrowser.app/Contents/MacOS/.
        f = Services.dirsvc.get("XREExeF", Ci.nsIFile).parent;
        if (AppConstants.platform === "macosx") {
          f = f.parent;
          f.append("Resources");
        }
        f.append("TorBrowser");
      } else {
        // "DefProfRt" is .../TorBrowser/Data/Browser
        f = Services.dirsvc.get("DefProfRt", Ci.nsIFile);
        f = f.parent.parent; // Remove "Data/Browser"
      }

      f.append("Docs");
      f.append("ChangeLog.txt");

      let fs = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
        Ci.nsIFileInputStream
      );
      fs.init(f, -1, 0, 0);
      let s = NetUtil.readInputStreamToString(fs, fs.available());
      fs.close();

      // Truncate at the first empty line.
      s = s.replace(/[\r\n][\r\n][\s\S]*$/m, "");

      // Split into first line (version plus releaseDate) and
      // remainder (releaseNotes).
      // This first match() uses multiline mode with two capture groups:
      //   first line: (.*$)
      //   remaining lines: ([\s\S]+)
      //     [\s\S] matches all characters including end of line. This trick
      //     is needed because when using JavaScript regex in multiline mode,
      //     . does not match an end of line character.
      let matchArray = s.match(/(.*$)\s*([\s\S]+)/m);
      if (matchArray && matchArray.length == 3) {
        info.releaseNotes = matchArray[2];
        let line1 = matchArray[1];
        // Extract the version and releaseDate. The first line looks like:
        //   Tor Browser 8.5 -- May 1 2019
        // The regex uses two capture groups:
        //   text that does not include a hyphen: (^[^-]*)
        //   remaining text: (.*$)
        // In between we match optional whitespace, one or more hyphens, and
        // optional whitespace by using: \s*-+\s*
        matchArray = line1.match(/(^[^-]*)\s*-+\s*(.*$)/);
        if (matchArray && matchArray.length == 3) {
          info.version = matchArray[1];
          info.releaseDate = matchArray[2];
        } else {
          info.version = line1; // Match failed: return entire line in version.
        }
      } else {
        info.releaseNotes = s; // Only one line: use as releaseNotes.
      }
    } catch (e) {}

    return info;
  }
}
+6 −0
Original line number Diff line number Diff line
@@ -87,3 +87,9 @@ FINAL_TARGET_FILES.actors += [
    "WebRTCChild.jsm",
    "WebRTCParent.jsm",
]

if CONFIG["TOR_BROWSER_UPDATE"]:
    FINAL_TARGET_FILES.actors += [
        "AboutTBUpdateChild.jsm",
        "AboutTBUpdateParent.jsm",
    ]
+74 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019, The Tor Project, Inc.
 * See LICENSE for licensing information.
 *
 * vim: set sw=2 sts=2 ts=8 et syntax=css:
 */

:root {
  --abouttor-text-color: white;
  --abouttor-bg-toron-color: #420C5D;
}

body {
  font-family: Helvetica, Arial, sans-serif;
  color: var(--abouttor-text-color);
  background-color: var(--abouttor-bg-toron-color);
  background-attachment: fixed;
  background-size: 100% 100%;
}

a {
  color: var(--abouttor-text-color);
}

.two-column-grid {
  display: inline-grid;
  grid-template-columns: auto auto;
  grid-column-gap: 50px;
  margin: 10px 0px 0px 50px;
}

.two-column-grid div {
  margin-top: 40px;
  align-self: baseline;  /* Align baseline of text across the row. */
}

.label-column {
  font-size: 14px;
  font-weight: 400;
}

/*
 * Use a reduced top margin to bring the row that contains the
 * "visit our website" link closer to the row that precedes it. This
 * looks better because the "visit our website" row does not have a
 * label in the left column.
 */
div.more-info-row {
  margin-top: 5px;
  font-size: 14px;
}

#version-content {
  font-size: 50px;
  font-weight: 300;
}

body:not([havereleasedate]) .release-date-cell {
  display: none;
}

#releasedate-content {
  font-size: 17px;
}

#releasenotes-label {
  align-self: start;  /* Anchor "Release Notes" label at the top. */
}

#releasenotes-content {
  font-family: monospace;
  font-size: 15px;
  white-space: pre;
}
+27 −0
Original line number Diff line number Diff line
// Copyright (c) 2020, The Tor Project, Inc.
// See LICENSE for licensing information.
//
// vim: set sw=2 sts=2 ts=8 et syntax=javascript:

/* eslint-env mozilla/frame-script */

// aData may contain the following string properties:
//   version
//   releaseDate
//   moreInfoURL
//   releaseNotes
function onUpdate(aData) {
  document.getElementById("version-content").textContent = aData.version;
  if (aData.releaseDate) {
    document.body.setAttribute("havereleasedate", "true");
    document.getElementById("releasedate-content").textContent =
      aData.releaseDate;
  }
  if (aData.moreInfoURL) {
    document.getElementById("infolink").setAttribute("href", aData.moreInfoURL);
  }
  document.getElementById("releasenotes-content").textContent =
    aData.releaseNotes;
}

RPMSendQuery("FetchUpdateData").then(onUpdate);
Loading