Commit ad7c2c03 authored by Kathleen Brade's avatar Kathleen Brade Committed by Georg Koppen
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 fc4042bc
// Copyright (c) 2019, 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 {ActorChild} = ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
class AboutTBUpdateChild extends ActorChild {
receiveMessage(aMessage) {
if (aMessage.name == "AboutTBUpdate:Update")
this.onUpdate(aMessage.data);
}
handleEvent(aEvent) {
switch (aEvent.type) {
case "AboutTBUpdateLoad":
this.onPageLoad();
break;
case "pagehide":
this.onPageHide(aEvent);
break;
}
}
// aData may contain the following string properties:
// version
// releaseDate
// moreInfoURL
// releaseNotes
onUpdate(aData) {
let doc = this.content.document;
doc.getElementById("version-content").textContent = aData.version;
if (aData.releaseDate) {
doc.body.setAttribute("havereleasedate", "true");
doc.getElementById("releasedate-content").textContent = aData.releaseDate;
}
if (aData.moreInfoURL)
doc.getElementById("infolink").setAttribute("href", aData.moreInfoURL);
doc.getElementById("releasenotes-content").textContent = aData.releaseNotes;
}
onPageLoad() {
this.mm.sendAsyncMessage("AboutTBUpdate:RequestUpdate");
}
onPageHide(aEvent) {
if (aEvent.target.defaultView.frameElement) {
return;
}
}
}
......@@ -74,3 +74,8 @@ FINAL_TARGET_FILES.actors += [
'WebRTCChild.jsm',
'WebRTCParent.jsm',
]
if CONFIG['TOR_BROWSER_UPDATE']:
FINAL_TARGET_FILES.actors += [
'AboutTBUpdateChild.jsm',
]
/*
* 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;
}
// Copyright (c) 2019, The Tor Project, Inc.
// See LICENSE for licensing information.
//
// vim: set sw=2 sts=2 ts=8 et syntax=javascript:
addEventListener("load", () => {
let event = new CustomEvent("AboutTBUpdateLoad", { bubbles: true });
document.dispatchEvent(event);
});
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % tbUpdateDTD SYSTEM "chrome://browser/locale/aboutTBUpdate.dtd">
%tbUpdateDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'" />
<title>&aboutTBUpdate.changelogTitle;</title>
<link rel="stylesheet" type="text/css"
href="chrome://browser/content/abouttbupdate/aboutTBUpdate.css"/>
<script src="chrome://browser/content/abouttbupdate/aboutTBUpdate.js"
type="text/javascript"/>
</head>
<body dir="&locale.dir;">
<div class="two-column-grid">
<div class="label-column">&aboutTBUpdate.version;</div>
<div id="version-content"/>
<div class="label-column release-date-cell">&aboutTBUpdate.releaseDate;</div>
<div id="releasedate-content" class="release-date-cell"/>
<div class="more-info-row"/>
<div class="more-info-row">&aboutTBUpdate.linkPrefix;<a id="infolink">&aboutTBUpdate.linkLabel;</a>&aboutTBUpdate.linkSuffix;</div>
<div id="releasenotes-label"
class="label-column">&aboutTBUpdate.releaseNotes;</div>
<div id="releasenotes-content"></div>
</div>
</body>
</html>
......@@ -57,7 +57,7 @@ var gIdentityHandler = {
* RegExp used to decide if an about url should be shown as being part of
* the browser UI.
*/
_secureInternalUIWhitelist: /^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback)(?:[?#]|$)/i,
_secureInternalUIWhitelist: (AppConstants.TOR_BROWSER_UPDATE ? /^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|tor|tbupdate)(?:[?#]|$)/i : /^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|tor)(?:[?#]|$)/i),
/**
* Whether the established HTTPS connection is considered "broken".
......
......@@ -630,6 +630,10 @@ var gInitialPages = [
"about:newinstall",
];
if (AppConstants.TOR_BROWSER_UPDATE) {
gInitialPages.push("about:tbupdate");
}
function isInitialPage(url) {
if (!(url instanceof Ci.nsIURI)) {
try {
......
......@@ -29,6 +29,11 @@ browser.jar:
content/browser/aboutTabCrashed.css (content/aboutTabCrashed.css)
content/browser/aboutTabCrashed.js (content/aboutTabCrashed.js)
content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml)
#ifdef TOR_BROWSER_UPDATE
content/browser/abouttbupdate/aboutTBUpdate.xhtml (content/abouttbupdate/aboutTBUpdate.xhtml)
content/browser/abouttbupdate/aboutTBUpdate.js (content/abouttbupdate/aboutTBUpdate.js)
content/browser/abouttbupdate/aboutTBUpdate.css (content/abouttbupdate/aboutTBUpdate.css)
#endif
* content/browser/browser.css (content/browser.css)
content/browser/browser.js (content/browser.js)
* content/browser/browser.xhtml (content/browser.xhtml)
......
......@@ -650,6 +650,23 @@ nsBrowserContentHandler.prototype = {
}
}
// Retrieve the home page early so we can compare it against about:tor
// to decide whether or not we need an override page (second tab) after
// an update was applied.
var startPage = "";
try {
var choice = prefb.getIntPref("browser.startup.page");
if (choice == 1 || choice == 3) {
startPage = HomePage.get();
}
} catch (e) {
Cu.reportError(e);
}
if (startPage == "about:blank") {
startPage = "";
}
var override;
var overridePage = "";
var additionalPage = "";
......@@ -701,6 +718,16 @@ nsBrowserContentHandler.prototype = {
// into account because that requires waiting for the session file
// to be read. If a crash occurs after updating, before restarting,
// we may open the startPage in addition to restoring the session.
//
// Tor Browser: Instead of opening the post-update "override page"
// directly, we ensure that about:tor will be opened in a special
// mode that notifies the user that their browser was updated.
// The about:tor page will provide a link to the override page
// where the user can learn more about the update, as well as a
// link to the Tor Browser changelog page (about:tbupdate). The
// override page URL comes from the openURL attribute within the
// updates.xml file or, if no showURL action is present, from the
// startup.homepage_override_url pref.
willRestoreSession = SessionStartup.isAutomaticRestoreEnabled();
overridePage = Services.urlFormatter.formatURLPref(
......@@ -720,6 +747,20 @@ nsBrowserContentHandler.prototype = {
overridePage = overridePage.replace("%OLD_VERSION%", old_mstone);
overridePage = overridePage.replace("%OLD_TOR_BROWSER_VERSION%",
old_tbversion);
#ifdef TOR_BROWSER_UPDATE
if (overridePage)
{
prefb.setCharPref("torbrowser.post_update.url", overridePage);
prefb.setBoolPref("torbrowser.post_update.shouldNotify", true);
// If the user's homepage is about:tor, we will inform them
// about the update on that page; otherwise, we arrange to
// open about:tor in a secondary tab.
if (startPage === "about:tor")
overridePage = "";
else
overridePage = "about:tor";
}
#endif
break;
case OVERRIDE_NEW_BUILD_ID:
if (UpdateManager.activeUpdate) {
......@@ -792,20 +833,6 @@ nsBrowserContentHandler.prototype = {
}
}
var startPage = "";
try {
var choice = prefb.getIntPref("browser.startup.page");
if (choice == 1 || choice == 3) {
startPage = HomePage.get();
}
} catch (e) {
Cu.reportError(e);
}
if (startPage == "about:blank") {
startPage = "";
}
let skipStartPage =
(override == OVERRIDE_NEW_PROFILE ||
override == OVERRIDE_ALTERNATE_PROFILE) &&
......
......@@ -560,6 +560,22 @@ let LEGACY_ACTORS = {
},
};
if (AppConstants.TOR_BROWSER_UPDATE) {
LEGACY_ACTORS["AboutTBUpdate"] = {
child: {
module: "resource:///actors/AboutTBUpdateChild.jsm",
events: {
"AboutTBUpdateLoad": {wantUntrusted: true},
"pagehide": {capture: true},
},
matches: ["about:tbupdate"],
messages: [
"AboutTBUpdate:Update",
],
}
};
}
(function earlyBlankFirstPaint() {
if (
AppConstants.platform == "macosx" ||
......@@ -747,6 +763,11 @@ if (AppConstants.MOZ_CRASHREPORTER) {
});
}
if (AppConstants.TOR_BROWSER_UPDATE) {
XPCOMUtils.defineLazyModuleGetter(this, "AboutTBUpdate",
"resource:///modules/AboutTBUpdate.jsm");
}
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
return Services.strings.createBundle(
"chrome://branding/locale/brand.properties"
......@@ -2204,6 +2225,10 @@ BrowserGlue.prototype = {
AsanReporter.init();
}
if (AppConstants.TOR_BROWSER_UPDATE) {
AboutTBUpdate.init();
}
Sanitizer.onStartup();
this._scheduleStartupIdleTasks();
this._lateTasksIdleObserver = (idleService, topic, data) => {
......
......@@ -120,6 +120,12 @@ static const RedirEntry kRedirMap[] = {
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS},
{"pioneer", "chrome://browser/content/pioneer.html",
nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT},
#ifdef TOR_BROWSER_UPDATE
{"tbupdate", "chrome://browser/content/abouttbupdate/aboutTBUpdate.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT},
#endif
};
static nsAutoCString GetAboutModuleName(nsIURI* aURI) {
......
......@@ -32,6 +32,9 @@ pages = [
'welcomeback',
]
if defined('TOR_BROWSER_UPDATE'):
pages.append('tbupdate')
Classes = [
{
'cid': '{7e4bb6ad-2fc4-4dc6-89ef-23e8e5ccf980}',
......
......@@ -90,11 +90,14 @@ EXTRA_COMPONENTS += [
]
EXTRA_JS_MODULES += [
'BrowserContentHandler.jsm',
'BrowserGlue.jsm',
'distribution.js',
]
EXTRA_PP_JS_MODULES += [
'BrowserContentHandler.jsm',
]
BROWSER_CHROME_MANIFESTS += [
'safebrowsing/content/test/browser.ini',
'tests/browser/browser.ini',
......
<!ENTITY aboutTBUpdate.changelogTitle "Tor Browser Changelog">
<!ENTITY aboutTBUpdate.updated "Tor Browser has been updated.">
<!ENTITY aboutTBUpdate.linkPrefix "For the most up-to-date information about this release, ">
<!ENTITY aboutTBUpdate.linkLabel "visit our website">
<!ENTITY aboutTBUpdate.linkSuffix ".">
<!ENTITY aboutTBUpdate.version "Version">
<!ENTITY aboutTBUpdate.releaseDate "Release Date">
<!ENTITY aboutTBUpdate.releaseNotes "Release Notes">
......@@ -20,6 +20,9 @@
locale/browser/accounts.properties (%chrome/browser/accounts.properties)
locale/browser/app-extension-fields.properties (%chrome/browser/app-extension-fields.properties)
#ifdef TOR_BROWSER_UPDATE
locale/browser/aboutTBUpdate.dtd (%chrome/browser/aboutTBUpdate.dtd)
#endif
locale/browser/browser.dtd (%chrome/browser/browser.dtd)
locale/browser/baseMenuOverlay.dtd (%chrome/browser/baseMenuOverlay.dtd)
locale/browser/browser.properties (%chrome/browser/browser.properties)
......
// Copyright (c) 2019, The Tor Project, Inc.
// See LICENSE for licensing information.
//
// vim: set sw=2 sts=2 ts=8 et syntax=javascript:
"use strict";
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
this.EXPORTED_SYMBOLS = [ "AboutTBUpdate" ];
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
const kRequestUpdateMessageName = "AboutTBUpdate:RequestUpdate";
const kSendUpdateMessageName = "AboutTBUpdate:Update";
#expand const TOR_BROWSER_VERSION = __TOR_BROWSER_VERSION_QUOTED__;
/**
* 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.
*/
var AboutTBUpdate = {
init: function() {
Services.mm.addMessageListener(kRequestUpdateMessageName, this);
},
receiveMessage: function(aMessage) {
if (aMessage.name == kRequestUpdateMessageName)
this.sendAboutTBUpdateData(aMessage.target);
},
sendAboutTBUpdateData: function(aTarget) {
let data = this.releaseNoteInfo;
data.moreInfoURL = this.moreInfoURL;
if (aTarget && aTarget.messageManager) {
aTarget.messageManager.sendAsyncMessage(kSendUpdateMessageName, data);
} else {
Services.mm.broadcastAsyncMessage(kSendUpdateMessageName, data);
}
},
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 = {};
try {
#ifdef 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/.
let f = Services.dirsvc.get("XREExeF", Ci.nsIFile).parent;
#ifdef XP_MACOSX
f = f.parent;
f.append("Resources");
#endif
f.append("TorBrowser");
#else
// "DefProfRt" is .../TorBrowser/Data/Browser
let f = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIProperties).get("DefProfRt", Ci.nsIFile);
f = f.parent.parent; // Remove "Data/Browser"
#endif
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;
},
};
......@@ -160,6 +160,11 @@ EXTRA_JS_MODULES += [
'ZoomUI.jsm',
]
if CONFIG['TOR_BROWSER_UPDATE']:
EXTRA_PP_JS_MODULES += [
'AboutTBUpdate.jsm',
]
if CONFIG['MOZ_ASAN_REPORTER']:
EXTRA_JS_MODULES += [
'AsanReporter.jsm',
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment