richard pushed to branch tor-browser-115.10.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
- 
e992fb9d
by Henry Wilkes at 2024-04-17T20:39:07+00:00
- 
3cd48ad5
by Henry Wilkes at 2024-04-17T20:39:07+00:00
- 
d1c78659
by Henry Wilkes at 2024-04-17T20:39:07+00:00
10 changed files:
- browser/components/torpreferences/content/connectionPane.js
- browser/components/torpreferences/content/connectionPane.xhtml
- browser/components/torpreferences/content/torPreferences.css
- browser/locales/en-US/browser/tor-browser.ftl
- toolkit/themes/shared/desktop-jar.inc.mn
- + toolkit/themes/shared/icons/tor-dark-loading.png
- + toolkit/themes/shared/icons/tor-dark-loading@xxxxxx
- + toolkit/themes/shared/icons/tor-light-loading.png
- + toolkit/themes/shared/icons/tor-light-loading@xxxxxx
- + tools/torbrowser/generate_tor_loading_png.py
Changes:
| ... | ... | @@ -5,7 +5,8 @@ | 
| 5 | 5 | |
| 6 | 6 |  "use strict";
 | 
| 7 | 7 | |
| 8 | -/* global Services, gSubDialog */
 | |
| 8 | +/* import-globals-from /browser/components/preferences/preferences.js */
 | |
| 9 | +/* import-globals-from /browser/components/preferences/search.js */
 | |
| 9 | 10 | |
| 10 | 11 |  const { setTimeout, clearTimeout } = ChromeUtils.import(
 | 
| 11 | 12 |    "resource://gre/modules/Timer.jsm"
 | 
| ... | ... | @@ -90,12 +91,6 @@ const Lox = { | 
| 90 | 91 |  };
 | 
| 91 | 92 |  */
 | 
| 92 | 93 | |
| 93 | -const InternetStatus = Object.freeze({
 | |
| 94 | -  Unknown: 0,
 | |
| 95 | -  Online: 1,
 | |
| 96 | -  Offline: -1,
 | |
| 97 | -});
 | |
| 98 | - | |
| 99 | 94 |  /**
 | 
| 100 | 95 |   * Make changes to TorSettings and save them.
 | 
| 101 | 96 |   *
 | 
| ... | ... | @@ -2283,6 +2278,168 @@ const gBridgeSettings = { | 
| 2283 | 2278 |    },
 | 
| 2284 | 2279 |  };
 | 
| 2285 | 2280 | |
| 2281 | +/**
 | |
| 2282 | + * Area to show the internet and tor network connection status.
 | |
| 2283 | + */
 | |
| 2284 | +const gNetworkStatus = {
 | |
| 2285 | +  /**
 | |
| 2286 | +   * Initialize the area.
 | |
| 2287 | +   */
 | |
| 2288 | +  init() {
 | |
| 2289 | +    this._internetAreaEl = document.getElementById(
 | |
| 2290 | +      "network-status-internet-area"
 | |
| 2291 | +    );
 | |
| 2292 | +    this._internetResultEl = this._internetAreaEl.querySelector(
 | |
| 2293 | +      ".network-status-result"
 | |
| 2294 | +    );
 | |
| 2295 | +    this._internetTestButton = document.getElementById(
 | |
| 2296 | +      "network-status-internet-test-button"
 | |
| 2297 | +    );
 | |
| 2298 | +    this._internetTestButton.addEventListener("click", () => {
 | |
| 2299 | +      this._startInternetTest();
 | |
| 2300 | +    });
 | |
| 2301 | + | |
| 2302 | +    this._torAreaEl = document.getElementById("network-status-tor-area");
 | |
| 2303 | +    this._torResultEl = this._torAreaEl.querySelector(".network-status-result");
 | |
| 2304 | +    this._torConnectButton = document.getElementById(
 | |
| 2305 | +      "network-status-tor-connect-button"
 | |
| 2306 | +    );
 | |
| 2307 | +    this._torConnectButton.addEventListener("click", () => {
 | |
| 2308 | +      TorConnect.openTorConnect({ beginBootstrap: true });
 | |
| 2309 | +    });
 | |
| 2310 | + | |
| 2311 | +    this._updateInternetStatus("unknown");
 | |
| 2312 | +    this._updateTorConnectionStatus();
 | |
| 2313 | + | |
| 2314 | +    Services.obs.addObserver(this, TorConnectTopics.StateChange);
 | |
| 2315 | +  },
 | |
| 2316 | + | |
| 2317 | +  /**
 | |
| 2318 | +   * Un-initialize the area.
 | |
| 2319 | +   */
 | |
| 2320 | +  deinint() {
 | |
| 2321 | +    Services.obs.removeObserver(this, TorConnectTopics.StateChange);
 | |
| 2322 | +  },
 | |
| 2323 | + | |
| 2324 | +  observe(subject, topic, data) {
 | |
| 2325 | +    switch (topic) {
 | |
| 2326 | +      // triggered when tor connect state changes and we may
 | |
| 2327 | +      // need to update the messagebox
 | |
| 2328 | +      case TorConnectTopics.StateChange: {
 | |
| 2329 | +        this._updateTorConnectionStatus();
 | |
| 2330 | +        break;
 | |
| 2331 | +      }
 | |
| 2332 | +    }
 | |
| 2333 | +  },
 | |
| 2334 | + | |
| 2335 | +  /**
 | |
| 2336 | +   * Whether the test should be disabled.
 | |
| 2337 | +   *
 | |
| 2338 | +   * @type {boolean}
 | |
| 2339 | +   */
 | |
| 2340 | +  _internetTestDisabled: false,
 | |
| 2341 | +  /**
 | |
| 2342 | +   * Start the internet test.
 | |
| 2343 | +   */
 | |
| 2344 | +  async _startInternetTest() {
 | |
| 2345 | +    if (this._internetTestDisabled) {
 | |
| 2346 | +      return;
 | |
| 2347 | +    }
 | |
| 2348 | +    this._internetTestDisabled = true;
 | |
| 2349 | +    // We use "aria-disabled" rather than the "disabled" attribute so that the
 | |
| 2350 | +    // button can remain focusable during the test.
 | |
| 2351 | +    this._internetTestButton.setAttribute("aria-disabled", "true");
 | |
| 2352 | +    this._internetTestButton.classList.add("spoof-button-disabled");
 | |
| 2353 | +    try {
 | |
| 2354 | +      this._updateInternetStatus("testing");
 | |
| 2355 | +      const mrpc = new MoatRPC();
 | |
| 2356 | +      let status = null;
 | |
| 2357 | +      try {
 | |
| 2358 | +        await mrpc.init();
 | |
| 2359 | +        status = await mrpc.testInternetConnection();
 | |
| 2360 | +      } catch (err) {
 | |
| 2361 | +        console.log("Error while checking the Internet connection", err);
 | |
| 2362 | +      } finally {
 | |
| 2363 | +        mrpc.uninit();
 | |
| 2364 | +      }
 | |
| 2365 | +      if (status) {
 | |
| 2366 | +        this._updateInternetStatus(status.successful ? "online" : "offline");
 | |
| 2367 | +      } else {
 | |
| 2368 | +        this._updateInternetStatus("unknown");
 | |
| 2369 | +      }
 | |
| 2370 | +    } finally {
 | |
| 2371 | +      this._internetTestButton.removeAttribute("aria-disabled");
 | |
| 2372 | +      this._internetTestButton.classList.remove("spoof-button-disabled");
 | |
| 2373 | +      this._internetTestDisabled = false;
 | |
| 2374 | +    }
 | |
| 2375 | +  },
 | |
| 2376 | + | |
| 2377 | +  /**
 | |
| 2378 | +   * Update the shown internet status.
 | |
| 2379 | +   *
 | |
| 2380 | +   * @param {string} stateName - The name of the state to show.
 | |
| 2381 | +   */
 | |
| 2382 | +  _updateInternetStatus(stateName) {
 | |
| 2383 | +    let l10nId;
 | |
| 2384 | +    switch (stateName) {
 | |
| 2385 | +      case "testing":
 | |
| 2386 | +        l10nId = "tor-connection-internet-status-testing";
 | |
| 2387 | +        break;
 | |
| 2388 | +      case "offline":
 | |
| 2389 | +        l10nId = "tor-connection-internet-status-offline";
 | |
| 2390 | +        break;
 | |
| 2391 | +      case "online":
 | |
| 2392 | +        l10nId = "tor-connection-internet-status-online";
 | |
| 2393 | +        break;
 | |
| 2394 | +    }
 | |
| 2395 | +    if (l10nId) {
 | |
| 2396 | +      this._internetResultEl.setAttribute("data-l10n-id", l10nId);
 | |
| 2397 | +    } else {
 | |
| 2398 | +      this._internetResultEl.removeAttribute("data-l10n-id");
 | |
| 2399 | +      this._internetResultEl.textContent = "";
 | |
| 2400 | +    }
 | |
| 2401 | + | |
| 2402 | +    this._internetAreaEl.classList.toggle(
 | |
| 2403 | +      "status-loading",
 | |
| 2404 | +      stateName === "testing"
 | |
| 2405 | +    );
 | |
| 2406 | +    this._internetAreaEl.classList.toggle(
 | |
| 2407 | +      "status-offline",
 | |
| 2408 | +      stateName === "offline"
 | |
| 2409 | +    );
 | |
| 2410 | +  },
 | |
| 2411 | + | |
| 2412 | +  /**
 | |
| 2413 | +   * Update the shown Tor connection status.
 | |
| 2414 | +   */
 | |
| 2415 | +  _updateTorConnectionStatus() {
 | |
| 2416 | +    const buttonHadFocus = this._torConnectButton.contains(
 | |
| 2417 | +      document.activeElement
 | |
| 2418 | +    );
 | |
| 2419 | +    const isBootstrapped = TorConnect.state === TorConnectState.Bootstrapped;
 | |
| 2420 | +    const isBlocked = !isBootstrapped && TorConnect.potentiallyBlocked;
 | |
| 2421 | +    let l10nId;
 | |
| 2422 | +    if (isBootstrapped) {
 | |
| 2423 | +      l10nId = "tor-connection-network-status-connected";
 | |
| 2424 | +    } else if (isBlocked) {
 | |
| 2425 | +      l10nId = "tor-connection-network-status-blocked";
 | |
| 2426 | +    } else {
 | |
| 2427 | +      l10nId = "tor-connection-network-status-not-connected";
 | |
| 2428 | +    }
 | |
| 2429 | + | |
| 2430 | +    document.l10n.setAttributes(this._torResultEl, l10nId);
 | |
| 2431 | +    this._torAreaEl.classList.toggle("status-connected", isBootstrapped);
 | |
| 2432 | +    this._torAreaEl.classList.toggle("status-blocked", isBlocked);
 | |
| 2433 | +    if (isBootstrapped && buttonHadFocus) {
 | |
| 2434 | +      // Button has become hidden and will loose focus. Most likely this has
 | |
| 2435 | +      // happened because the user clicked the button to open about:torconnect.
 | |
| 2436 | +      // Since this is near the top of the page, we move focus to the search
 | |
| 2437 | +      // input (for when the user returns).
 | |
| 2438 | +      gSearchResultsPane.searchInput.focus();
 | |
| 2439 | +    }
 | |
| 2440 | +  },
 | |
| 2441 | +};
 | |
| 2442 | + | |
| 2286 | 2443 |  /*
 | 
| 2287 | 2444 |    Connection Pane
 | 
| 2288 | 2445 | |
| ... | ... | @@ -2304,8 +2461,6 @@ const gConnectionPane = (function () { | 
| 2304 | 2461 |      // cached frequently accessed DOM elements
 | 
| 2305 | 2462 |      _enableQuickstartCheckbox: null,
 | 
| 2306 | 2463 | |
| 2307 | -    _internetStatus: InternetStatus.Unknown,
 | |
| 2308 | - | |
| 2309 | 2464 |      // populate xul with strings and cache the relevant elements
 | 
| 2310 | 2465 |      _populateXUL() {
 | 
| 2311 | 2466 |        // saves tor settings to disk when navigate away from about:preferences
 | 
| ... | ... | @@ -2321,80 +2476,6 @@ const gConnectionPane = (function () { | 
| 2321 | 2476 |          }
 | 
| 2322 | 2477 |        });
 | 
| 2323 | 2478 | |
| 2324 | -      // Internet and Tor status
 | |
| 2325 | -      const internetStatus = document.getElementById(
 | |
| 2326 | -        "torPreferences-status-internet"
 | |
| 2327 | -      );
 | |
| 2328 | -      const internetResult = internetStatus.querySelector(
 | |
| 2329 | -        ".torPreferences-status-result"
 | |
| 2330 | -      );
 | |
| 2331 | -      const internetTest = document.getElementById(
 | |
| 2332 | -        "torPreferences-status-internet-test"
 | |
| 2333 | -      );
 | |
| 2334 | -      internetTest.addEventListener("click", () => {
 | |
| 2335 | -        this.onInternetTest();
 | |
| 2336 | -      });
 | |
| 2337 | - | |
| 2338 | -      const torConnectStatus = document.getElementById(
 | |
| 2339 | -        "torPreferences-status-tor-connect"
 | |
| 2340 | -      );
 | |
| 2341 | -      const torConnectResult = torConnectStatus.querySelector(
 | |
| 2342 | -        ".torPreferences-status-result"
 | |
| 2343 | -      );
 | |
| 2344 | -      const torConnectButton = document.getElementById(
 | |
| 2345 | -        "torPreferences-status-tor-connect-button"
 | |
| 2346 | -      );
 | |
| 2347 | -      torConnectButton.addEventListener("click", () => {
 | |
| 2348 | -        TorConnect.openTorConnect({ beginBootstrap: true });
 | |
| 2349 | -      });
 | |
| 2350 | - | |
| 2351 | -      this._populateStatus = () => {
 | |
| 2352 | -        let internetId;
 | |
| 2353 | -        switch (this._internetStatus) {
 | |
| 2354 | -          case InternetStatus.Online:
 | |
| 2355 | -            internetStatus.classList.remove("offline");
 | |
| 2356 | -            internetId = "tor-connection-internet-status-online";
 | |
| 2357 | -            break;
 | |
| 2358 | -          case InternetStatus.Offline:
 | |
| 2359 | -            internetStatus.classList.add("offline");
 | |
| 2360 | -            internetId = "tor-connection-internet-status-offline";
 | |
| 2361 | -            break;
 | |
| 2362 | -          case InternetStatus.Unknown:
 | |
| 2363 | -          default:
 | |
| 2364 | -            internetStatus.classList.remove("offline");
 | |
| 2365 | -            break;
 | |
| 2366 | -        }
 | |
| 2367 | -        if (internetId) {
 | |
| 2368 | -          document.l10n.setAttributes(internetResult, internetId);
 | |
| 2369 | -          internetResult.hidden = false;
 | |
| 2370 | -        } else {
 | |
| 2371 | -          internetResult.hidden = true;
 | |
| 2372 | -        }
 | |
| 2373 | - | |
| 2374 | -        let connectId;
 | |
| 2375 | -        // FIXME: What about the TorConnectState.Disabled state?
 | |
| 2376 | -        if (TorConnect.state === TorConnectState.Bootstrapped) {
 | |
| 2377 | -          torConnectStatus.classList.add("connected");
 | |
| 2378 | -          torConnectStatus.classList.remove("blocked");
 | |
| 2379 | -          connectId = "tor-connection-network-status-connected";
 | |
| 2380 | -          // NOTE: If the button is focused when we hide it, the focus may be
 | |
| 2381 | -          // lost. But we don't have an obvious place to put the focus instead.
 | |
| 2382 | -          torConnectButton.hidden = true;
 | |
| 2383 | -        } else {
 | |
| 2384 | -          torConnectStatus.classList.remove("connected");
 | |
| 2385 | -          torConnectStatus.classList.toggle(
 | |
| 2386 | -            "blocked",
 | |
| 2387 | -            TorConnect.potentiallyBlocked
 | |
| 2388 | -          );
 | |
| 2389 | -          connectId = TorConnect.potentiallyBlocked
 | |
| 2390 | -            ? "tor-connection-network-status-blocked"
 | |
| 2391 | -            : "tor-connection-network-status-not-connected";
 | |
| 2392 | -          torConnectButton.hidden = false;
 | |
| 2393 | -        }
 | |
| 2394 | -        document.l10n.setAttributes(torConnectResult, connectId);
 | |
| 2395 | -      };
 | |
| 2396 | -      this._populateStatus();
 | |
| 2397 | - | |
| 2398 | 2479 |        // Quickstart
 | 
| 2399 | 2480 |        this._enableQuickstartCheckbox = document.getElementById(
 | 
| 2400 | 2481 |          "torPreferences-quickstart-toggle"
 | 
| ... | ... | @@ -2514,6 +2595,7 @@ const gConnectionPane = (function () { | 
| 2514 | 2595 | |
| 2515 | 2596 |      init() {
 | 
| 2516 | 2597 |        gBridgeSettings.init();
 | 
| 2598 | +      gNetworkStatus.init();
 | |
| 2517 | 2599 | |
| 2518 | 2600 |        TorSettings.initializedPromise.then(() => this._populateXUL());
 | 
| 2519 | 2601 | |
| ... | ... | @@ -2526,6 +2608,7 @@ const gConnectionPane = (function () { | 
| 2526 | 2608 | |
| 2527 | 2609 |      uninit() {
 | 
| 2528 | 2610 |        gBridgeSettings.uninit();
 | 
| 2611 | +      gNetworkStatus.uninit();
 | |
| 2529 | 2612 | |
| 2530 | 2613 |        // unregister our observer topics
 | 
| 2531 | 2614 |        Services.obs.removeObserver(this, TorSettingsTopics.SettingsChanged);
 | 
| ... | ... | @@ -2554,36 +2637,12 @@ const gConnectionPane = (function () { | 
| 2554 | 2637 |          // triggered when tor connect state changes and we may
 | 
| 2555 | 2638 |          // need to update the messagebox
 | 
| 2556 | 2639 |          case TorConnectTopics.StateChange: {
 | 
| 2557 | -          this.onStateChange();
 | |
| 2640 | +          this._showAutoconfiguration();
 | |
| 2558 | 2641 |            break;
 | 
| 2559 | 2642 |          }
 | 
| 2560 | 2643 |        }
 | 
| 2561 | 2644 |      },
 | 
| 2562 | 2645 | |
| 2563 | -    async onInternetTest() {
 | |
| 2564 | -      const mrpc = new MoatRPC();
 | |
| 2565 | -      let status = null;
 | |
| 2566 | -      try {
 | |
| 2567 | -        await mrpc.init();
 | |
| 2568 | -        status = await mrpc.testInternetConnection();
 | |
| 2569 | -      } catch (err) {
 | |
| 2570 | -        console.log("Error while checking the Internet connection", err);
 | |
| 2571 | -      } finally {
 | |
| 2572 | -        mrpc.uninit();
 | |
| 2573 | -      }
 | |
| 2574 | -      if (status) {
 | |
| 2575 | -        this._internetStatus = status.successful
 | |
| 2576 | -          ? InternetStatus.Online
 | |
| 2577 | -          : InternetStatus.Offline;
 | |
| 2578 | -        this._populateStatus();
 | |
| 2579 | -      }
 | |
| 2580 | -    },
 | |
| 2581 | - | |
| 2582 | -    onStateChange() {
 | |
| 2583 | -      this._populateStatus();
 | |
| 2584 | -      this._showAutoconfiguration();
 | |
| 2585 | -    },
 | |
| 2586 | - | |
| 2587 | 2646 |      onAdvancedSettings() {
 | 
| 2588 | 2647 |        gSubDialog.open(
 | 
| 2589 | 2648 |          "chrome://browser/content/torpreferences/connectionSettingsDialog.xhtml",
 | 
| ... | ... | @@ -5,16 +5,13 @@ | 
| 5 | 5 |    src="">"chrome://browser/content/torpreferences/connectionPane.js"
 | 
| 6 | 6 |  />
 | 
| 7 | 7 |  <html:template id="template-paneConnection">
 | 
| 8 | -  <hbox
 | |
| 8 | +  <vbox
 | |
| 9 | 9 |      id="torPreferencesCategory"
 | 
| 10 | 10 |      class="subcategory"
 | 
| 11 | 11 |      data-category="paneConnection"
 | 
| 12 | 12 |      hidden="true"
 | 
| 13 | 13 |    >
 | 
| 14 | 14 |      <html:h1 data-l10n-id="tor-connection-settings-heading"></html:h1>
 | 
| 15 | -  </hbox>
 | |
| 16 | - | |
| 17 | -  <groupbox data-category="paneConnection" hidden="true">
 | |
| 18 | 15 |      <description flex="1">
 | 
| 19 | 16 |        <html:span
 | 
| 20 | 17 |          data-l10n-id="tor-connection-overview"
 | 
| ... | ... | @@ -28,46 +25,61 @@ | 
| 28 | 25 |          data-l10n-id="tor-connection-browser-learn-more-link"
 | 
| 29 | 26 |        />
 | 
| 30 | 27 |      </description>
 | 
| 31 | -  </groupbox>
 | |
| 32 | - | |
| 33 | -  <groupbox
 | |
| 34 | -    id="torPreferences-status-group"
 | |
| 35 | -    data-category="paneConnection"
 | |
| 36 | -    hidden="true"
 | |
| 37 | -  >
 | |
| 38 | -    <hbox id="torPreferences-status-box">
 | |
| 39 | -      <hbox
 | |
| 40 | -        id="torPreferences-status-internet"
 | |
| 41 | -        class="torPreferences-status-grouping"
 | |
| 28 | +    <!-- Keep within #torPreferencesCategory so this won't appear in search
 | |
| 29 | +       - results. -->
 | |
| 30 | +    <html:div
 | |
| 31 | +      id="network-status-internet-area"
 | |
| 32 | +      class="network-status-area"
 | |
| 33 | +      role="group"
 | |
| 34 | +      aria-labelledby="network-status-internet-area-label"
 | |
| 35 | +    >
 | |
| 36 | +      <html:img alt="" class="network-status-icon" />
 | |
| 37 | +      <!-- Use an aria-live area to announce "Internet: Online" or
 | |
| 38 | +         - "Internet: Offline". We only expect this to change when triggered by
 | |
| 39 | +         - the user clicking the "Test" button, so shouldn't occur unexpectedly.
 | |
| 40 | +         -->
 | |
| 41 | +      <html:div
 | |
| 42 | +        class="network-status-live-area"
 | |
| 43 | +        aria-live="polite"
 | |
| 44 | +        aria-atomic="true"
 | |
| 42 | 45 |        >
 | 
| 43 | -        <image class="torPreferences-status-icon" />
 | |
| 44 | 46 |          <html:span
 | 
| 45 | -          class="torPreferences-status-name"
 | |
| 47 | +          id="network-status-internet-area-label"
 | |
| 48 | +          class="network-status-label"
 | |
| 46 | 49 |            data-l10n-id="tor-connection-internet-status-label"
 | 
| 47 | 50 |          ></html:span>
 | 
| 48 | -        <html:span class="torPreferences-status-result"></html:span>
 | |
| 49 | -        <html:button
 | |
| 50 | -          id="torPreferences-status-internet-test"
 | |
| 51 | -          data-l10n-id="tor-connection-internet-status-test-button"
 | |
| 52 | -        ></html:button>
 | |
| 53 | -      </hbox>
 | |
| 54 | -      <hbox
 | |
| 55 | -        id="torPreferences-status-tor-connect"
 | |
| 56 | -        class="torPreferences-status-grouping"
 | |
| 57 | -      >
 | |
| 58 | -        <image class="torPreferences-status-icon" />
 | |
| 59 | -        <html:span
 | |
| 60 | -          class="torPreferences-status-name"
 | |
| 61 | -          data-l10n-id="tor-connection-network-status-label"
 | |
| 62 | -        ></html:span>
 | |
| 63 | -        <html:span class="torPreferences-status-result"></html:span>
 | |
| 64 | -        <html:button
 | |
| 65 | -          id="torPreferences-status-tor-connect-button"
 | |
| 66 | -          data-l10n-id="tor-connection-network-status-connect-button"
 | |
| 67 | -        ></html:button>
 | |
| 68 | -      </hbox>
 | |
| 69 | -    </hbox>
 | |
| 70 | -  </groupbox>
 | |
| 51 | +        <img alt="" class="network-status-loading-icon" />
 | |
| 52 | +        <html:span class="network-status-result"></html:span>
 | |
| 53 | +      </html:div>
 | |
| 54 | +      <html:button
 | |
| 55 | +        id="network-status-internet-test-button"
 | |
| 56 | +        data-l10n-id="tor-connection-internet-status-test-button"
 | |
| 57 | +      ></html:button>
 | |
| 58 | +    </html:div>
 | |
| 59 | +    <html:div
 | |
| 60 | +      id="network-status-tor-area"
 | |
| 61 | +      class="network-status-area"
 | |
| 62 | +      role="group"
 | |
| 63 | +      aria-labelledby="network-status-tor-area-label"
 | |
| 64 | +    >
 | |
| 65 | +      <html:img alt="" class="network-status-icon" />
 | |
| 66 | +      <!-- NOTE: Unlike #network-status-internet-area, we do not wrap the label
 | |
| 67 | +         - and status ("Tor network: Not connected", etc) in an aria-live area.
 | |
| 68 | +         - This is not likely to change whilst this page has focus.
 | |
| 69 | +         - Moreover, the status is already present in the torconnect status bar
 | |
| 70 | +         - in the window tab bar. -->
 | |
| 71 | +      <html:span
 | |
| 72 | +        id="network-status-tor-area-label"
 | |
| 73 | +        class="network-status-label"
 | |
| 74 | +        data-l10n-id="tor-connection-network-status-label"
 | |
| 75 | +      ></html:span>
 | |
| 76 | +      <html:span class="network-status-result"></html:span>
 | |
| 77 | +      <html:button
 | |
| 78 | +        id="network-status-tor-connect-button"
 | |
| 79 | +        data-l10n-id="tor-connection-network-status-connect-button"
 | |
| 80 | +      ></html:button>
 | |
| 81 | +    </html:div>
 | |
| 82 | +  </vbox>
 | |
| 71 | 83 | |
| 72 | 84 |    <!-- Quickstart -->
 | 
| 73 | 85 |    <groupbox data-category="paneConnection" hidden="true">
 | 
| ... | ... | @@ -5,25 +5,36 @@ | 
| 5 | 5 |    list-style-image: url("chrome://global/content/torconnect/tor-connect.svg");
 | 
| 6 | 6 |  }
 | 
| 7 | 7 | |
| 8 | +/* Make a button appear disabled, whilst still allowing it to keep keyboard
 | |
| 9 | + * focus. */
 | |
| 10 | +button.spoof-button-disabled {
 | |
| 11 | +  /* Borrow the :disabled rule from common-shared.css */
 | |
| 12 | +  opacity: 0.4;
 | |
| 13 | +  /* Also ensure it does not get hover or active styling. */
 | |
| 14 | +  pointer-events: none;
 | |
| 15 | +}
 | |
| 16 | + | |
| 8 | 17 |  /* Status */
 | 
| 9 | 18 | |
| 10 | -#torPreferences-status-box {
 | |
| 11 | -  display: flex;
 | |
| 12 | -  align-items: center;
 | |
| 13 | -  gap: 32px;
 | |
| 19 | +#network-status-internet-area {
 | |
| 20 | +  margin-block: 16px;
 | |
| 21 | +}
 | |
| 22 | + | |
| 23 | +#network-status-tor-area {
 | |
| 24 | +  margin-block: 0 32px;
 | |
| 14 | 25 |  }
 | 
| 15 | 26 | |
| 16 | -.torPreferences-status-grouping {
 | |
| 27 | +.network-status-area {
 | |
| 17 | 28 |    display: flex;
 | 
| 18 | 29 |    align-items: center;
 | 
| 19 | 30 |    white-space: nowrap;
 | 
| 20 | 31 |  }
 | 
| 21 | 32 | |
| 22 | -.torPreferences-status-grouping > * {
 | |
| 33 | +.network-status-area > * {
 | |
| 23 | 34 |    flex: 0 0 auto;
 | 
| 24 | 35 |  }
 | 
| 25 | 36 | |
| 26 | -.torPreferences-status-icon {
 | |
| 37 | +.network-status-icon {
 | |
| 27 | 38 |    width: 18px;
 | 
| 28 | 39 |    height: 18px;
 | 
| 29 | 40 |    margin-inline-end: 8px;
 | 
| ... | ... | @@ -32,42 +43,74 @@ | 
| 32 | 43 |    stroke: currentColor;
 | 
| 33 | 44 |  }
 | 
| 34 | 45 | |
| 35 | -#torPreferences-status-internet .torPreferences-status-icon {
 | |
| 36 | -  list-style-image: url("chrome://browser/content/torpreferences/network.svg");
 | |
| 46 | +#network-status-internet-area .network-status-icon {
 | |
| 47 | +  content: url("chrome://browser/content/torpreferences/network.svg");
 | |
| 37 | 48 |  }
 | 
| 38 | 49 | |
| 39 | -#torPreferences-status-tor-connect .torPreferences-status-icon {
 | |
| 40 | -  list-style-image: url("chrome://global/content/torconnect/tor-connect-broken.svg");
 | |
| 50 | +#network-status-internet-area.status-offline .network-status-icon {
 | |
| 51 | +  content: url("chrome://browser/content/torpreferences/network-broken.svg");
 | |
| 41 | 52 |  }
 | 
| 42 | 53 | |
| 43 | -.torPreferences-status-name {
 | |
| 44 | -  font-weight: bold;
 | |
| 45 | -  margin-inline-end: 0.75em;
 | |
| 54 | +#network-status-tor-area .network-status-icon {
 | |
| 55 | +  content: url("chrome://global/content/torconnect/tor-connect.svg");
 | |
| 46 | 56 |  }
 | 
| 47 | 57 | |
| 48 | -.torPreferences-status-result {
 | |
| 49 | -  margin-inline-end: 8px;
 | |
| 58 | +#network-status-tor-area:not(.status-connected) .network-status-icon {
 | |
| 59 | +  content: url("chrome://global/content/torconnect/tor-connect-broken.svg");
 | |
| 50 | 60 |  }
 | 
| 51 | 61 | |
| 52 | -#torPreferences-status-internet.offline .torPreferences-status-icon {
 | |
| 53 | -  list-style-image: url("chrome://browser/content/torpreferences/network-broken.svg");
 | |
| 62 | +#network-status-tor-area.status-blocked .network-status-icon {
 | |
| 63 | +  /* Same as .tor-connect-status-potentially-blocked. */
 | |
| 64 | +  stroke: #c50042;
 | |
| 54 | 65 |  }
 | 
| 55 | 66 | |
| 56 | -#torPreferences-status-tor-connect.connected .torPreferences-status-icon {
 | |
| 57 | -  list-style-image: url("chrome://global/content/torconnect/tor-connect.svg");
 | |
| 67 | +@media (prefers-color-scheme: dark) {
 | |
| 68 | +  #network-status-tor-area.status-blocked  .network-status-icon {
 | |
| 69 | +    stroke: #ff9aa2;
 | |
| 70 | +  }
 | |
| 58 | 71 |  }
 | 
| 59 | 72 | |
| 60 | -#torPreferences-status-tor-connect.blocked .torPreferences-status-icon {
 | |
| 61 | -  /* Same as .tor-connect-status-potentially-blocked. */
 | |
| 62 | -  stroke: #c50042;
 | |
| 73 | +.network-status-live-area {
 | |
| 74 | +  display: contents;
 | |
| 75 | +}
 | |
| 76 | + | |
| 77 | +.network-status-label {
 | |
| 78 | +  font-weight: bold;
 | |
| 79 | +  margin-inline-end: 0.75em;
 | |
| 80 | +}
 | |
| 81 | + | |
| 82 | +.network-status-loading-icon {
 | |
| 83 | +  margin-inline-end: 0.5em;
 | |
| 84 | +  width: 16px;
 | |
| 85 | +  height: 16px;
 | |
| 86 | +  content: image-set(
 | |
| 87 | +    url("chrome://global/skin/icons/tor-light-loading.png"),
 | |
| 88 | +    url("chrome://global/skin/icons/tor-light-loading@xxxxxx") 2x
 | |
| 89 | +  );
 | |
| 63 | 90 |  }
 | 
| 64 | 91 | |
| 65 | 92 |  @media (prefers-color-scheme: dark) {
 | 
| 66 | -  #torPreferences-status-tor-connect.blocked .torPreferences-status-icon {
 | |
| 67 | -    stroke: #ff9aa2;
 | |
| 93 | +  .network-status-loading-icon {
 | |
| 94 | +    content: image-set(
 | |
| 95 | +      url("chrome://global/skin/icons/tor-dark-loading.png"),
 | |
| 96 | +      url("chrome://global/skin/icons/tor-dark-loading@xxxxxx") 2x
 | |
| 97 | +    );
 | |
| 68 | 98 |    }
 | 
| 69 | 99 |  }
 | 
| 70 | 100 | |
| 101 | +#network-status-internet-area:not(.status-loading) .network-status-loading-icon {
 | |
| 102 | +  display: none;
 | |
| 103 | +}
 | |
| 104 | + | |
| 105 | +.network-status-result:not(:empty) {
 | |
| 106 | +  margin-inline-end: 0.75em;
 | |
| 107 | +}
 | |
| 108 | + | |
| 109 | +#network-status-tor-area.status-connected #network-status-tor-connect-button {
 | |
| 110 | +  /* Hide button when already connected. */
 | |
| 111 | +  display: none;
 | |
| 112 | +}
 | |
| 113 | + | |
| 71 | 114 |  /* Bridge settings */
 | 
| 72 | 115 | |
| 73 | 116 |  .tor-focusable-heading:focus-visible {
 | 
| ... | ... | @@ -65,6 +65,9 @@ tor-connection-internet-status-label = Internet: | 
| 65 | 65 |  # Here "Test" is a verb, as in "test the internet connection".
 | 
| 66 | 66 |  # Uses sentence case in English (US).
 | 
| 67 | 67 |  tor-connection-internet-status-test-button = Test
 | 
| 68 | +# Shown when testing the internet status.
 | |
| 69 | +# Uses sentence case in English (US).
 | |
| 70 | +tor-connection-internet-status-testing = Testing…
 | |
| 68 | 71 |  # Shown when the user is connected to the internet.
 | 
| 69 | 72 |  # Uses sentence case in English (US).
 | 
| 70 | 73 |  tor-connection-internet-status-online = Online
 | 
| ... | ... | @@ -86,6 +86,10 @@ | 
| 86 | 86 |    skin/classic/global/icons/link.svg                       (../../shared/icons/link.svg)
 | 
| 87 | 87 |    skin/classic/global/icons/loading.png                    (../../shared/icons/loading.png)
 | 
| 88 | 88 |    skin/classic/global/icons/loading@xxxxxx                 (../../shared/icons/loading@xxxxxx)
 | 
| 89 | +  skin/classic/global/icons/tor-light-loading.png          (../../shared/icons/tor-light-loading.png)
 | |
| 90 | +  skin/classic/global/icons/tor-light-loading@xxxxxx       (../../shared/icons/tor-light-loading@xxxxxx)
 | |
| 91 | +  skin/classic/global/icons/tor-dark-loading.png           (../../shared/icons/tor-dark-loading.png)
 | |
| 92 | +  skin/classic/global/icons/tor-dark-loading@xxxxxx        (../../shared/icons/tor-dark-loading@xxxxxx)
 | |
| 89 | 93 |    skin/classic/global/icons/more.svg                       (../../shared/icons/more.svg)
 | 
| 90 | 94 |    skin/classic/global/icons/open-in-new.svg                (../../shared/icons/open-in-new.svg)
 | 
| 91 | 95 |    skin/classic/global/icons/page-portrait.svg              (../../shared/icons/page-portrait.svg)
 | 
| 1 | +"""
 | |
| 2 | +Script to convert the loading.png and loading@xxxxxx blue spinners to purple
 | |
| 3 | +spinners for Tor Browser, for both the light and dark themes.
 | |
| 4 | +"""
 | |
| 5 | + | |
| 6 | +import argparse
 | |
| 7 | +import colorsys
 | |
| 8 | +import os
 | |
| 9 | + | |
| 10 | +from PIL import ExifTags, Image, ImageFilter
 | |
| 11 | + | |
| 12 | +parser = argparse.ArgumentParser(description="Convert the loading APNG to be purple.")
 | |
| 13 | +parser.add_argument("loading_png", help="The loading png to convert")
 | |
| 14 | +parser.add_argument(
 | |
| 15 | +    "--light", required=True, help="The name of the light-theme purple output image"
 | |
| 16 | +)
 | |
| 17 | +parser.add_argument(
 | |
| 18 | +    "--dark", required=True, help="The name of the dark-theme purple output image"
 | |
| 19 | +)
 | |
| 20 | + | |
| 21 | +parsed_args = parser.parse_args()
 | |
| 22 | + | |
| 23 | +orig_im = Image.open(parsed_args.loading_png)
 | |
| 24 | + | |
| 25 | + | |
| 26 | +def filter_to_light_theme(r, g, b):
 | |
| 27 | +    h, s, v = colorsys.rgb_to_hsv(r, g, b)
 | |
| 28 | +    # Convert from HSV 0.58, 1.0, 255 (start of the circle)
 | |
| 29 | +    # to --purple-60 #8000d7 HSV 0.766, 1.0, 215
 | |
| 30 | +    h = 0.766
 | |
| 31 | +    v = v * 215 / 255
 | |
| 32 | +    return colorsys.hsv_to_rgb(h, s, v)
 | |
| 33 | + | |
| 34 | + | |
| 35 | +def filter_to_dark_theme(r, g, b):
 | |
| 36 | +    h, s, v = colorsys.rgb_to_hsv(r, g, b)
 | |
| 37 | +    # Convert from HSV 0.58, 1.0, 255 (start of the circle)
 | |
| 38 | +    # to --purple-30 #c069ff HSV 0.766, 0.59, 255
 | |
| 39 | +    h = 0.766
 | |
| 40 | +    s = s * 0.59 / 1.0
 | |
| 41 | +    return colorsys.hsv_to_rgb(h, s, v)
 | |
| 42 | + | |
| 43 | + | |
| 44 | +filt_light = ImageFilter.Color3DLUT.generate(65, filter_to_light_theme)
 | |
| 45 | +filt_dark = ImageFilter.Color3DLUT.generate(65, filter_to_dark_theme)
 | |
| 46 | + | |
| 47 | +transformed_light = []
 | |
| 48 | +transformed_dark = []
 | |
| 49 | +duration = orig_im.info["duration"]
 | |
| 50 | + | |
| 51 | +# Transform each APNG frame individually.
 | |
| 52 | +for frame in range(orig_im.n_frames):
 | |
| 53 | +    orig_im.seek(frame)
 | |
| 54 | +    transformed_light.append(orig_im.filter(filt_light))
 | |
| 55 | +    transformed_dark.append(orig_im.filter(filt_dark))
 | |
| 56 | + | |
| 57 | +exif = Image.Exif()
 | |
| 58 | +exif[ExifTags.Base.ImageDescription] = f"Generated by {os.path.basename(__file__)}"
 | |
| 59 | + | |
| 60 | +transformed_light[0].save(
 | |
| 61 | +    parsed_args.light,
 | |
| 62 | +    save_all=True,
 | |
| 63 | +    append_images=transformed_light[1:],
 | |
| 64 | +    duration=duration,
 | |
| 65 | +    exif=exif,
 | |
| 66 | +)
 | |
| 67 | + | |
| 68 | +transformed_dark[0].save(
 | |
| 69 | +    parsed_args.dark,
 | |
| 70 | +    save_all=True,
 | |
| 71 | +    append_images=transformed_dark[1:],
 | |
| 72 | +    duration=duration,
 | |
| 73 | +    exif=exif,
 | |
| 74 | +) |