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@2x.png
- + toolkit/themes/shared/icons/tor-light-loading.png
- + toolkit/themes/shared/icons/tor-light-loading@2x.png
- + 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@2x.png") 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@2x.png") 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@2x.png (../../shared/icons/loading@2x.png)
|
89 | + skin/classic/global/icons/tor-light-loading.png (../../shared/icons/tor-light-loading.png)
|
|
90 | + skin/classic/global/icons/tor-light-loading@2x.png (../../shared/icons/tor-light-loading@2x.png)
|
|
91 | + skin/classic/global/icons/tor-dark-loading.png (../../shared/icons/tor-dark-loading.png)
|
|
92 | + skin/classic/global/icons/tor-dark-loading@2x.png (../../shared/icons/tor-dark-loading@2x.png)
|
|
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@2x.png 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 | +) |