[tbb-commits] [torbutton/maint-1.9.7] Bug 23483: Donation banner on about:tor for 2017

gk at torproject.org gk at torproject.org
Tue Oct 17 11:28:29 UTC 2017


commit 2e6d58145ff2de2ac705f2feebe3d594eab382a8
Author: Arthur Edelstein <arthuredelstein at gmail.com>
Date:   Mon Sep 18 16:42:07 2017 -0700

    Bug 23483: Donation banner on about:tor for 2017
    
    (Also removes a dot from aboutTor.donationBanner.slogan)
---
 src/chrome/content/aboutTor/aboutTor-content.js |   5 ++
 src/chrome/content/aboutTor/aboutTor.xhtml      |  19 +++-
 src/chrome/content/aboutTor/donation_banner.js  | 105 ++++++++++++++++++++++
 src/chrome/content/aboutTor/onion-hand.png      | Bin 0 -> 69055 bytes
 src/chrome/content/torbutton.js                 |   4 +-
 src/chrome/locale/en/aboutTor.properties        |   2 +-
 src/chrome/skin/donation_banner.css             | 113 ++++++++++++++++++++++++
 src/modules/donation-banner.js                  | 112 +++++++++++++++++++++++
 8 files changed, 357 insertions(+), 3 deletions(-)

diff --git a/src/chrome/content/aboutTor/aboutTor-content.js b/src/chrome/content/aboutTor/aboutTor-content.js
index ec515bb..95e8abd 100644
--- a/src/chrome/content/aboutTor/aboutTor-content.js
+++ b/src/chrome/content/aboutTor/aboutTor-content.js
@@ -105,6 +105,11 @@ var AboutTorListener = {
     else
       body.removeAttribute("showmanual");
 
+    if (aData.bannerData)
+      body.setAttribute("banner-data", aData.bannerData);
+    else
+      body.removeAttribute("banner-data");
+
     // Setting body.initialized="yes" displays the body, which must be done
     // at this point because our remaining initialization depends on elements
     // being visible so that their size and position are accurate.
diff --git a/src/chrome/content/aboutTor/aboutTor.xhtml b/src/chrome/content/aboutTor/aboutTor.xhtml
index 7ae4b8b..367f9a6 100644
--- a/src/chrome/content/aboutTor/aboutTor.xhtml
+++ b/src/chrome/content/aboutTor/aboutTor.xhtml
@@ -21,6 +21,8 @@
   <title>&aboutTor.title;</title>
   <link rel="stylesheet" type="text/css" media="all"
         href="resource://torbutton/chrome/skin/aboutTor.css"/>
+  <link rel="stylesheet" type="text/css" media="all"
+        href="resource://torbutton/chrome/skin/donation_banner.css"/>
 <script type="text/javascript;version=1.7">
  <![CDATA[
 window.addEventListener("pageshow", function() {
@@ -31,6 +33,21 @@ window.addEventListener("pageshow", function() {
 </script>
 </head>
 <body dir="&locale.dir;">
+  <div id="banner">
+    <div id="banner-contents-container">
+      <div id="banner-tagline"><span></span></div>
+      <div id="banner-slogan"><span></span></div>
+      <a id="banner-donate-button-link"
+         href="https://www.torproject.org/donate/donate-tbb">
+        <div id="banner-donate-button">
+          <div id="banner-donate-button-inner">
+            <span></span>
+          </div>
+        </div>
+      </a>
+    </div>
+  </div>
+  <div id="banner-spacer"></div>
 <div id="torstatus" class="top">
   <div id="torstatus-version"/>
   <div id="torstatus-image"/>
@@ -112,6 +129,6 @@ window.addEventListener("pageshow", function() {
   <p>&aboutTor.footer.label;
 <a href="&aboutTor.learnMore.link;">&aboutTor.learnMore.label;</a></p>
 </div>
-
+  <script src="resource://torbutton/chrome/content/aboutTor/donation_banner.js"></script>
 </body>
 </html>
diff --git a/src/chrome/content/aboutTor/donation_banner.js b/src/chrome/content/aboutTor/donation_banner.js
new file mode 100644
index 0000000..1c95822
--- /dev/null
+++ b/src/chrome/content/aboutTor/donation_banner.js
@@ -0,0 +1,105 @@
+/* jshint esnext:true */
+
+let sel = selector => document.querySelector(selector);
+
+// Shrink the font size if the text in the given element is overflowing.
+let fitTextInElement = function (element) {
+  element.style.fontSize = "8px";
+  let defaultWidth = element.scrollWidth,
+      defaultHeight = element.scrollHeight;
+  let bestSize;
+  for (let testSize = 8; testSize <= 40; testSize += 0.5) {
+    element.style.fontSize = `${testSize}px`;
+    if (element.scrollWidth <= defaultWidth &&
+        element.scrollHeight <= defaultHeight) {
+      bestSize = testSize;
+    } else {
+      break;
+    }
+  }
+  element.style.fontSize = `${bestSize}px`;
+};
+
+// Increase padding at end to "squeeze" text, until just before
+// it gets squeezed so much that it gets longer vertically.
+let avoidWidows = function (element) {
+  element.style.paddingRight = "0px";
+  let originalWidth = element.scrollWidth;
+  let originalHeight = element.scrollHeight;
+  let bestPadding;
+  for (let testPadding = 0; testPadding < originalWidth; testPadding += 0.5) {
+    element.style.paddingRight = `${testPadding}px`;
+    if (element.scrollHeight <= originalHeight) {
+      bestPadding = testPadding;
+    } else {
+      break;
+    }
+  }
+  element.style.paddingRight = `${bestPadding}px`;
+  if (window.getComputedStyle(element).direction === "rtl") {
+    element.style.paddingLeft = element.style.paddingRight;
+    element.style.paddingRight = "0px";
+  }
+};
+
+// Resize the text inside banner to fit.
+let updateTextSizes = function () {
+  fitTextInElement(sel("#banner-tagline"));
+  fitTextInElement(sel("#banner-slogan"));
+  fitTextInElement(sel("#banner-donate-button-inner"));
+  avoidWidows(sel("#banner-tagline span"));
+};
+
+// Returns a random integer x, such that 0 <= x < max
+let randomInteger = max => Math.floor(max * Math.random());
+
+// The main donation banner function.
+let runDonationBanner = function ({ taglines, slogan, donate, shortLocale }) {
+  try {
+    sel("#banner-tagline span").innerText = taglines[randomInteger(taglines.length)];
+    sel("#banner-slogan span").innerText = slogan;
+    let donateButtonText = sel("#banner-donate-button-inner span");
+    let rtl = window.getComputedStyle(donateButtonText).direction === "rtl";
+    donateButtonText.innerHTML = donate + " " + (rtl ? "◀" : "▶");
+    sel("#banner").style.display = "flex";
+    sel("#banner-spacer").style.display = "block";
+    addEventListener("resize", updateTextSizes);
+    updateTextSizes();
+    // Add a suffix corresponding to locale so we can send user
+    // to a correctly-localized donation page via redirect.
+    sel("#banner-donate-button-link").href += "-" + shortLocale;
+    sel("#torstatus-image").style.display = "none";
+  } catch (e) {
+    // Something went wrong.
+    console.error(e);
+    sel("#banner").style.display = "none";
+    sel("#bannerSpacer").style.display = "none";
+    sel("#torstatus-image").style.display = "block";
+  }
+};
+
+// Calls callback(attributeValue) when the specified attribute changes on
+// target. Returns a zero-arg function that stops observing.
+let observeAttribute = function (target, attributeName, callback) {
+  let observer = new MutationObserver(mutations => {
+    mutations.forEach(mutation => {
+      if (mutation.type === "attributes" &&
+          mutation.attributeName === attributeName) {
+        callback(target.getAttribute(attributeName));
+      }
+    });
+  });
+  observer.observe(target, { attributes: true });
+  return () => observer.disconnect();
+};
+
+// Start the donation banner if "toron" has been set to "yes".
+let stopObserving = observeAttribute(document.body, "toron", value => {
+  stopObserving();
+  if (value === "yes") {
+    let bannerDataJSON = document.body.getAttribute("banner-data");
+    if (bannerDataJSON && bannerDataJSON.length > 0) {
+      runDonationBanner(JSON.parse(bannerDataJSON));
+    }
+  }
+});
diff --git a/src/chrome/content/aboutTor/onion-hand.png b/src/chrome/content/aboutTor/onion-hand.png
new file mode 100644
index 0000000..00a5a41
Binary files /dev/null and b/src/chrome/content/aboutTor/onion-hand.png differ
diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js
index db6b694..955f8eb 100644
--- a/src/chrome/content/torbutton.js
+++ b/src/chrome/content/torbutton.js
@@ -13,6 +13,7 @@ let { showDialog } = Cu.import("resource://torbutton/modules/utils.js", {});
 let { unescapeTorString } = Cu.import("resource://torbutton/modules/utils.js", {});
 let SecurityPrefs = Cu.import("resource://torbutton/modules/security-prefs.js", {});
 let { bindPrefAndInit, observe } = Cu.import("resource://torbutton/modules/utils.js", {});
+let { bannerData } = Cu.import("resource://torbutton/modules/donation-banner.js", {});
 
 const k_tb_last_browser_version_pref = "extensions.torbutton.lastBrowserVersion";
 const k_tb_browser_update_needed_pref = "extensions.torbutton.updateNeeded";
@@ -450,7 +451,8 @@ var torbutton_abouttor_message_handler = {
       torOn: torbutton_tor_check_ok(),
       updateNeeded: torbutton_update_is_needed(),
       showManual: torbutton_show_torbrowser_manual(),
-      toolbarButtonXPos: torbutton_get_toolbarbutton_xpos()
+      toolbarButtonXPos: torbutton_get_toolbarbutton_xpos(),
+      bannerData: bannerData(),
     };
   },
 
diff --git a/src/chrome/locale/en/aboutTor.properties b/src/chrome/locale/en/aboutTor.properties
index 4436e21..d0d3a64 100644
--- a/src/chrome/locale/en/aboutTor.properties
+++ b/src/chrome/locale/en/aboutTor.properties
@@ -10,7 +10,7 @@ aboutTor.searchDDG.search.link=https://duckduckgo.com/
 
 aboutTor.donationBanner.donate=Donate Now!
 
-aboutTor.donationBanner.slogan=Tor: Powering Digital Resistance.
+aboutTor.donationBanner.slogan=Tor: Powering Digital Resistance
 aboutTor.donationBanner.mozilla=Give today and Mozilla will match your gift!
 
 aboutTor.donationBanner.tagline1=Protecting Journalists, Whistleblowers, & Activists Since 2006
diff --git a/src/chrome/skin/donation_banner.css b/src/chrome/skin/donation_banner.css
new file mode 100644
index 0000000..8580066
--- /dev/null
+++ b/src/chrome/skin/donation_banner.css
@@ -0,0 +1,113 @@
+#banner {
+    -khtml-user-select: none;    /* Konqueror */
+    -moz-user-select: none;      /* Firefox */
+    -ms-user-select: none;       /* Internet Explorer/Edge */
+    -webkit-touch-callout: none; /* iOS Safari */
+    -webkit-user-select: none;   /* Chrome/Safari/Opera */
+    display: none;
+    height: 150px;
+    justify-content: center;
+    left: 0px;
+    margin-top: 0px;
+    min-width: 900px;
+    opacity: 1;
+    position: absolute;
+    user-select: none;
+    width: 100%;
+    z-index: 1;
+}
+#banner:before {
+    background-color: #406;
+    background-image: url('resource://torbutton/chrome/content/aboutTor/onion-hand.png');
+    background-position: center;
+    background-size: cover;
+    content: "";
+    height: 150px;
+    left: 0px;
+    position: absolute;
+    top: 0px;
+    width: 100%;
+}
+#banner:-moz-dir(rtl):before {
+    transform: scaleX(-1);
+}
+#banner-contents-container {
+    align-items: center;
+    height: 100%;
+    max-width: 700px;
+    position: relative;
+    width: 700px;
+}
+#banner-tagline {
+    align-items: center;
+    bottom: 60px;
+    color: white;
+    display: flex;
+    font-family: monospace;
+    font-size: 8px;
+    font-weight: bold;
+    left: 85px;
+    position: absolute;
+    right: 0px;
+    text-align: start;
+    text-transform: uppercase;
+    top: 10px;
+}
+#banner-tagline:-moz-dir(rtl) {
+    left: 0px;
+    right: 85px;
+}
+#banner-slogan {
+    align-items: start;
+    bottom: 0px;
+    color: #f8f8a0;
+    display: flex;
+    font-family: monospace;
+    font-weight: bold;
+    left: 85px;
+    position: absolute;
+    right: 285px;
+    text-align: start;
+    top: 100px;
+    white-space: nowrap;
+}
+#banner-slogan:-moz-dir(rtl) {
+    left: 285px;
+    right: 85px;
+}
+#banner-donate-button {
+    background-color: #13a513;
+    border: 0px;
+    bottom: 10px;
+    color: #fbf7ef;
+    font-family: sans-serif;
+    font-size: 12px;
+    font-weight: bold;
+    left: 430px;
+    letter-spacing: -0.00em;
+    position: absolute;
+    right: 0px;
+    top: 100px;
+}
+#banner-donate-button:-moz-dir(rtl) {
+    left: 0px;
+    right: 430px;
+}
+#banner-donate-button:hover {
+    background-color: #38bc38;
+}
+#banner-donate-button-inner {
+    bottom: 6px;
+    display: flex;
+    justify-content: center;
+    left: 8px;
+    position: absolute;
+    right: 8px;
+    top: 6px;
+}
+#banner-spacer {
+    display: none;
+    height: 150px;
+    position: relative;
+    top: 0;
+}
diff --git a/src/modules/donation-banner.js b/src/modules/donation-banner.js
new file mode 100644
index 0000000..bb35e86
--- /dev/null
+++ b/src/modules/donation-banner.js
@@ -0,0 +1,112 @@
+/* jshint esversion:6 */
+
+const Cu = Components.utils;
+
+// ### Import Mozilla Services
+Cu.import("resource://gre/modules/Services.jsm");
+
+// A list of locales for which the banner has been translated.
+const kBannerLocales = [
+  "bg",
+  "da",
+  "el",
+  "en",
+  "es",
+  "fr",
+  "fr_CA",
+  "is",
+  "it",
+  "nb",
+  "tr",
+];
+
+// A list of donation page locales (at least redirects should exist).
+const kDonationPageLocales = [
+  "ar",
+  "de",
+  "en",
+  "es",
+  "fa",
+  "fr",
+  "it",
+  "ja",
+  "ko",
+  "nl",
+  "pl",
+  "pt",
+  "ru",
+  "tr",
+  "vi",
+  "zh",
+];
+
+const kPropertiesURL = "chrome://torbutton/locale/aboutTor.properties";
+const gStringBundle = Services.strings.createBundle(kPropertiesURL);
+
+// Check if we should show the banner, depends on
+// browser locale, current date, and how many times
+// we have already shown the banner.
+const shouldShowBanner = function ({ locale, shortLocale }) {
+  try {
+    // If our override test pref is true, then just show the banner regardless.
+    if (Services.prefs.getBoolPref("extensions.torbutton.testBanner", false)) {
+      return true;
+    }
+    // Don't show a banner if update is needed.
+    let updateNeeded = Services.prefs.getBoolPref("extensions.torbutton.updateNeeded");
+    if (updateNeeded) {
+      return false;
+    }
+    // Only show banner when we have that locale and if a donation redirect exists.
+    if (kBannerLocales.indexOf(locale) === -1 ||
+        kDonationPageLocales.indexOf(shortLocale) === -1) {
+      return false;
+    }
+    // Only show banner between 2017 Oct 23 and 2018 Jan 25.
+    let now = new Date();
+    let start = new Date(2017, 9, 23);
+    let end = new Date(2018, 0, 26);
+    let shownCountPref = "extensions.torbutton.donation_banner2017.shown_count";
+    if (now < start || now > end) {
+      // Clean up pref if not in use.
+      Services.prefs.clearUserPref(shownCountPref);
+      return false;
+    }
+    // Only show banner 50 times.
+    let count = 0;
+    if (Services.prefs.prefHasUserValue(shownCountPref)) {
+      count = Services.prefs.getIntPref(shownCountPref);
+    }
+    if (count >= 50) {
+      return false;
+    }
+    Services.prefs.setIntPref(shownCountPref, count+1);
+    return true;
+  } catch (e) {
+    return false;
+  }
+};
+
+// Read data needed for displaying banner on page.
+var bannerData = function () {
+  // Read short locale.
+  let locale = Services.prefs.getCharPref("general.useragent.locale");
+  let shortLocale = locale.match(/[a-zA-Z]+/)[0].toLowerCase();
+  if (!shouldShowBanner({ locale, shortLocale })) {
+    return null;
+  }
+  // Load tag lines.
+  let taglines = [];
+  for (let index = 0; index < 5; ++index) {
+    let tagline = gStringBundle.GetStringFromName(
+      "aboutTor.donationBanner.tagline" + (index + 1));
+    taglines.push(tagline);
+  }
+  // Read slogan and donate button text.
+  let slogan = gStringBundle.GetStringFromName("aboutTor.donationBanner.slogan");
+  let donate = gStringBundle.GetStringFromName("aboutTor.donationBanner.donate");
+  return JSON.stringify({ taglines, slogan, donate, shortLocale });
+};
+
+// Export utility functions for external use.
+var EXPORTED_SYMBOLS = ["bannerData"];





More information about the tbb-commits mailing list