tbb-commits
Threads by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 20970 discussions
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] 2 commits: squash! Bug 40933: Add tor-launcher functionality
by Pier Angelo Vendrame (@pierov) 04 Aug '23
by Pier Angelo Vendrame (@pierov) 04 Aug '23
04 Aug '23
Pier Angelo Vendrame pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
57b25177 by Pier Angelo Vendrame at 2023-08-04T20:02:03+02:00
squash! Bug 40933: Add tor-launcher functionality
Bug 41926: Reimplement the control port
- - - - -
9722ca26 by Pier Angelo Vendrame at 2023-08-04T20:02:04+02:00
fixup! Bug 10760: Integrate TorButton to TorBrowser core
Removed torbutton.js, tor-control-port.js and utils.js.
- - - - -
13 changed files:
- browser/base/content/browser.xhtml
- + toolkit/components/tor-launcher/TorControlPort.sys.mjs
- toolkit/components/tor-launcher/TorMonitorService.sys.mjs
- toolkit/components/tor-launcher/TorProtocolService.sys.mjs
- toolkit/components/tor-launcher/moz.build
- − toolkit/torbutton/chrome/content/torbutton.js
- − toolkit/torbutton/components.conf
- toolkit/torbutton/jar.mn
- − toolkit/torbutton/modules/TorbuttonLogger.jsm
- − toolkit/torbutton/modules/tor-control-port.js
- − toolkit/torbutton/modules/utils.js
- toolkit/torbutton/moz.build
- tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js
Changes:
=====================================
browser/base/content/browser.xhtml
=====================================
@@ -130,17 +130,11 @@
Services.scriptloader.loadSubScript("chrome://browser/content/search/autocomplete-popup.js", this);
Services.scriptloader.loadSubScript("chrome://browser/content/search/searchbar.js", this);
Services.scriptloader.loadSubScript("chrome://browser/content/languageNotification.js", this);
- Services.scriptloader.loadSubScript("chrome://torbutton/content/torbutton.js", this);
window.onload = gBrowserInit.onLoad.bind(gBrowserInit);
window.onunload = gBrowserInit.onUnload.bind(gBrowserInit);
window.onclose = WindowIsClosing;
- //onLoad Handler
- try {
- window.addEventListener("load", torbutton_init);
- } catch (e) {}
-
window.addEventListener("MozBeforeInitialXULLayout",
gBrowserInit.onBeforeInitialXULLayout.bind(gBrowserInit), { once: true });
=====================================
toolkit/components/tor-launcher/TorControlPort.sys.mjs
=====================================
@@ -0,0 +1,1534 @@
+import { TorParsers } from "resource://gre/modules/TorParsers.sys.mjs";
+
+/**
+ * @callback MessageCallback A callback to receive messages from the control
+ * port.
+ * @param {string} message The message to handle
+ */
+/**
+ * @callback RemoveCallback A function used to remove a previously registered
+ * callback.
+ */
+
+class CallbackDispatcher {
+ #callbackPairs = [];
+
+ /**
+ * Register a callback to handle a certain type of responses.
+ *
+ * @param {RegExp} regex The regex that tells which messages the callback
+ * wants to handle.
+ * @param {MessageCallback} callback The function to call
+ * @returns {RemoveCallback} A function to remove the just added callback
+ */
+ addCallback(regex, callback) {
+ this.#callbackPairs.push([regex, callback]);
+ }
+
+ /**
+ * Push a certain message to all the callbacks whose regex matches it.
+ *
+ * @param {string} message The message to push to the callbacks
+ */
+ pushMessage(message) {
+ for (const [regex, callback] of this.#callbackPairs) {
+ if (message.match(regex)) {
+ callback(message);
+ }
+ }
+ }
+}
+
+/**
+ * A wrapper around XPCOM sockets and buffers to handle streams in a standard
+ * async JS fashion.
+ * This class can handle both Unix sockets and TCP sockets.
+ */
+class AsyncSocket {
+ /**
+ * The output stream used for write operations.
+ *
+ * @type {nsIAsyncOutputStream}
+ */
+ #outputStream;
+ /**
+ * The output stream can only have one registered callback at a time, so
+ * multiple writes need to be queued up (see nsIAsyncOutputStream.idl).
+ * Every item is associated with a promise we returned in write, and it will
+ * resolve it or reject it when called by the output stream.
+ *
+ * @type {nsIOutputStreamCallback[]}
+ */
+ #outputQueue = [];
+ /**
+ * The input stream.
+ *
+ * @type {nsIAsyncInputStream}
+ */
+ #inputStream;
+ /**
+ * An input stream adapter that makes reading from scripts easier.
+ *
+ * @type {nsIScriptableInputStream}
+ */
+ #scriptableInputStream;
+ /**
+ * The queue of callbacks to be used when we receive data.
+ * Every item is associated with a promise we returned in read, and it will
+ * resolve it or reject it when called by the input stream.
+ *
+ * @type {nsIInputStreamCallback[]}
+ */
+ #inputQueue = [];
+
+ /**
+ * Connect to a Unix socket. Not available on Windows.
+ *
+ * @param {nsIFile} ipcFile The path to the Unix socket to connect to.
+ */
+ static fromIpcFile(ipcFile) {
+ const sts = Cc[
+ "@mozilla.org/network/socket-transport-service;1"
+ ].getService(Ci.nsISocketTransportService);
+ const socket = new AsyncSocket();
+ const transport = sts.createUnixDomainTransport(ipcFile);
+ socket.#createStreams(transport);
+ return socket;
+ }
+
+ /**
+ * Connect to a TCP socket.
+ *
+ * @param {string} host The hostname to connect the TCP socket to.
+ * @param {number} port The port to connect the TCP socket to.
+ */
+ static fromSocketAddress(host, port) {
+ const sts = Cc[
+ "@mozilla.org/network/socket-transport-service;1"
+ ].getService(Ci.nsISocketTransportService);
+ const socket = new AsyncSocket();
+ const transport = sts.createTransport([], host, port, null, null);
+ socket.#createStreams(transport);
+ return socket;
+ }
+
+ #createStreams(socketTransport) {
+ const OPEN_UNBUFFERED = Ci.nsITransport.OPEN_UNBUFFERED;
+ this.#outputStream = socketTransport
+ .openOutputStream(OPEN_UNBUFFERED, 1, 1)
+ .QueryInterface(Ci.nsIAsyncOutputStream);
+
+ this.#inputStream = socketTransport
+ .openInputStream(OPEN_UNBUFFERED, 1, 1)
+ .QueryInterface(Ci.nsIAsyncInputStream);
+ this.#scriptableInputStream = Cc[
+ "@mozilla.org/scriptableinputstream;1"
+ ].createInstance(Ci.nsIScriptableInputStream);
+ this.#scriptableInputStream.init(this.#inputStream);
+ }
+
+ /**
+ * Asynchronously write string to underlying socket.
+ *
+ * When write is called, we create a new promise and queue it on the output
+ * queue. If it is the only element in the queue, we ask the output stream to
+ * run it immediately.
+ * Otherwise, the previous item of the queue will run it after it finishes.
+ *
+ * @param {string} str The string to write to the socket. The underlying
+ * implementation shoulw convert JS strings (UTF-16) into UTF-8 strings.
+ * See also write nsIOutputStream (the first argument is a string, not a
+ * wstring).
+ * @returns {Promise<number>} The number of written bytes
+ */
+ async write(str) {
+ return new Promise((resolve, reject) => {
+ // asyncWait next write request
+ const tryAsyncWait = () => {
+ if (this.#outputQueue.length) {
+ this.#outputStream.asyncWait(
+ this.#outputQueue.at(0), // next request
+ 0,
+ 0,
+ Services.tm.currentThread
+ );
+ }
+ };
+
+ // Implement an nsIOutputStreamCallback: write the string once possible,
+ // and then start running the following queue item, if any.
+ this.#outputQueue.push({
+ onOutputStreamReady: () => {
+ try {
+ const bytesWritten = this.#outputStream.write(str, str.length);
+
+ // remove this callback object from queue as it is now completed
+ this.#outputQueue.shift();
+
+ // request next wait if there is one
+ tryAsyncWait();
+
+ // finally resolve promise
+ resolve(bytesWritten);
+ } catch (err) {
+ // reject promise on error
+ reject(err);
+ }
+ },
+ });
+
+ // Length 1 imples that there is no in-flight asyncWait, so we may
+ // immediately follow through on this write.
+ if (this.#outputQueue.length === 1) {
+ tryAsyncWait();
+ }
+ });
+ }
+
+ /**
+ * Asynchronously read string from underlying socket and return it.
+ *
+ * When read is called, we create a new promise and queue it on the input
+ * queue. If it is the only element in the queue, we ask the input stream to
+ * run it immediately.
+ * Otherwise, the previous item of the queue will run it after it finishes.
+ *
+ * This function is expected to throw when the underlying socket has been
+ * closed.
+ *
+ * @returns {Promise<string>} The read string
+ */
+ async read() {
+ return new Promise((resolve, reject) => {
+ const tryAsyncWait = () => {
+ if (this.#inputQueue.length) {
+ this.#inputStream.asyncWait(
+ this.#inputQueue.at(0), // next input request
+ 0,
+ 0,
+ Services.tm.currentThread
+ );
+ }
+ };
+
+ this.#inputQueue.push({
+ onInputStreamReady: stream => {
+ try {
+ if (!this.#scriptableInputStream.available()) {
+ // This means EOF, but not closed yet. However, arriving at EOF
+ // should be an error condition for us, since we are in a socket,
+ // and EOF should mean peer disconnected.
+ // If the stream has been closed, this function itself should
+ // throw.
+ reject(
+ new Error("onInputStreamReady called without available bytes.")
+ );
+ return;
+ }
+
+ // Read our string from input stream.
+ const str = this.#scriptableInputStream.read(
+ this.#scriptableInputStream.available()
+ );
+
+ // Remove this callback object from queue now that we have read.
+ this.#inputQueue.shift();
+
+ // Start waiting for incoming data again if the reading queue is not
+ // empty.
+ tryAsyncWait();
+
+ // Finally resolve the promise.
+ resolve(str);
+ } catch (err) {
+ // E.g., we received a NS_BASE_STREAM_CLOSED because the socket was
+ // closed.
+ reject(err);
+ }
+ },
+ });
+
+ // Length 1 imples that there is no in-flight asyncWait, so we may
+ // immediately follow through on this read.
+ if (this.#inputQueue.length === 1) {
+ tryAsyncWait();
+ }
+ });
+ }
+
+ /**
+ * Close the streams.
+ */
+ close() {
+ this.#outputStream.close();
+ this.#inputStream.close();
+ }
+}
+
+/**
+ * @typedef Command
+ * @property {string} commandString The string to send over the control port
+ * @property {Function} resolve The function to resolve the promise with the
+ * response we got on the control port
+ * @property {Function} reject The function to reject the promise associated to
+ * the command
+ */
+
+class TorError extends Error {
+ constructor(command, reply) {
+ super(`${command} -> ${reply}`);
+ this.name = "TorError";
+ const info = reply.match(/(?<code>\d{3})(?:\s(?<message>.+))?/);
+ this.torStatusCode = info.groups.code;
+ if (info.groups.message) {
+ this.torMessage = info.groups.message;
+ }
+ }
+}
+
+class ControlSocket {
+ /**
+ * The socket to write to the control port.
+ *
+ * @type {AsyncSocket}
+ */
+ #socket;
+
+ /**
+ * The dispatcher used for the data we receive over the control port.
+ *
+ * @type {CallbackDispatcher}
+ */
+ #mainDispatcher = new CallbackDispatcher();
+ /**
+ * A secondary dispatcher used only to dispatch aynchronous events.
+ *
+ * @type {CallbackDispatcher}
+ */
+ #notificationDispatcher = new CallbackDispatcher();
+
+ /**
+ * Data we received on a read but that was not a complete line (missing a
+ * final CRLF). We will prepend it to the next read.
+ *
+ * @type {string}
+ */
+ #pendingData = "";
+ /**
+ * The lines we received and are still queued for being evaluated.
+ *
+ * @type {string[]}
+ */
+ #pendingLines = [];
+ /**
+ * The commands that need to be run or receive a response.
+ *
+ * @type {Command[]}
+ */
+ #commandQueue = [];
+
+ constructor(asyncSocket) {
+ this.#socket = asyncSocket;
+
+ // #mainDispatcher pushes only async notifications (650) to
+ // #notificationDispatcher
+ this.#mainDispatcher.addCallback(
+ /^650/,
+ this.#handleNotification.bind(this)
+ );
+ // callback for handling responses and errors
+ this.#mainDispatcher.addCallback(
+ /^[245]\d\d/,
+ this.#handleCommandReply.bind(this)
+ );
+
+ this.#startMessagePump();
+ }
+
+ /**
+ * Return the next line in the queue. If there is not any, block until one is
+ * read (or until a communication error happens, including the underlying
+ * socket being closed while it was still waiting for data).
+ * Any letfovers will be prepended to the next read.
+ *
+ * @returns {Promise<string>} A line read over the socket
+ */
+ async #readLine() {
+ // Keep reading from socket until we have at least a full line to return.
+ while (!this.#pendingLines.length) {
+ if (!this.#socket) {
+ throw new Error(
+ "Read interrupted because the control socket is not available anymore"
+ );
+ }
+ // Read data from our socket and split on newline tokens.
+ // This might still throw when the socket has been closed.
+ this.#pendingData += await this.#socket.read();
+ const lines = this.#pendingData.split("\r\n");
+ // The last line will either be empty string, or a partial read of a
+ // response/event so save it off for the next socket read.
+ this.#pendingData = lines.pop();
+ // Copy remaining full lines to our pendingLines list.
+ this.#pendingLines = this.#pendingLines.concat(lines);
+ }
+ return this.#pendingLines.shift();
+ }
+
+ /**
+ * Blocks until an entire message is ready and returns it.
+ * This function does a rudimentary parsing of the data only to handle
+ * multi-line responses.
+ *
+ * @returns {Promise<string>} The read message (without the final CRLF)
+ */
+ async #readMessage() {
+ // whether we are searching for the end of a multi-line values
+ // See control-spec section 3.9
+ let handlingMultlineValue = false;
+ let endOfMessageFound = false;
+ const message = [];
+
+ do {
+ const line = await this.#readLine();
+ message.push(line);
+
+ if (handlingMultlineValue) {
+ // look for end of multiline
+ if (line === ".") {
+ handlingMultlineValue = false;
+ }
+ } else {
+ // 'Multiline values' are possible. We avoid interrupting one by
+ // detecting it and waiting for a terminating "." on its own line.
+ // (See control-spec section 3.9 and
+ // https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/16990#n…)
+ // Ensure this is the first line of a new message
+ // eslint-disable-next-line no-lonely-if
+ if (message.length === 1 && line.match(/^\d\d\d\+.+?=$/)) {
+ handlingMultlineValue = true;
+ }
+ // look for end of message (notice the space character at end of the
+ // regex!)
+ else if (line.match(/^\d\d\d /)) {
+ if (message.length === 1) {
+ endOfMessageFound = true;
+ } else {
+ const firstReplyCode = message[0].substring(0, 3);
+ const lastReplyCode = line.substring(0, 3);
+ endOfMessageFound = firstReplyCode === lastReplyCode;
+ }
+ }
+ }
+ } while (!endOfMessageFound);
+
+ // join our lines back together to form one message
+ return message.join("\r\n");
+ }
+
+ /**
+ * Read messages on the socket and routed them to a dispatcher until the
+ * socket is open or some error happens (including the underlying socket being
+ * closed).
+ */
+ async #startMessagePump() {
+ try {
+ // This while is inside the try block because it is very likely that it
+ // will be broken by a NS_BASE_STREAM_CLOSED exception, rather than by its
+ // condition becoming false.
+ while (this.#socket) {
+ const message = await this.#readMessage();
+ // log("controlPort >> " + message);
+ this.#mainDispatcher.pushMessage(message);
+ }
+ } catch (err) {
+ try {
+ this.#close(err);
+ } catch (ec) {
+ console.error(
+ "Caught another error while closing the control socket.",
+ ec
+ );
+ }
+ }
+ }
+
+ /**
+ * Start running the first available command in the queue.
+ * To be called when the previous one has finished running.
+ * This makes sure to avoid conflicts when using the control port.
+ */
+ #writeNextCommand() {
+ const cmd = this.#commandQueue[0];
+ // log("controlPort << " + cmd.commandString);
+ this.#socket.write(`${cmd.commandString}\r\n`).catch(cmd.reject);
+ }
+
+ /**
+ * Send a command over the control port.
+ * This function returns only when it receives a complete message over the
+ * control port. This class does some rudimentary parsing to check wheter it
+ * needs to handle multi-line messages.
+ *
+ * @param {string} commandString
+ * @returns {Promise<string>} The message sent by the control port. It will
+ * always start with 2xx. In case of other codes the function will throw,
+ * instead. This means that the return value will never be an empty string
+ * (even though it will not include the final CRLF).
+ */
+ async sendCommand(commandString) {
+ if (!this.#socket) {
+ throw new Error("ControlSocket not open");
+ }
+
+ // this promise is resolved either in #handleCommandReply, or in
+ // #startMessagePump (on stream error)
+ return new Promise((resolve, reject) => {
+ const command = {
+ commandString,
+ resolve,
+ reject,
+ };
+ this.#commandQueue.push(command);
+ if (this.#commandQueue.length === 1) {
+ this.#writeNextCommand();
+ }
+ });
+ }
+
+ /**
+ * Handles a message starting with 2xx, 4xx, or 5xx.
+ * This function should be used only as a callback for the main dispatcher.
+ *
+ * @param {string} message The message to handle
+ */
+ #handleCommandReply(message) {
+ const cmd = this.#commandQueue.shift();
+ if (message[0] === "2") {
+ cmd.resolve(message);
+ } else if (message.match(/^[45]/)) {
+ cmd.reject(new TorError(cmd.commandString, message));
+ } else {
+ // This should never happen, as the dispatcher should filter the messages
+ // already.
+ cmd.reject(
+ new Error(`Received unexpected message:\n----\n${message}\n----`)
+ );
+ }
+
+ // send next command if one is available
+ if (this.#commandQueue.length) {
+ this.#writeNextCommand();
+ }
+ }
+
+ /**
+ * Re-route an event message to the notification dispatcher.
+ * This function should be used only as a callback for the main dispatcher.
+ *
+ * @param {string} message The message received on the control port
+ */
+ #handleNotification(message) {
+ try {
+ this.#notificationDispatcher.pushMessage(message);
+ } catch (e) {
+ console.error("An event watcher threw", e);
+ }
+ }
+
+ /**
+ * Reject all the commands that are still in queue and close the control
+ * socket.
+ *
+ * @param {object?} reason An error object used to pass a more specific
+ * rejection reason to the commands that are still queued.
+ */
+ #close(reason) {
+ const error = new Error(
+ "The control socket has been closed" +
+ (reason ? `: ${reason.message}` : "")
+ );
+ const commands = this.#commandQueue;
+ this.#commandQueue = [];
+ for (const cmd of commands) {
+ cmd.reject(error);
+ }
+ try {
+ this.#socket?.close();
+ } finally {
+ this.#socket = null;
+ }
+ }
+
+ /**
+ * Closes the socket connected to the control port.
+ */
+ close() {
+ this.#close(null);
+ }
+
+ /**
+ * Register an event watcher.
+ *
+ * @param {RegExp} regex The regex to filter on messages to receive
+ * @param {MessageCallback} callback The callback for the messages
+ */
+ addNotificationCallback(regex, callback) {
+ this.#notificationDispatcher.addCallback(regex, callback);
+ }
+
+ /**
+ * Tells whether the underlying socket is still open.
+ */
+ get isOpen() {
+ return !!this.#socket;
+ }
+}
+
+// ## utils
+// A namespace for utility functions
+let utils = {};
+
+// __utils.identity(x)__.
+// Returns its argument unchanged.
+utils.identity = function (x) {
+ return x;
+};
+
+// __utils.capture(string, regex)__.
+// Takes a string and returns an array of capture items, where regex must have a single
+// capturing group and use the suffix /.../g to specify a global search.
+utils.capture = function (string, regex) {
+ let matches = [];
+ // Special trick to use string.replace for capturing multiple matches.
+ string.replace(regex, function (a, captured) {
+ matches.push(captured);
+ });
+ return matches;
+};
+
+// __utils.extractor(regex)__.
+// Returns a function that takes a string and returns an array of regex matches. The
+// regex must use the suffix /.../g to specify a global search.
+utils.extractor = function (regex) {
+ return function (text) {
+ return utils.capture(text, regex);
+ };
+};
+
+// __utils.splitLines(string)__.
+// Splits a string into an array of strings, each corresponding to a line.
+utils.splitLines = function (string) {
+ return string.split(/\r?\n/);
+};
+
+// __utils.splitAtSpaces(string)__.
+// Splits a string into chunks between spaces. Does not split at spaces
+// inside pairs of quotation marks.
+utils.splitAtSpaces = utils.extractor(/((\S*?"(.*?)")+\S*|\S+)/g);
+
+// __utils.splitAtFirst(string, regex)__.
+// Splits a string at the first instance of regex match. If no match is
+// found, returns the whole string.
+utils.splitAtFirst = function (string, regex) {
+ let match = string.match(regex);
+ return match
+ ? [
+ string.substring(0, match.index),
+ string.substring(match.index + match[0].length),
+ ]
+ : string;
+};
+
+// __utils.splitAtEquals(string)__.
+// Splits a string into chunks between equals. Does not split at equals
+// inside pairs of quotation marks.
+utils.splitAtEquals = utils.extractor(/(([^=]*?"(.*?)")+[^=]*|[^=]+)/g);
+
+// __utils.mergeObjects(arrayOfObjects)__.
+// Takes an array of objects like [{"a":"b"},{"c":"d"}] and merges to a single object.
+// Pure function.
+utils.mergeObjects = function (arrayOfObjects) {
+ let result = {};
+ for (let obj of arrayOfObjects) {
+ for (let key in obj) {
+ result[key] = obj[key];
+ }
+ }
+ return result;
+};
+
+// __utils.listMapData(parameterString, listNames)__.
+// Takes a list of parameters separated by spaces, of which the first several are
+// unnamed, and the remainder are named, in the form `NAME=VALUE`. Apply listNames
+// to the unnamed parameters, and combine them in a map with the named parameters.
+// Example: `40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH`
+//
+// utils.listMapData("40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH",
+// ["streamID", "event", "circuitID", "IP"])
+// // --> {"streamID" : "40", "event" : "FAILED", "circuitID" : "0",
+// // "address" : "95.78.59.36:80", "REASON" : "CANT_ATTACH"}"
+utils.listMapData = function (parameterString, listNames) {
+ // Split out the space-delimited parameters.
+ let parameters = utils.splitAtSpaces(parameterString),
+ dataMap = {};
+ // Assign listNames to the first n = listNames.length parameters.
+ for (let i = 0; i < listNames.length; ++i) {
+ dataMap[listNames[i]] = parameters[i];
+ }
+ // Read key-value pairs and copy these to the dataMap.
+ for (let i = listNames.length; i < parameters.length; ++i) {
+ let [key, value] = utils.splitAtEquals(parameters[i]);
+ if (key && value) {
+ dataMap[key] = value;
+ }
+ }
+ return dataMap;
+};
+
+// ## info
+// A namespace for functions related to tor's GETINFO and GETCONF command.
+let info = {};
+
+// __info.keyValueStringsFromMessage(messageText)__.
+// Takes a message (text) response to GETINFO or GETCONF and provides
+// a series of key-value strings, which are either multiline (with a `250+` prefix):
+//
+// 250+config/defaults=
+// AccountingMax "0 bytes"
+// AllowDotExit "0"
+// .
+//
+// or single-line (with a `250-` or `250 ` prefix):
+//
+// 250-version=0.2.6.0-alpha-dev (git-b408125288ad6943)
+info.keyValueStringsFromMessage = utils.extractor(
+ /^(250\+[\s\S]+?^\.|250[- ].+?)$/gim
+);
+
+// __info.applyPerLine(transformFunction)__.
+// Returns a function that splits text into lines,
+// and applies transformFunction to each line.
+info.applyPerLine = function (transformFunction) {
+ return function (text) {
+ return utils.splitLines(text.trim()).map(transformFunction);
+ };
+};
+
+// __info.routerStatusParser(valueString)__.
+// Parses a router status entry as, described in
+// https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt
+// (search for "router status entry")
+info.routerStatusParser = function (valueString) {
+ let lines = utils.splitLines(valueString),
+ objects = [];
+ for (let line of lines) {
+ // Drop first character and grab data following it.
+ let myData = line.substring(2),
+ // Accumulate more maps with data, depending on the first character in the line.
+ dataFun = {
+ r: data =>
+ utils.listMapData(data, [
+ "nickname",
+ "identity",
+ "digest",
+ "publicationDate",
+ "publicationTime",
+ "IP",
+ "ORPort",
+ "DirPort",
+ ]),
+ a: data => ({ IPv6: data }),
+ s: data => ({ statusFlags: utils.splitAtSpaces(data) }),
+ v: data => ({ version: data }),
+ w: data => utils.listMapData(data, []),
+ p: data => ({ portList: data.split(",") }),
+ }[line.charAt(0)];
+ if (dataFun !== undefined) {
+ objects.push(dataFun(myData));
+ }
+ }
+ return utils.mergeObjects(objects);
+};
+
+// __info.circuitStatusParser(line)__.
+// Parse the output of a circuit status line.
+info.circuitStatusParser = function (line) {
+ let data = utils.listMapData(line, ["id", "status", "circuit"]),
+ circuit = data.circuit;
+ // Parse out the individual circuit IDs and names.
+ if (circuit) {
+ data.circuit = circuit.split(",").map(function (x) {
+ return x.split(/~|=/);
+ });
+ }
+ return data;
+};
+
+// __info.streamStatusParser(line)__.
+// Parse the output of a stream status line.
+info.streamStatusParser = function (text) {
+ return utils.listMapData(text, [
+ "StreamID",
+ "StreamStatus",
+ "CircuitID",
+ "Target",
+ ]);
+};
+
+// TODO: fix this parsing logic to handle bridgeLine correctly
+// fingerprint/id is an optional parameter
+// __info.bridgeParser(bridgeLine)__.
+// Takes a single line from a `getconf bridge` result and returns
+// a map containing the bridge's type, address, and ID.
+info.bridgeParser = function (bridgeLine) {
+ let result = {},
+ tokens = bridgeLine.split(/\s+/);
+ // First check if we have a "vanilla" bridge:
+ if (tokens[0].match(/^\d+\.\d+\.\d+\.\d+/)) {
+ result.type = "vanilla";
+ [result.address, result.ID] = tokens;
+ // Several bridge types have a similar format:
+ } else {
+ result.type = tokens[0];
+ if (
+ [
+ "flashproxy",
+ "fte",
+ "meek",
+ "meek_lite",
+ "obfs3",
+ "obfs4",
+ "scramblesuit",
+ "snowflake",
+ ].includes(result.type)
+ ) {
+ [result.address, result.ID] = tokens.slice(1);
+ }
+ }
+ return result.type ? result : null;
+};
+
+// __info.parsers__.
+// A map of GETINFO and GETCONF keys to parsing function, which convert
+// result strings to JavaScript data.
+info.parsers = {
+ "ns/id/": info.routerStatusParser,
+ "ip-to-country/": utils.identity,
+ "circuit-status": info.applyPerLine(info.circuitStatusParser),
+ bridge: info.bridgeParser,
+ // Currently unused parsers:
+ // "ns/name/" : info.routerStatusParser,
+ // "stream-status" : info.applyPerLine(info.streamStatusParser),
+ // "version" : utils.identity,
+ // "config-file" : utils.identity,
+};
+
+// __info.getParser(key)__.
+// Takes a key and determines the parser function that should be used to
+// convert its corresponding valueString to JavaScript data.
+info.getParser = function (key) {
+ return (
+ info.parsers[key] ||
+ info.parsers[key.substring(0, key.lastIndexOf("/") + 1)]
+ );
+};
+
+// __info.stringToValue(string)__.
+// Converts a key-value string as from GETINFO or GETCONF to a value.
+info.stringToValue = function (string) {
+ // key should look something like `250+circuit-status=` or `250-circuit-status=...`
+ // or `250 circuit-status=...`
+ let matchForKey = string.match(/^250[ +-](.+?)=/),
+ key = matchForKey ? matchForKey[1] : null;
+ if (key === null) {
+ return null;
+ }
+ // matchResult finds a single-line result for `250-` or `250 `,
+ // or a multi-line one for `250+`.
+ let matchResult =
+ string.match(/^250[ -].+?=(.*)$/) ||
+ string.match(/^250\+.+?=([\s\S]*?)^\.$/m),
+ // Retrieve the captured group (the text of the value in the key-value pair)
+ valueString = matchResult ? matchResult[1] : null,
+ // Get the parser function for the key found.
+ parse = info.getParser(key.toLowerCase());
+ if (parse === undefined) {
+ throw new Error("No parser found for '" + key + "'");
+ }
+ // Return value produced by the parser.
+ return parse(valueString);
+};
+
+/**
+ * @typedef {object} Bridge
+ * @property {string} transport The transport of the bridge, or vanilla if not
+ * specified.
+ * @property {string} addr The IP address and port of the bridge
+ * @property {string} id The fingerprint of the bridge
+ * @property {string} args Optional arguments passed to the bridge
+ */
+/**
+ * @typedef {object} PTInfo The information about a pluggable transport
+ * @property {string[]} transports An array with all the transports supported by
+ * this configuration.
+ * @property {string} type Either socks4, socks5 or exec
+ * @property {string} [ip] The IP address of the proxy (only for socks4 and
+ * socks5)
+ * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
+ * @property {string} [pathToBinary] Path to the binary that is run (only for
+ * exec)
+ * @property {string} [options] Optional options passed to the binary (only for
+ * exec)
+ */
+/**
+ * @typedef {object} OnionAuthKeyInfo
+ * @property {string} address The address of the onion service
+ * @property {string} typeAndKey Onion service key and type of key, as
+ * `type:base64-private-key`
+ * @property {string} Flags Additional flags, such as Permanent
+ */
+/**
+ * @callback EventFilterCallback
+ * @param {any} data Either a raw string, or already parsed data
+ * @returns {boolean}
+ */
+/**
+ * @callback EventCallback
+ * @param {any} data Either a raw string, or already parsed data
+ */
+
+class TorController {
+ /**
+ * The control socket
+ *
+ * @type {ControlSocket}
+ */
+ #socket;
+
+ /**
+ * A map of EVENT keys to parsing functions, which convert result strings to
+ * JavaScript data.
+ */
+ #eventParsers = {
+ stream: info.streamStatusParser,
+ // Currently unused:
+ // "circ" : info.circuitStatusParser,
+ };
+
+ /**
+ * Builds a new TorController.
+ *
+ * @param {AsyncSocket} socket The socket to communicate to the control port
+ */
+ constructor(socket) {
+ this.#socket = new ControlSocket(socket);
+ }
+
+ /**
+ * Tells whether the underlying socket is open.
+ *
+ * @returns {boolean}
+ */
+ get isOpen() {
+ return this.#socket.isOpen;
+ }
+
+ /**
+ * Close the underlying socket.
+ */
+ close() {
+ this.#socket.close();
+ }
+
+ /**
+ * Send a command over the control port.
+ * TODO: Make this function private, and force the operations to go through
+ * specialized methods.
+ *
+ * @param {string} cmd The command to send
+ * @returns {Promise<string>} A 2xx response obtained from the control port.
+ * For other codes, this function will throw. The returned string will never
+ * be empty.
+ */
+ async sendCommand(cmd) {
+ return this.#socket.sendCommand(cmd);
+ }
+
+ /**
+ * Send a simple command whose response is expected to be simply a "250 OK".
+ * The function will not return a reply, but will throw if an unexpected one
+ * is received.
+ *
+ * @param {string} command The command to send
+ */
+ async #sendCommandSimple(command) {
+ const reply = await this.sendCommand(command);
+ if (!/^250 OK\s*$/i.test(reply)) {
+ throw new TorError(command, reply);
+ }
+ }
+
+ /**
+ * Authenticate to the tor daemon.
+ * Notice that a failure in the authentication makes the connection close.
+ *
+ * @param {string} password The password for the control port.
+ */
+ async authenticate(password) {
+ if (password) {
+ this.#expectString(password, "password");
+ }
+ await this.#sendCommandSimple(`authenticate ${password || ""}`);
+ }
+
+ /**
+ * Sends a GETINFO for a single key.
+ *
+ * @param {string} key The key to get value for
+ * @returns {any} The return value depends on the requested key
+ */
+ async getInfo(key) {
+ this.#expectString(key, "key");
+ const response = await this.sendCommand(`getinfo ${key}`);
+ return this.#getMultipleResponseValues(response)[0];
+ }
+
+ /**
+ * Sends a GETINFO for a single key.
+ * control-spec.txt says "one ReplyLine is sent for each requested value", so,
+ * we expect to receive only one line starting with `250-keyword=`, or one
+ * line starting with `250+keyword=` (in which case we will match until a
+ * period).
+ * This function could be possibly extended to handle several keys at once,
+ * but we currently do not need this functionality, so we preferred keeping
+ * the function simpler.
+ *
+ * @param {string} key The key to get value for
+ * @returns {Promise<string>} The string we received (only the value, without
+ * the key). We do not do any additional parsing on it.
+ */
+ async #getInfo(key) {
+ this.#expectString(key);
+ const cmd = `GETINFO ${key}`;
+ const reply = await this.sendCommand(cmd);
+ const match =
+ reply.match(/^250-([^=]+)=(.*)$/m) ||
+ reply.match(/^250\+([^=]+)=([\s\S]*?)^\.\r?\n^250 OK\s*$/m);
+ if (!match || match[1] !== key) {
+ throw new TorError(cmd, reply);
+ }
+ return match[2];
+ }
+
+ /**
+ * Ask Tor its bootstrap phase.
+ *
+ * @returns {object} An object with the bootstrap information received from
+ * Tor. Its keys might vary, depending on the input
+ */
+ async getBootstrapPhase() {
+ return this.#parseBootstrapStatus(
+ await this.#getInfo("status/bootstrap-phase")
+ );
+ }
+
+ /**
+ * Get the IPv4 and optionally IPv6 addresses of an onion router.
+ *
+ * @param {NodeFingerprint} id The fingerprint of the node the caller is
+ * interested in
+ * @returns {string[]} The IP addresses (one IPv4 and optionally an IPv6)
+ */
+ async getNodeAddresses(id) {
+ this.#expectString(id, "id");
+ const reply = await this.#getInfo(`ns/id/${id}`);
+ // See dir-spec.txt.
+ // r nickname identity digest publication IP OrPort DirPort
+ const rLine = reply.match(/^r\s+(.*)$/m);
+ const v4 = rLine ? rLine[1].split(/\s+/) : [];
+ // Tor should already reply with a 552 when a relay cannot be found.
+ // Also, publication is a date with a space inside, so it is counted twice.
+ if (!rLine || v4.length !== 8) {
+ throw new Error(`Received an invalid node information: ${reply}`);
+ }
+ const addresses = [v4[5]];
+ // a address:port
+ // dir-spec.txt also states only the first one should be taken
+ // TODO: The consumers do not care about the port or the square brackets
+ // either. Remove them when integrating this function with the rest
+ const v6 = reply.match(/^a\s+(\[[0-9a-fA-F:]+\]:[0-9]{1,5})$/m);
+ if (v6) {
+ addresses.push(v6[1]);
+ }
+ return addresses;
+ }
+
+ /**
+ * Maps IP addresses to 2-letter country codes, or ?? if unknown.
+ *
+ * @param {string} ip The IP address to look for
+ * @returns {Promise<string>} A promise with the country code. If unknown, the
+ * promise is resolved with "??". It is rejected only when the underlying
+ * GETINFO command fails or if an exception is thrown
+ */
+ async getIPCountry(ip) {
+ this.#expectString(ip, "ip");
+ return this.#getInfo(`ip-to-country/${ip}`);
+ }
+
+ /**
+ * Ask tor which ports it is listening to for SOCKS connections.
+ *
+ * @returns {Promise<string[]>} An array of addresses. It might be empty
+ * (e.g., when DisableNetwork is set)
+ */
+ async getSocksListeners() {
+ const listeners = await this.#getInfo("net/listeners/socks");
+ return Array.from(listeners.matchAll(/\s*("(?:[^"\\]|\\.)*"|\S+)\s*/g), m =>
+ TorParsers.unescapeString(m[1])
+ );
+ }
+
+ // Configuration
+
+ /**
+ * Sends a GETCONF for a single key.
+ * GETCONF with a single argument returns results with one or more lines that
+ * look like `250[- ]key=value`.
+ * Any GETCONF lines that contain a single keyword only are currently dropped.
+ * So we can use similar parsing to that for getInfo.
+ *
+ * @param {string} key The key to get value for
+ * @returns {any} A parsed config value (it depends if a parser is known)
+ */
+ async getConf(key) {
+ this.#expectString(key, "key");
+ return this.#getMultipleResponseValues(
+ await this.sendCommand(`getconf ${key}`)
+ );
+ }
+
+ /**
+ * Sends a GETCONF for a single key.
+ * The function could be easily generalized to get multiple keys at once, but
+ * we do not need this functionality, at the moment.
+ *
+ * @param {string} key The keys to get info for
+ * @returns {Promise<string[]>} The values obtained from the control port.
+ * The key is removed, and the values unescaped, but they are not parsed.
+ * The array might contain an empty string, which means that the default value
+ * is used.
+ */
+ async #getConf(key) {
+ this.#expectString(key, "key");
+ // GETCONF expects a `keyword`, which should be only alpha characters,
+ // according to the definition in control-port.txt. But as a matter of fact,
+ // several configuration keys include numbers (e.g., Socks4Proxy). So, we
+ // accept also numbers in this regular expression. One of the reason to
+ // sanitize the input is that we then use it to create a regular expression.
+ // Sadly, JavaScript does not provide a function to escape/quote a string
+ // for inclusion in a regex. Should we remove this limitation, we should
+ // also implement a regex sanitizer, or switch to another pattern, like
+ // `([^=])` and then filter on the keyword.
+ if (!/^[A-Za-z0-9]+$/.test(key)) {
+ throw new Error("The key can be composed only of letters and numbers.");
+ }
+ const cmd = `GETCONF ${key}`;
+ const reply = await this.sendCommand(cmd);
+ // From control-spec.txt: a 'default' value semantically different from an
+ // empty string will not have an equal sign, just `250 $key`.
+ const defaultRe = new RegExp(`^250[-\\s]${key}$`, "gim");
+ if (reply.match(defaultRe)) {
+ return [];
+ }
+ const re = new RegExp(`^250[-\\s]${key}=(.*)$`, "gim");
+ const values = Array.from(reply.matchAll(re), m =>
+ TorParsers.unescapeString(m[1])
+ );
+ if (!values.length) {
+ throw new TorError(cmd, reply);
+ }
+ return values;
+ }
+
+ /**
+ * Get the bridges Tor has been configured with.
+ *
+ * @returns {Bridge[]} The configured bridges
+ */
+ async getBridges() {
+ return (await this.#getConf("BRIDGE")).map(TorParsers.parseBridgeLine);
+ }
+
+ /**
+ * Get the configured pluggable transports.
+ *
+ * @returns {PTInfo[]} An array with the info of all the configured pluggable
+ * transports.
+ */
+ async getPluggableTransports() {
+ return (await this.#getConf("ClientTransportPlugin")).map(ptLine => {
+ // man 1 tor: ClientTransportPlugin transport socks4|socks5 IP:PORT
+ const socksLine = ptLine.match(
+ /(\S+)\s+(socks[45])\s+([\d.]{7,15}|\[[\da-fA-F:]+\]):(\d{1,5})/i
+ );
+ // man 1 tor: transport exec path-to-binary [options]
+ const execLine = ptLine.match(
+ /(\S+)\s+(exec)\s+("(?:[^"\\]|\\.)*"|\S+)\s*(.*)/i
+ );
+ if (socksLine) {
+ return {
+ transports: socksLine[1].split(","),
+ type: socksLine[2].toLowerCase(),
+ ip: socksLine[3],
+ port: parseInt(socksLine[4], 10),
+ };
+ } else if (execLine) {
+ return {
+ transports: execLine[1].split(","),
+ type: execLine[2].toLowerCase(),
+ pathToBinary: TorParsers.unescapeString(execLine[3]),
+ options: execLine[4],
+ };
+ }
+ throw new Error(
+ `Received an invalid ClientTransportPlugin line: ${ptLine}`
+ );
+ });
+ }
+
+ /**
+ * Send multiple configuration values to tor.
+ *
+ * @param {object} values The values to set
+ */
+ async setConf(values) {
+ const args = Object.entries(values)
+ .flatMap(([key, value]) => {
+ if (value === undefined || value === null) {
+ return [key];
+ }
+ if (Array.isArray(value)) {
+ return value.length
+ ? value.map(v => `${key}=${TorParsers.escapeString(v)}`)
+ : key;
+ } else if (typeof value === "string" || value instanceof String) {
+ return `${key}=${TorParsers.escapeString(value)}`;
+ } else if (typeof value === "boolean") {
+ return `${key}=${value ? "1" : "0"}`;
+ } else if (typeof value === "number") {
+ return `${key}=${value}`;
+ }
+ throw new Error(`Unsupported type ${typeof value} (key ${key})`);
+ })
+ .join(" ");
+ return this.#sendCommandSimple(`SETCONF ${args}`);
+ }
+
+ /**
+ * Enable or disable the network.
+ * Notice: switching from network disabled to network enabled will trigger a
+ * bootstrap on C tor! (Or stop the current one).
+ *
+ * @param {boolean} enabled Tell whether the network should be enabled
+ */
+ async setNetworkEnabled(enabled) {
+ return this.setConf({ DisableNetwork: !enabled });
+ }
+
+ /**
+ * Ask Tor to write out its config options into its torrc.
+ */
+ async flushSettings() {
+ return this.#sendCommandSimple("SAVECONF");
+ }
+
+ // Onion service authentication
+
+ /**
+ * Sends a ONION_CLIENT_AUTH_VIEW command to retrieve the list of private
+ * keys.
+ *
+ * @returns {OnionAuthKeyInfo[]}
+ */
+ async onionAuthViewKeys() {
+ const cmd = "onion_client_auth_view";
+ const message = await this.sendCommand(cmd);
+ // Either `250-CLIENT`, or `250 OK` if no keys are available.
+ if (!message.startsWith("250")) {
+ throw new TorError(cmd, message);
+ }
+ const re =
+ /^250-CLIENT\s+(?<HSAddress>[A-Za-z2-7]+)\s+(?<KeyType>[^:]+):(?<PrivateKeyBlob>\S+)(?:\s(?<other>.+))?$/gim;
+ return Array.from(message.matchAll(re), match => {
+ // TODO: Change the consumer and make the fields more consistent with what
+ // we get (e.g., separate key and type, and use a boolen for permanent).
+ const info = {
+ hsAddress: match.groups.HSAddress,
+ typeAndKey: `${match.groups.KeyType}:${match.groups.PrivateKeyBlob}`,
+ };
+ const maybeFlags = match.groups.other?.match(/Flags=(\S+)/);
+ if (maybeFlags) {
+ info.Flags = maybeFlags[1];
+ }
+ return info;
+ });
+ }
+
+ /**
+ * Sends an ONION_CLIENT_AUTH_ADD command to add a private key to the Tor
+ * configuration.
+ *
+ * @param {string} address The address of the onion service
+ * @param {string} b64PrivateKey The private key of the service, in base64
+ * @param {boolean} isPermanent Tell whether the key should be saved forever
+ */
+ async onionAuthAdd(address, b64PrivateKey, isPermanent) {
+ this.#expectString(address, "address");
+ this.#expectString(b64PrivateKey, "b64PrivateKey");
+ const keyType = "x25519";
+ let cmd = `onion_client_auth_add ${address} ${keyType}:${b64PrivateKey}`;
+ if (isPermanent) {
+ cmd += " Flags=Permanent";
+ }
+ const reply = await this.sendCommand(cmd);
+ const status = reply.substring(0, 3);
+ if (status !== "250" && status !== "251" && status !== "252") {
+ throw new TorError(cmd, reply);
+ }
+ }
+
+ /**
+ * Sends an ONION_CLIENT_AUTH_REMOVE command to remove a private key from the
+ * Tor configuration.
+ *
+ * @param {string} address The address of the onion service
+ */
+ async onionAuthRemove(address) {
+ this.#expectString(address, "address");
+ const cmd = `onion_client_auth_remove ${address}`;
+ const reply = await this.sendCommand(cmd);
+ const status = reply.substring(0, 3);
+ if (status !== "250" && status !== "251") {
+ throw new TorError(cmd, reply);
+ }
+ }
+
+ // Daemon ownership
+
+ /**
+ * Instructs Tor to shut down when this control connection is closed.
+ * If multiple connection sends this request, Tor will shut dwon when any of
+ * them is closed.
+ */
+ async takeOwnership() {
+ return this.#sendCommandSimple("TAKEOWNERSHIP");
+ }
+
+ /**
+ * The __OwningControllerProcess argument can be used to make Tor periodically
+ * check if a certain PID is still present, or terminate itself otherwise.
+ * When switching to the ownership tied to the control port, this mechanism
+ * should be stopped by calling this function.
+ */
+ async resetOwningControllerProcess() {
+ return this.#sendCommandSimple("RESETCONF __OwningControllerProcess");
+ }
+
+ // Signals
+
+ /**
+ * Ask Tor to swtich to new circuits and clear the DNS cache.
+ */
+ async newnym() {
+ return this.#sendCommandSimple("SIGNAL NEWNYM");
+ }
+
+ // Events monitoring
+
+ /**
+ * Enable receiving certain events.
+ * As per control-spec.txt, any events turned on in previous calls but not
+ * included in this one will be turned off.
+ *
+ * @param {string[]} types The events to enable. If empty, no events will be
+ * watched.
+ */
+ setEvents(types) {
+ if (!types.every(t => typeof t === "string" || t instanceof String)) {
+ throw new Error("Event types must be strings");
+ }
+ return this.#sendCommandSimple("SETEVENTS " + types.join(" "));
+ }
+
+ /**
+ * Watches for a particular type of asynchronous event.
+ * Notice: we only observe `"650" SP...` events, currently (no `650+...` or
+ * `650-...` events).
+ * Also, you need to enable the events in the control port with SETEVENTS,
+ * first.
+ *
+ * @param {string} type The event type to catch
+ * @param {EventFilterCallback?} filter An optional callback to filter
+ * events for which the callback will be called. If null, all events will be
+ * passed.
+ * @param {EventCallback} callback The callback that will handle the event
+ * @param {boolean} raw Tell whether to ignore the data parser, even if
+ * supported
+ */
+ watchEvent(type, filter, callback, raw = false) {
+ this.#expectString(type, "type");
+ const start = `650 ${type}`;
+ this.#socket.addNotificationCallback(new RegExp(`^${start}`), message => {
+ // Remove also the initial text
+ const dataText = message.substring(start.length + 1);
+ const parser = this.#eventParsers[type.toLowerCase()];
+ const data = dataText && parser ? parser(dataText) : null;
+ // FIXME: This is the original code, but we risk of not filtering on the
+ // data, if we ask for raw data (which we always do at the moment, but we
+ // do not use a filter either...)
+ if (filter === null || filter(data)) {
+ callback(data && !raw ? data : message);
+ }
+ });
+ }
+
+ // Other helpers
+
+ /**
+ * Parse a bootstrap status line.
+ *
+ * @param {string} line The line to parse, without the command/notification
+ * prefix
+ * @returns {object} An object with the bootstrap information received from
+ * Tor. Its keys might vary, depending on the input
+ */
+ #parseBootstrapStatus(line) {
+ const match = line.match(/^(NOTICE|WARN) BOOTSTRAP\s*(.*)/);
+ if (!match) {
+ throw Error(
+ `Received an invalid response for the bootstrap phase: ${line}`
+ );
+ }
+ const status = {
+ TYPE: match[1],
+ ...this.#getKeyValues(match[2]),
+ };
+ if (status.PROGRESS !== undefined) {
+ status.PROGRESS = parseInt(status.PROGRESS, 10);
+ }
+ if (status.COUNT !== undefined) {
+ status.COUNT = parseInt(status.COUNT, 10);
+ }
+ return status;
+ }
+
+ /**
+ * Throw an exception when value is not a string.
+ *
+ * @param {any} value The value to check
+ * @param {string} name The name of the `value` argument
+ */
+ #expectString(value, name) {
+ if (typeof value !== "string" && !(value instanceof String)) {
+ throw new Error(`The ${name} argument is expected to be a string.`);
+ }
+ }
+
+ /**
+ * Return an object with all the matches that are in the form `key="value"` or
+ * `key=value`. The values will be unescaped, but no additional parsing will
+ * be done (e.g., numbers will be returned as strings).
+ * If keys are repeated, only the last one will be taken.
+ *
+ * @param {string} str The string to match tokens in
+ * @returns {object} An object with all the various tokens. If none is found,
+ * an empty object is returned.
+ */
+ #getKeyValues(str) {
+ return Object.fromEntries(
+ Array.from(
+ str.matchAll(/\s*([^=]+)=("(?:[^"\\]|\\.)*"|\S+)\s*/g) || [],
+ pair => [pair[1], TorParsers.unescapeString(pair[2])]
+ )
+ );
+ }
+
+ /**
+ * Process multiple responses to a GETINFO or GETCONF request.
+ *
+ * @param {string} message The message to process
+ * @returns {object[]} The keys depend on the message
+ */
+ #getMultipleResponseValues(message) {
+ return info
+ .keyValueStringsFromMessage(message)
+ .map(info.stringToValue)
+ .filter(x => x);
+ }
+}
+
+const controlPortInfo = {};
+
+/**
+ * Sets Tor control port connection parameters to be used in future calls to
+ * the controller() function.
+ *
+ * Example:
+ * configureControlPortModule(undefined, "127.0.0.1", 9151, "MyPassw0rd");
+ *
+ * @param {nsIFile?} ipcFile An optional file to use to communicate to the
+ * control port on Unix platforms
+ * @param {string?} host The hostname to connect to the control port. Mutually
+ * exclusive with ipcFile
+ * @param {integer?} port The port number of the control port. To be used only
+ * with host. The default is 9151.
+ * @param {string} password The password of the control port in clear text.
+ */
+export function configureControlPortModule(ipcFile, host, port, password) {
+ controlPortInfo.ipcFile = ipcFile;
+ controlPortInfo.host = host;
+ controlPortInfo.port = port || 9151;
+ controlPortInfo.password = password;
+}
+
+/**
+ * Instantiates and returns a controller object that is connected and
+ * authenticated to a Tor ControlPort using the connection parameters
+ * provided in the most recent call to configureControlPortModule().
+ *
+ * Example:
+ * // Get a new controller
+ * let c = await controller();
+ * // Send command and receive a `250` reply or an error message:
+ * let replyPromise = await c.getInfo("ip-to-country/16.16.16.16");
+ * // Close the controller permanently
+ * c.close();
+ */
+export async function controller() {
+ if (!controlPortInfo.ipcFile && !controlPortInfo.host) {
+ throw new Error("Please call configureControlPortModule first");
+ }
+ let socket;
+ if (controlPortInfo.ipcFile) {
+ socket = AsyncSocket.fromIpcFile(controlPortInfo.ipcFile);
+ } else {
+ socket = AsyncSocket.fromSocketAddress(
+ controlPortInfo.host,
+ controlPortInfo.port
+ );
+ }
+ const controller = new TorController(socket);
+ try {
+ await controller.authenticate(controlPortInfo.password);
+ } catch (e) {
+ try {
+ controller.close();
+ } catch (ec) {
+ // TODO: Use a custom logger?
+ console.error("Cannot close the socket", ec);
+ }
+ throw e;
+ }
+ return controller;
+}
=====================================
toolkit/components/tor-launcher/TorMonitorService.sys.mjs
=====================================
@@ -15,14 +15,9 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
+ controller: "resource://gre/modules/TorControlPort.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "controller",
- "resource://torbutton/modules/tor-control-port.js"
-);
-
ChromeUtils.defineESModuleGetters(lazy, {
TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
});
@@ -172,9 +167,7 @@ export const TorMonitorService = {
const cmd = "GETINFO";
const key = "status/bootstrap-phase";
let reply = await this._connection.sendCommand(`${cmd} ${key}`);
- if (!reply) {
- throw new Error("We received an empty reply");
- }
+
// A typical reply looks like:
// 250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"
// 250 OK
@@ -335,8 +328,7 @@ export const TorMonitorService = {
let conn;
try {
- const avoidCache = true;
- conn = await lazy.controller(avoidCache);
+ conn = await lazy.controller();
} catch (e) {
logger.error("Cannot open a control port connection", e);
if (conn) {
@@ -353,12 +345,10 @@ export const TorMonitorService = {
}
// TODO: optionally monitor INFO and DEBUG log messages.
- let reply = await conn.sendCommand(
- "SETEVENTS " + Array.from(this._eventHandlers.keys()).join(" ")
- );
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- logger.error("SETEVENTS failed");
+ try {
+ await conn.setEvents(Array.from(this._eventHandlers.keys()));
+ } catch (e) {
+ logger.error("SETEVENTS failed", e);
conn.close();
return false;
}
@@ -405,18 +395,16 @@ export const TorMonitorService = {
// Try to become the primary controller (TAKEOWNERSHIP).
async _takeTorOwnership(conn) {
- const takeOwnership = "TAKEOWNERSHIP";
- let reply = await conn.sendCommand(takeOwnership);
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- logger.warn("Take ownership failed");
- } else {
- const resetConf = "RESETCONF __OwningControllerProcess";
- reply = await conn.sendCommand(resetConf);
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- logger.warn("Clear owning controller process failed");
- }
+ try {
+ conn.takeOwnership();
+ } catch (e) {
+ logger.warn("Take ownership failed", e);
+ return;
+ }
+ try {
+ conn.resetOwningControllerProcess();
+ } catch (e) {
+ logger.warn("Clear owning controller process failed", e);
}
},
=====================================
toolkit/components/tor-launcher/TorProtocolService.sys.mjs
=====================================
@@ -19,16 +19,10 @@ ChromeUtils.defineModuleGetter(
"TorMonitorService",
"resource://gre/modules/TorMonitorService.jsm"
);
-ChromeUtils.defineModuleGetter(
- lazy,
- "configureControlPortModule",
- "resource://torbutton/modules/tor-control-port.js"
-);
-ChromeUtils.defineModuleGetter(
- lazy,
- "controller",
- "resource://torbutton/modules/tor-control-port.js"
-);
+ChromeUtils.defineESModuleGetters(lazy, {
+ controller: "resource://gre/modules/TorControlPort.sys.mjs",
+ configureControlPortModule: "resource://gre/modules/TorControlPort.sys.mjs",
+});
const TorTopics = Object.freeze({
ProcessExited: "TorProcessExited",
@@ -285,8 +279,7 @@ export const TorProtocolService = {
});
},
- // TODO: transform the following 4 functions in getters. At the moment they
- // are also used in torbutton.
+ // TODO: transform the following 4 functions in getters.
// Returns Tor password string or null if an error occurs.
torGetPassword() {
@@ -490,8 +483,6 @@ export const TorProtocolService = {
TorLauncherUtil.setProxyConfiguration(this._SOCKSPortInfo);
// Set the global control port info parameters.
- // These values may be overwritten by torbutton when it initializes, but
- // torbutton's values *should* be identical.
lazy.configureControlPortModule(
this._controlIPCFile,
this._controlHost,
@@ -616,8 +607,7 @@ export const TorProtocolService = {
// return it.
async _getConnection() {
if (!this._controlConnection) {
- const avoidCache = true;
- this._controlConnection = await lazy.controller(avoidCache);
+ this._controlConnection = await lazy.controller();
}
if (this._controlConnection.inUse) {
await new Promise((resolve, reject) =>
=====================================
toolkit/components/tor-launcher/moz.build
=====================================
@@ -1,5 +1,6 @@
EXTRA_JS_MODULES += [
"TorBootstrapRequest.sys.mjs",
+ "TorControlPort.sys.mjs",
"TorDomainIsolator.sys.mjs",
"TorLauncherUtil.sys.mjs",
"TorMonitorService.sys.mjs",
=====================================
toolkit/torbutton/chrome/content/torbutton.js deleted
=====================================
@@ -1,148 +0,0 @@
-// window globals
-var torbutton_init;
-
-(() => {
- // Bug 1506 P1-P5: This is the main Torbutton overlay file. Much needs to be
- // preserved here, but in an ideal world, most of this code should perhaps be
- // moved into an XPCOM service, and much can also be tossed. See also
- // individual 1506 comments for details.
-
- // TODO: check for leaks: http://www.mozilla.org/scriptable/avoiding-leaks.html
- // TODO: Double-check there are no strange exploits to defeat:
- // http://kb.mozillazine.org/Links_to_local_pages_don%27t_work
-
- /* global gBrowser, Services, AppConstants */
-
- let { torbutton_log } = ChromeUtils.import(
- "resource://torbutton/modules/utils.js"
- );
- let { configureControlPortModule } = ChromeUtils.import(
- "resource://torbutton/modules/tor-control-port.js"
- );
-
- const { TorProtocolService } = ChromeUtils.import(
- "resource://gre/modules/TorProtocolService.jsm"
- );
-
- var m_tb_prefs = Services.prefs;
-
- // status
- var m_tb_wasinited = false;
-
- var m_tb_control_ipc_file = null; // Set if using IPC (UNIX domain socket).
- var m_tb_control_port = null; // Set if using TCP.
- var m_tb_control_host = null; // Set if using TCP.
- var m_tb_control_pass = null;
-
- // Bug 1506 P2-P4: This code sets some version variables that are irrelevant.
- // It does read out some important environment variables, though. It is
- // called once per browser window.. This might belong in a component.
- torbutton_init = function () {
- torbutton_log(3, "called init()");
-
- if (m_tb_wasinited) {
- return;
- }
- m_tb_wasinited = true;
-
- // Bug 1506 P4: These vars are very important for New Identity
- if (Services.env.exists("TOR_CONTROL_PASSWD")) {
- m_tb_control_pass = Services.env.get("TOR_CONTROL_PASSWD");
- } else if (Services.env.exists("TOR_CONTROL_COOKIE_AUTH_FILE")) {
- var cookie_path = Services.env.get("TOR_CONTROL_COOKIE_AUTH_FILE");
- try {
- if ("" != cookie_path) {
- m_tb_control_pass = torbutton_read_authentication_cookie(cookie_path);
- }
- } catch (e) {
- torbutton_log(4, "unable to read authentication cookie");
- }
- } else {
- try {
- // Try to get password from Tor Launcher.
- m_tb_control_pass = TorProtocolService.torGetPassword();
- } catch (e) {}
- }
-
- // Try to get the control port IPC file (an nsIFile) from Tor Launcher,
- // since Tor Launcher knows how to handle its own preferences and how to
- // resolve relative paths.
- try {
- m_tb_control_ipc_file = TorProtocolService.torGetControlIPCFile();
- } catch (e) {}
-
- if (!m_tb_control_ipc_file) {
- if (Services.env.exists("TOR_CONTROL_PORT")) {
- m_tb_control_port = Services.env.get("TOR_CONTROL_PORT");
- } else {
- try {
- const kTLControlPortPref = "extensions.torlauncher.control_port";
- m_tb_control_port = m_tb_prefs.getIntPref(kTLControlPortPref);
- } catch (e) {
- // Since we want to disable some features when Tor Launcher is
- // not installed (e.g., New Identity), we do not set a default
- // port value here.
- }
- }
-
- if (Services.env.exists("TOR_CONTROL_HOST")) {
- m_tb_control_host = Services.env.get("TOR_CONTROL_HOST");
- } else {
- try {
- const kTLControlHostPref = "extensions.torlauncher.control_host";
- m_tb_control_host = m_tb_prefs.getCharPref(kTLControlHostPref);
- } catch (e) {
- m_tb_control_host = "127.0.0.1";
- }
- }
- }
-
- configureControlPortModule(
- m_tb_control_ipc_file,
- m_tb_control_host,
- m_tb_control_port,
- m_tb_control_pass
- );
-
- torbutton_log(3, "init completed");
- };
-
- // Bug 1506 P4: Control port interaction. Needed for New Identity.
- function torbutton_read_authentication_cookie(path) {
- var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
- file.initWithPath(path);
- var fileStream = Cc[
- "@mozilla.org/network/file-input-stream;1"
- ].createInstance(Ci.nsIFileInputStream);
- fileStream.init(file, 1, 0, false);
- var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
- Ci.nsIBinaryInputStream
- );
- binaryStream.setInputStream(fileStream);
- var array = binaryStream.readByteArray(fileStream.available());
- binaryStream.close();
- fileStream.close();
- return torbutton_array_to_hexdigits(array);
- }
-
- // Bug 1506 P4: Control port interaction. Needed for New Identity.
- function torbutton_array_to_hexdigits(array) {
- return array
- .map(function (c) {
- return String("0" + c.toString(16)).slice(-2);
- })
- .join("");
- }
-
- // ---------------------- Event handlers -----------------
-
- // Bug 1506 P3: This is needed pretty much only for the window resizing.
- // See comments for individual functions for details
- function torbutton_new_window(event) {
- torbutton_log(3, "New window");
- if (!m_tb_wasinited) {
- torbutton_init();
- }
- }
- window.addEventListener("load", torbutton_new_window);
-})();
=====================================
toolkit/torbutton/components.conf deleted
=====================================
@@ -1,10 +0,0 @@
-Classes = [
- {
- "cid": "{f36d72c9-9718-4134-b550-e109638331d7}",
- "contract_ids": [
- "@torproject.org/torbutton-logger;1"
- ],
- "jsm": "resource://torbutton/modules/TorbuttonLogger.jsm",
- "constructor": "TorbuttonLogger",
- },
-]
=====================================
toolkit/torbutton/jar.mn
=====================================
@@ -1,19 +1,12 @@
#filter substitution
torbutton.jar:
-
-% content torbutton %content/
-
- content/torbutton.js (chrome/content/torbutton.js)
-
- modules/ (modules/*)
-
% resource torbutton %
+% category l10n-registry torbutton resource://torbutton/locale/{locale}/
# browser branding
% override chrome://branding/locale/brand.dtd chrome://torbutton/locale/brand.dtd
% override chrome://branding/locale/brand.properties chrome://torbutton/locale/brand.properties
-% category l10n-registry torbutton resource://torbutton/locale/{locale}/
# Strings for the about:tbupdate page
% override chrome://browser/locale/aboutTBUpdate.dtd chrome://torbutton/locale/aboutTBUpdate.dtd
=====================================
toolkit/torbutton/modules/TorbuttonLogger.jsm deleted
=====================================
@@ -1,147 +0,0 @@
-// Bug 1506 P1: This is just a handy logger. If you have a better one, toss
-// this in the trash.
-
-/*************************************************************************
- * TBLogger (JavaScript XPCOM component)
- *
- * Allows loglevel-based logging to different logging mechanisms.
- *
- *************************************************************************/
-
-var EXPORTED_SYMBOLS = ["TorbuttonLogger"];
-
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-function TorbuttonLogger() {
- // Register observer
- Services.prefs.addObserver("extensions.torbutton", this);
-
- this.loglevel = Services.prefs.getIntPref("extensions.torbutton.loglevel", 4);
- this.logmethod = Services.prefs.getIntPref(
- "extensions.torbutton.logmethod",
- 1
- );
-
- try {
- var logMngr = Cc["@mozmonkey.com/debuglogger/manager;1"].getService(
- Ci.nsIDebugLoggerManager
- );
- this._debuglog = logMngr.registerLogger("torbutton");
- } catch (exErr) {
- this._debuglog = false;
- }
- this._console = Services.console;
-
- // This JSObject is exported directly to chrome
- this.wrappedJSObject = this;
- this.log(3, "Torbutton debug output ready");
-}
-
-/**
- * JS XPCOM component registration goop:
- *
- * Everything below is boring boilerplate and can probably be ignored.
- */
-
-TorbuttonLogger.prototype = {
- QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo]),
-
- wrappedJSObject: null, // Initialized by constructor
-
- formatLog(str, level) {
- const padInt = n => String(n).padStart(2, "0");
- const logString = { 1: "VERB", 2: "DBUG", 3: "INFO", 4: "NOTE", 5: "WARN" };
- const d = new Date();
- const now =
- padInt(d.getUTCMonth() + 1) +
- "-" +
- padInt(d.getUTCDate()) +
- " " +
- padInt(d.getUTCHours()) +
- ":" +
- padInt(d.getUTCMinutes()) +
- ":" +
- padInt(d.getUTCSeconds());
- return `${now} Torbutton ${logString[level]}: ${str}`;
- },
-
- // error console log
- eclog(level, str) {
- switch (this.logmethod) {
- case 0: // stderr
- if (this.loglevel <= level) {
- dump(this.formatLog(str, level) + "\n");
- }
- break;
- default:
- // errorconsole
- if (this.loglevel <= level) {
- this._console.logStringMessage(this.formatLog(str, level));
- }
- break;
- }
- },
-
- safe_log(level, str, scrub) {
- if (this.loglevel < 4) {
- this.eclog(level, str + scrub);
- } else {
- this.eclog(level, str + " [scrubbed]");
- }
- },
-
- log(level, str) {
- switch (this.logmethod) {
- case 2: // debuglogger
- if (this._debuglog) {
- this._debuglog.log(6 - level, this.formatLog(str, level));
- break;
- }
- // fallthrough
- case 0: // stderr
- if (this.loglevel <= level) {
- dump(this.formatLog(str, level) + "\n");
- }
- break;
- case 1: // errorconsole
- if (this.loglevel <= level) {
- this._console.logStringMessage(this.formatLog(str, level));
- }
- break;
- default:
- dump("Bad log method: " + this.logmethod);
- }
- },
-
- // Pref observer interface implementation
-
- // topic: what event occurred
- // subject: what nsIPrefBranch we're observing
- // data: which pref has been changed (relative to subject)
- observe(subject, topic, data) {
- if (topic != "nsPref:changed") {
- return;
- }
- switch (data) {
- case "extensions.torbutton.logmethod":
- this.logmethod = Services.prefs.getIntPref(
- "extensions.torbutton.logmethod"
- );
- if (this.logmethod === 0) {
- Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true);
- } else if (
- Services.prefs.getIntPref("extensions.torlauncher.logmethod", 3) !== 0
- ) {
- // If Tor Launcher is not available or its log method is not 0
- // then let's reset the dump pref.
- Services.prefs.setBoolPref("browser.dom.window.dump.enabled", false);
- }
- break;
- case "extensions.torbutton.loglevel":
- this.loglevel = Services.prefs.getIntPref(
- "extensions.torbutton.loglevel"
- );
- break;
- }
- },
-};
=====================================
toolkit/torbutton/modules/tor-control-port.js deleted
=====================================
@@ -1,1002 +0,0 @@
-// A module for TorBrowser that provides an asynchronous controller for
-// Tor, through its ControlPort.
-//
-// This file is written in call stack order (later functions
-// call earlier functions). The file can be processed
-// with docco.js to produce pretty documentation.
-//
-// To import the module, use
-//
-// let { configureControlPortModule, controller, wait_for_controller } =
-// Components.utils.import("path/to/tor-control-port.js", {});
-//
-// See the third-to-last function defined in this file:
-// configureControlPortModule(ipcFile, host, port, password)
-// for usage of the configureControlPortModule function.
-//
-// See the last functions defined in this file:
-// controller(avoidCache), wait_for_controller(avoidCache)
-// for usage of the controller functions.
-
-/* jshint esnext: true */
-/* jshint -W097 */
-/* global console */
-"use strict";
-
-const { XPCOMUtils } = ChromeUtils.importESModule(
- "resource://gre/modules/XPCOMUtils.sys.mjs"
-);
-
-ChromeUtils.defineModuleGetter(
- this,
- "TorMonitorService",
- "resource://gre/modules/TorMonitorService.jsm"
-);
-
-XPCOMUtils.defineLazyServiceGetter(
- this,
- "logger",
- "@torproject.org/torbutton-logger;1",
- "nsISupports"
-);
-
-// tor-launcher observer topics
-const TorTopics = Object.freeze({
- ProcessIsReady: "TorProcessIsReady",
-});
-
-// __log__.
-// Logging function
-let log = x =>
- logger.wrappedJSObject.eclog(3, x.trimRight().replace(/\r\n/g, "\n"));
-
-// ### announce this file
-log("Loading tor-control-port.js\n");
-
-class AsyncSocket {
- constructor(ipcFile, host, port) {
- let sts = Cc["@mozilla.org/network/socket-transport-service;1"].getService(
- Ci.nsISocketTransportService
- );
- const OPEN_UNBUFFERED = Ci.nsITransport.OPEN_UNBUFFERED;
-
- let socketTransport = ipcFile
- ? sts.createUnixDomainTransport(ipcFile)
- : sts.createTransport([], host, port, null, null);
-
- this.outputStream = socketTransport
- .openOutputStream(OPEN_UNBUFFERED, 1, 1)
- .QueryInterface(Ci.nsIAsyncOutputStream);
- this.outputQueue = [];
-
- this.inputStream = socketTransport
- .openInputStream(OPEN_UNBUFFERED, 1, 1)
- .QueryInterface(Ci.nsIAsyncInputStream);
- this.scriptableInputStream = Cc[
- "@mozilla.org/scriptableinputstream;1"
- ].createInstance(Ci.nsIScriptableInputStream);
- this.scriptableInputStream.init(this.inputStream);
- this.inputQueue = [];
- }
-
- // asynchronously write string to underlying socket and return number of bytes written
- async write(str) {
- return new Promise((resolve, reject) => {
- // asyncWait next write request
- const tryAsyncWait = () => {
- if (this.outputQueue.length) {
- this.outputStream.asyncWait(
- this.outputQueue.at(0), // next request
- 0,
- 0,
- Services.tm.currentThread
- );
- }
- };
-
- // output stream can only have 1 registered callback at a time, so multiple writes
- // need to be queued up (see nsIAsyncOutputStream.idl)
- this.outputQueue.push({
- // Implement an nsIOutputStreamCallback:
- onOutputStreamReady: () => {
- try {
- let bytesWritten = this.outputStream.write(str, str.length);
-
- // remove this callback object from queue as it is now completed
- this.outputQueue.shift();
-
- // request next wait if there is one
- tryAsyncWait();
-
- // finally resolve promise
- resolve(bytesWritten);
- } catch (err) {
- // reject promise on error
- reject(err);
- }
- },
- });
-
- // length 1 imples that there is no in-flight asyncWait, so we may immediately
- // follow through on this write
- if (this.outputQueue.length == 1) {
- tryAsyncWait();
- }
- });
- }
-
- // asynchronously read string from underlying socket and return it
- async read() {
- return new Promise((resolve, reject) => {
- const tryAsyncWait = () => {
- if (this.inputQueue.length) {
- this.inputStream.asyncWait(
- this.inputQueue.at(0), // next input request
- 0,
- 0,
- Services.tm.currentThread
- );
- }
- };
-
- this.inputQueue.push({
- onInputStreamReady: stream => {
- try {
- if (!this.scriptableInputStream.available()) {
- // This means EOF, but not closed yet. However, arriving at EOF
- // should be an error condition for us, since we are in a socket,
- // and EOF should mean peer disconnected.
- // If the stream has been closed, this function itself should
- // throw.
- reject(
- new Error("onInputStreamReady called without available bytes.")
- );
- return;
- }
-
- // read our string from input stream
- let str = this.scriptableInputStream.read(
- this.scriptableInputStream.available()
- );
-
- // remove this callback object from queue now that we have read
- this.inputQueue.shift();
-
- // request next wait if there is one
- tryAsyncWait();
-
- // finally resolve promise
- resolve(str);
- } catch (err) {
- reject(err);
- }
- },
- });
-
- // length 1 imples that there is no in-flight asyncWait, so we may immediately
- // follow through on this read
- if (this.inputQueue.length == 1) {
- tryAsyncWait();
- }
- });
- }
-
- close() {
- this.outputStream.close();
- this.inputStream.close();
- }
-}
-
-class ControlSocket {
- constructor(asyncSocket) {
- this.socket = asyncSocket;
- this._isOpen = true;
- this.pendingData = "";
- this.pendingLines = [];
-
- this.mainDispatcher = io.callbackDispatcher();
- this.notificationDispatcher = io.callbackDispatcher();
- // mainDispatcher pushes only async notifications (650) to notificationDispatcher
- this.mainDispatcher.addCallback(
- /^650/,
- this._handleNotification.bind(this)
- );
- // callback for handling responses and errors
- this.mainDispatcher.addCallback(
- /^[245]\d\d/,
- this._handleCommandReply.bind(this)
- );
-
- this.commandQueue = [];
-
- this._startMessagePump();
- }
-
- // blocks until an entire line is read and returns it
- // immediately returns next line in queue (pendingLines) if present
- async _readLine() {
- // keep reading from socket until we have a full line to return
- while (!this.pendingLines.length) {
- // read data from our socket and spit on newline tokens
- this.pendingData += await this.socket.read();
- let lines = this.pendingData.split("\r\n");
-
- // the last line will either be empty string, or a partial read of a response/event
- // so save it off for the next socket read
- this.pendingData = lines.pop();
-
- // copy remaining full lines to our pendingLines list
- this.pendingLines = this.pendingLines.concat(lines);
- }
- return this.pendingLines.shift();
- }
-
- // blocks until an entire message is ready and returns it
- async _readMessage() {
- // whether we are searching for the end of a multi-line values
- // See control-spec section 3.9
- let handlingMultlineValue = false;
- let endOfMessageFound = false;
- const message = [];
-
- do {
- const line = await this._readLine();
- message.push(line);
-
- if (handlingMultlineValue) {
- // look for end of multiline
- if (line.match(/^\.$/)) {
- handlingMultlineValue = false;
- }
- } else {
- // 'Multiline values' are possible. We avoid interrupting one by detecting it
- // and waiting for a terminating "." on its own line.
- // (See control-spec section 3.9 and https://trac.torproject.org/16990#comment:28
- // Ensure this is the first line of a new message
- // eslint-disable-next-line no-lonely-if
- if (message.length === 1 && line.match(/^\d\d\d\+.+?=$/)) {
- handlingMultlineValue = true;
- }
- // look for end of message (note the space character at end of the regex)
- else if (line.match(/^\d\d\d /)) {
- if (message.length == 1) {
- endOfMessageFound = true;
- } else {
- let firstReplyCode = message[0].substring(0, 3);
- let lastReplyCode = line.substring(0, 3);
- if (firstReplyCode == lastReplyCode) {
- endOfMessageFound = true;
- }
- }
- }
- }
- } while (!endOfMessageFound);
-
- // join our lines back together to form one message
- return message.join("\r\n");
- }
-
- async _startMessagePump() {
- try {
- while (true) {
- let message = await this._readMessage();
- log("controlPort >> " + message);
- this.mainDispatcher.pushMessage(message);
- }
- } catch (err) {
- this._isOpen = false;
- for (const cmd of this.commandQueue) {
- cmd.reject(err);
- }
- this.commandQueue = [];
- }
- }
-
- _writeNextCommand() {
- let cmd = this.commandQueue[0];
- log("controlPort << " + cmd.commandString);
- this.socket.write(`${cmd.commandString}\r\n`).catch(cmd.reject);
- }
-
- async sendCommand(commandString) {
- if (!this.isOpen()) {
- throw new Error("ControlSocket not open");
- }
-
- // this promise is resolved either in _handleCommandReply, or
- // in _startMessagePump (on stream error)
- return new Promise((resolve, reject) => {
- let command = {
- commandString,
- resolve,
- reject,
- };
-
- this.commandQueue.push(command);
- if (this.commandQueue.length == 1) {
- this._writeNextCommand();
- }
- });
- }
-
- _handleCommandReply(message) {
- let cmd = this.commandQueue.shift();
- if (message.match(/^2/)) {
- cmd.resolve(message);
- } else if (message.match(/^[45]/)) {
- let myErr = new Error(cmd.commandString + " -> " + message);
- // Add Tor-specific information to the Error object.
- let idx = message.indexOf(" ");
- if (idx > 0) {
- myErr.torStatusCode = message.substring(0, idx);
- myErr.torMessage = message.substring(idx);
- } else {
- myErr.torStatusCode = message;
- }
- cmd.reject(myErr);
- } else {
- cmd.reject(
- new Error(
- `ControlSocket::_handleCommandReply received unexpected message:\n----\n${message}\n----`
- )
- );
- }
-
- // send next command if one is available
- if (this.commandQueue.length) {
- this._writeNextCommand();
- }
- }
-
- _handleNotification(message) {
- this.notificationDispatcher.pushMessage(message);
- }
-
- close() {
- this.socket.close();
- this._isOpen = false;
- }
-
- addNotificationCallback(regex, callback) {
- this.notificationDispatcher.addCallback(regex, callback);
- }
-
- isOpen() {
- return this._isOpen;
- }
-}
-
-// ## io
-// I/O utilities namespace
-
-let io = {};
-
-// __io.callbackDispatcher()__.
-// Returns dispatcher object with three member functions:
-// dispatcher.addCallback(regex, callback), dispatcher.removeCallback(callback),
-// and dispatcher.pushMessage(message).
-// Pass pushMessage to another function that needs a callback with a single string
-// argument. Whenever dispatcher.pushMessage receives a string, the dispatcher will
-// check for any regex matches and pass the string on to the corresponding callback(s).
-io.callbackDispatcher = function () {
- let callbackPairs = [],
- removeCallback = function (aCallback) {
- callbackPairs = callbackPairs.filter(function ([regex, callback]) {
- return callback !== aCallback;
- });
- },
- addCallback = function (regex, callback) {
- if (callback) {
- callbackPairs.push([regex, callback]);
- }
- return function () {
- removeCallback(callback);
- };
- },
- pushMessage = function (message) {
- for (let [regex, callback] of callbackPairs) {
- if (message.match(regex)) {
- callback(message);
- }
- }
- };
- return {
- pushMessage,
- removeCallback,
- addCallback,
- };
-};
-
-// __io.controlSocket(ipcFile, host, port, password)__.
-// Instantiates and returns a socket to a tor ControlPort at ipcFile or
-// host:port, authenticating with the given password. Example:
-//
-// // Open the socket
-// let socket = await io.controlSocket(undefined, "127.0.0.1", 9151, "MyPassw0rd");
-// // Send command and receive "250" response reply or error is thrown
-// await socket.sendCommand(commandText);
-// // Register or deregister for "650" notifications
-// // that match regex
-// socket.addNotificationCallback(regex, callback);
-// socket.removeNotificationCallback(callback);
-// // Close the socket permanently
-// socket.close();
-io.controlSocket = async function (ipcFile, host, port, password) {
- let socket = new AsyncSocket(ipcFile, host, port);
- let controlSocket = new ControlSocket(socket);
-
- // Log in to control port.
- await controlSocket.sendCommand("authenticate " + (password || ""));
- // Activate needed events.
- await controlSocket.sendCommand("setevents stream");
-
- return controlSocket;
-};
-
-// ## utils
-// A namespace for utility functions
-let utils = {};
-
-// __utils.identity(x)__.
-// Returns its argument unchanged.
-utils.identity = function (x) {
- return x;
-};
-
-// __utils.isString(x)__.
-// Returns true iff x is a string.
-utils.isString = function (x) {
- return typeof x === "string" || x instanceof String;
-};
-
-// __utils.capture(string, regex)__.
-// Takes a string and returns an array of capture items, where regex must have a single
-// capturing group and use the suffix /.../g to specify a global search.
-utils.capture = function (string, regex) {
- let matches = [];
- // Special trick to use string.replace for capturing multiple matches.
- string.replace(regex, function (a, captured) {
- matches.push(captured);
- });
- return matches;
-};
-
-// __utils.extractor(regex)__.
-// Returns a function that takes a string and returns an array of regex matches. The
-// regex must use the suffix /.../g to specify a global search.
-utils.extractor = function (regex) {
- return function (text) {
- return utils.capture(text, regex);
- };
-};
-
-// __utils.splitLines(string)__.
-// Splits a string into an array of strings, each corresponding to a line.
-utils.splitLines = function (string) {
- return string.split(/\r?\n/);
-};
-
-// __utils.splitAtSpaces(string)__.
-// Splits a string into chunks between spaces. Does not split at spaces
-// inside pairs of quotation marks.
-utils.splitAtSpaces = utils.extractor(/((\S*?"(.*?)")+\S*|\S+)/g);
-
-// __utils.splitAtFirst(string, regex)__.
-// Splits a string at the first instance of regex match. If no match is
-// found, returns the whole string.
-utils.splitAtFirst = function (string, regex) {
- let match = string.match(regex);
- return match
- ? [
- string.substring(0, match.index),
- string.substring(match.index + match[0].length),
- ]
- : string;
-};
-
-// __utils.splitAtEquals(string)__.
-// Splits a string into chunks between equals. Does not split at equals
-// inside pairs of quotation marks.
-utils.splitAtEquals = utils.extractor(/(([^=]*?"(.*?)")+[^=]*|[^=]+)/g);
-
-// __utils.mergeObjects(arrayOfObjects)__.
-// Takes an array of objects like [{"a":"b"},{"c":"d"}] and merges to a single object.
-// Pure function.
-utils.mergeObjects = function (arrayOfObjects) {
- let result = {};
- for (let obj of arrayOfObjects) {
- for (let key in obj) {
- result[key] = obj[key];
- }
- }
- return result;
-};
-
-// __utils.listMapData(parameterString, listNames)__.
-// Takes a list of parameters separated by spaces, of which the first several are
-// unnamed, and the remainder are named, in the form `NAME=VALUE`. Apply listNames
-// to the unnamed parameters, and combine them in a map with the named parameters.
-// Example: `40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH`
-//
-// utils.listMapData("40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH",
-// ["streamID", "event", "circuitID", "IP"])
-// // --> {"streamID" : "40", "event" : "FAILED", "circuitID" : "0",
-// // "address" : "95.78.59.36:80", "REASON" : "CANT_ATTACH"}"
-utils.listMapData = function (parameterString, listNames) {
- // Split out the space-delimited parameters.
- let parameters = utils.splitAtSpaces(parameterString),
- dataMap = {};
- // Assign listNames to the first n = listNames.length parameters.
- for (let i = 0; i < listNames.length; ++i) {
- dataMap[listNames[i]] = parameters[i];
- }
- // Read key-value pairs and copy these to the dataMap.
- for (let i = listNames.length; i < parameters.length; ++i) {
- let [key, value] = utils.splitAtEquals(parameters[i]);
- if (key && value) {
- dataMap[key] = value;
- }
- }
- return dataMap;
-};
-
-// __utils.rejectPromise(errorMessage)__.
-// Returns a rejected promise with the given error message.
-utils.rejectPromise = errorMessage => Promise.reject(new Error(errorMessage));
-
-// ## info
-// A namespace for functions related to tor's GETINFO and GETCONF command.
-let info = {};
-
-// __info.keyValueStringsFromMessage(messageText)__.
-// Takes a message (text) response to GETINFO or GETCONF and provides
-// a series of key-value strings, which are either multiline (with a `250+` prefix):
-//
-// 250+config/defaults=
-// AccountingMax "0 bytes"
-// AllowDotExit "0"
-// .
-//
-// or single-line (with a `250-` or `250 ` prefix):
-//
-// 250-version=0.2.6.0-alpha-dev (git-b408125288ad6943)
-info.keyValueStringsFromMessage = utils.extractor(
- /^(250\+[\s\S]+?^\.|250[- ].+?)$/gim
-);
-
-// __info.applyPerLine(transformFunction)__.
-// Returns a function that splits text into lines,
-// and applies transformFunction to each line.
-info.applyPerLine = function (transformFunction) {
- return function (text) {
- return utils.splitLines(text.trim()).map(transformFunction);
- };
-};
-
-// __info.routerStatusParser(valueString)__.
-// Parses a router status entry as, described in
-// https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt
-// (search for "router status entry")
-info.routerStatusParser = function (valueString) {
- let lines = utils.splitLines(valueString),
- objects = [];
- for (let line of lines) {
- // Drop first character and grab data following it.
- let myData = line.substring(2),
- // Accumulate more maps with data, depending on the first character in the line.
- dataFun = {
- r: data =>
- utils.listMapData(data, [
- "nickname",
- "identity",
- "digest",
- "publicationDate",
- "publicationTime",
- "IP",
- "ORPort",
- "DirPort",
- ]),
- a: data => ({ IPv6: data }),
- s: data => ({ statusFlags: utils.splitAtSpaces(data) }),
- v: data => ({ version: data }),
- w: data => utils.listMapData(data, []),
- p: data => ({ portList: data.split(",") }),
- }[line.charAt(0)];
- if (dataFun !== undefined) {
- objects.push(dataFun(myData));
- }
- }
- return utils.mergeObjects(objects);
-};
-
-// __info.circuitStatusParser(line)__.
-// Parse the output of a circuit status line.
-info.circuitStatusParser = function (line) {
- let data = utils.listMapData(line, ["id", "status", "circuit"]),
- circuit = data.circuit;
- // Parse out the individual circuit IDs and names.
- if (circuit) {
- data.circuit = circuit.split(",").map(function (x) {
- return x.split(/~|=/);
- });
- }
- return data;
-};
-
-// __info.streamStatusParser(line)__.
-// Parse the output of a stream status line.
-info.streamStatusParser = function (text) {
- return utils.listMapData(text, [
- "StreamID",
- "StreamStatus",
- "CircuitID",
- "Target",
- ]);
-};
-
-// TODO: fix this parsing logic to handle bridgeLine correctly
-// fingerprint/id is an optional parameter
-// __info.bridgeParser(bridgeLine)__.
-// Takes a single line from a `getconf bridge` result and returns
-// a map containing the bridge's type, address, and ID.
-info.bridgeParser = function (bridgeLine) {
- let result = {},
- tokens = bridgeLine.split(/\s+/);
- // First check if we have a "vanilla" bridge:
- if (tokens[0].match(/^\d+\.\d+\.\d+\.\d+/)) {
- result.type = "vanilla";
- [result.address, result.ID] = tokens;
- // Several bridge types have a similar format:
- } else {
- result.type = tokens[0];
- if (
- [
- "flashproxy",
- "fte",
- "meek",
- "meek_lite",
- "obfs3",
- "obfs4",
- "scramblesuit",
- "snowflake",
- ].includes(result.type)
- ) {
- [result.address, result.ID] = tokens.slice(1);
- }
- }
- return result.type ? result : null;
-};
-
-// __info.parsers__.
-// A map of GETINFO and GETCONF keys to parsing function, which convert
-// result strings to JavaScript data.
-info.parsers = {
- "ns/id/": info.routerStatusParser,
- "ip-to-country/": utils.identity,
- "circuit-status": info.applyPerLine(info.circuitStatusParser),
- bridge: info.bridgeParser,
- // Currently unused parsers:
- // "ns/name/" : info.routerStatusParser,
- // "stream-status" : info.applyPerLine(info.streamStatusParser),
- // "version" : utils.identity,
- // "config-file" : utils.identity,
-};
-
-// __info.getParser(key)__.
-// Takes a key and determines the parser function that should be used to
-// convert its corresponding valueString to JavaScript data.
-info.getParser = function (key) {
- return (
- info.parsers[key] ||
- info.parsers[key.substring(0, key.lastIndexOf("/") + 1)]
- );
-};
-
-// __info.stringToValue(string)__.
-// Converts a key-value string as from GETINFO or GETCONF to a value.
-info.stringToValue = function (string) {
- // key should look something like `250+circuit-status=` or `250-circuit-status=...`
- // or `250 circuit-status=...`
- let matchForKey = string.match(/^250[ +-](.+?)=/),
- key = matchForKey ? matchForKey[1] : null;
- if (key === null) {
- return null;
- }
- // matchResult finds a single-line result for `250-` or `250 `,
- // or a multi-line one for `250+`.
- let matchResult =
- string.match(/^250[ -].+?=(.*)$/) ||
- string.match(/^250\+.+?=([\s\S]*?)^\.$/m),
- // Retrieve the captured group (the text of the value in the key-value pair)
- valueString = matchResult ? matchResult[1] : null,
- // Get the parser function for the key found.
- parse = info.getParser(key.toLowerCase());
- if (parse === undefined) {
- throw new Error("No parser found for '" + key + "'");
- }
- // Return value produced by the parser.
- return parse(valueString);
-};
-
-// __info.getMultipleResponseValues(message)__.
-// Process multiple responses to a GETINFO or GETCONF request.
-info.getMultipleResponseValues = function (message) {
- return info
- .keyValueStringsFromMessage(message)
- .map(info.stringToValue)
- .filter(utils.identity);
-};
-
-// __info.getInfo(controlSocket, key)__.
-// Sends GETINFO for a single key. Returns a promise with the result.
-info.getInfo = function (aControlSocket, key) {
- if (!utils.isString(key)) {
- return utils.rejectPromise("key argument should be a string");
- }
- return aControlSocket
- .sendCommand("getinfo " + key)
- .then(response => info.getMultipleResponseValues(response)[0]);
-};
-
-// __info.getConf(aControlSocket, key)__.
-// Sends GETCONF for a single key. Returns a promise with the result.
-info.getConf = function (aControlSocket, key) {
- // GETCONF with a single argument returns results with
- // one or more lines that look like `250[- ]key=value`.
- // Any GETCONF lines that contain a single keyword only are currently dropped.
- // So we can use similar parsing to that for getInfo.
- if (!utils.isString(key)) {
- return utils.rejectPromise("key argument should be a string");
- }
- return aControlSocket
- .sendCommand("getconf " + key)
- .then(info.getMultipleResponseValues);
-};
-
-// ## onionAuth
-// A namespace for functions related to tor's ONION_CLIENT_AUTH_* commands.
-let onionAuth = {};
-
-onionAuth.keyInfoStringsFromMessage = utils.extractor(/^250-CLIENT\s+(.+)$/gim);
-
-onionAuth.keyInfoObjectsFromMessage = function (message) {
- let keyInfoStrings = onionAuth.keyInfoStringsFromMessage(message);
- return keyInfoStrings.map(infoStr =>
- utils.listMapData(infoStr, ["hsAddress", "typeAndKey"])
- );
-};
-
-// __onionAuth.viewKeys()__.
-// Sends a ONION_CLIENT_AUTH_VIEW command to retrieve the list of private keys.
-// Returns a promise that is fulfilled with an array of key info objects which
-// contain the following properties:
-// hsAddress
-// typeAndKey
-// Flags (e.g., "Permanent")
-onionAuth.viewKeys = function (aControlSocket) {
- let cmd = "onion_client_auth_view";
- return aControlSocket
- .sendCommand(cmd)
- .then(onionAuth.keyInfoObjectsFromMessage);
-};
-
-// __onionAuth.add(controlSocket, hsAddress, b64PrivateKey, isPermanent)__.
-// Sends a ONION_CLIENT_AUTH_ADD command to add a private key to the
-// Tor configuration.
-onionAuth.add = function (
- aControlSocket,
- hsAddress,
- b64PrivateKey,
- isPermanent
-) {
- if (!utils.isString(hsAddress)) {
- return utils.rejectPromise("hsAddress argument should be a string");
- }
-
- if (!utils.isString(b64PrivateKey)) {
- return utils.rejectPromise("b64PrivateKey argument should be a string");
- }
-
- const keyType = "x25519";
- let cmd = `onion_client_auth_add ${hsAddress} ${keyType}:${b64PrivateKey}`;
- if (isPermanent) {
- cmd += " Flags=Permanent";
- }
- return aControlSocket.sendCommand(cmd);
-};
-
-// __onionAuth.remove(controlSocket, hsAddress)__.
-// Sends a ONION_CLIENT_AUTH_REMOVE command to remove a private key from the
-// Tor configuration.
-onionAuth.remove = function (aControlSocket, hsAddress) {
- if (!utils.isString(hsAddress)) {
- return utils.rejectPromise("hsAddress argument should be a string");
- }
-
- let cmd = `onion_client_auth_remove ${hsAddress}`;
- return aControlSocket.sendCommand(cmd);
-};
-
-// ## event
-// Handlers for events
-
-let event = {};
-
-// __event.parsers__.
-// A map of EVENT keys to parsing functions, which convert result strings to JavaScript
-// data.
-event.parsers = {
- stream: info.streamStatusParser,
- // Currently unused:
- // "circ" : info.circuitStatusParser,
-};
-
-// __event.messageToData(type, message)__.
-// Extract the data from an event. Note, at present
-// we only extract streams that look like `"650" SP...`
-event.messageToData = function (type, message) {
- let dataText = message.match(/^650 \S+?\s(.*)/m)[1];
- return dataText && type.toLowerCase() in event.parsers
- ? event.parsers[type.toLowerCase()](dataText)
- : null;
-};
-
-// __event.watchEvent(controlSocket, type, filter, onData)__.
-// Watches for a particular type of event. If filter(data) returns true, the event's
-// data is passed to the onData callback. Returns a zero arg function that
-// stops watching the event. Note: we only observe `"650" SP...` events
-// currently (no `650+...` or `650-...` events).
-event.watchEvent = function (controlSocket, type, filter, onData, raw = false) {
- controlSocket.addNotificationCallback(
- new RegExp("^650 " + type),
- function (message) {
- let data = event.messageToData(type, message);
- if (filter === null || filter(data)) {
- if (raw || !data) {
- onData(message);
- return;
- }
- onData(data);
- }
- }
- );
-};
-
-// ## tor
-// Things related to the main controller.
-let tor = {};
-
-// __tor.controllerCache__.
-// A map from "unix:socketpath" or "host:port" to controller objects. Prevents
-// redundant instantiation of control sockets.
-tor.controllerCache = new Map();
-
-// __tor.controller(ipcFile, host, port, password)__.
-// Creates a tor controller at the given ipcFile or host and port, with the
-// given password.
-tor.controller = async function (ipcFile, host, port, password) {
- let socket = await io.controlSocket(ipcFile, host, port, password);
- return {
- getInfo: key => info.getInfo(socket, key),
- getConf: key => info.getConf(socket, key),
- onionAuthViewKeys: () => onionAuth.viewKeys(socket),
- onionAuthAdd: (hsAddress, b64PrivateKey, isPermanent) =>
- onionAuth.add(socket, hsAddress, b64PrivateKey, isPermanent),
- onionAuthRemove: hsAddress => onionAuth.remove(socket, hsAddress),
- watchEvent: (type, filter, onData, raw = false) => {
- event.watchEvent(socket, type, filter, onData, raw);
- },
- isOpen: () => socket.isOpen(),
- close: () => {
- socket.close();
- },
- sendCommand: cmd => socket.sendCommand(cmd),
- };
-};
-
-// ## Export
-
-let controlPortInfo = {};
-
-// __configureControlPortModule(ipcFile, host, port, password)__.
-// Sets Tor control port connection parameters to be used in future calls to
-// the controller() function. Example:
-// configureControlPortModule(undefined, "127.0.0.1", 9151, "MyPassw0rd");
-var configureControlPortModule = function (ipcFile, host, port, password) {
- controlPortInfo.ipcFile = ipcFile;
- controlPortInfo.host = host;
- controlPortInfo.port = port || 9151;
- controlPortInfo.password = password;
-};
-
-// __controller(avoidCache)__.
-// Instantiates and returns a controller object that is connected and
-// authenticated to a Tor ControlPort using the connection parameters
-// provided in the most recent call to configureControlPortModule(), if
-// the controller doesn't yet exist. Otherwise returns the existing
-// controller to the given ipcFile or host:port. Throws on error.
-//
-// Example:
-//
-// // Get a new controller
-// const avoidCache = true;
-// let c = controller(avoidCache);
-// // Send command and receive `250` reply or error message in a promise:
-// let replyPromise = c.getInfo("ip-to-country/16.16.16.16");
-// // Close the controller permanently
-// c.close();
-var controller = async function (avoidCache) {
- if (!controlPortInfo.ipcFile && !controlPortInfo.host) {
- throw new Error("Please call configureControlPortModule first");
- }
-
- const dest = controlPortInfo.ipcFile
- ? `unix:${controlPortInfo.ipcFile.path}`
- : `${controlPortInfo.host}:${controlPortInfo.port}`;
-
- // constructor shorthand
- const newTorController = async () => {
- return tor.controller(
- controlPortInfo.ipcFile,
- controlPortInfo.host,
- controlPortInfo.port,
- controlPortInfo.password
- );
- };
-
- // avoid cache so always return a new controller
- if (avoidCache) {
- return newTorController();
- }
-
- // first check our cache and see if we already have one
- let cachedController = tor.controllerCache.get(dest);
- if (cachedController && cachedController.isOpen()) {
- return cachedController;
- }
-
- // create a new one and store in the map
- cachedController = await newTorController();
- // overwrite the close() function to prevent consumers from closing a shared/cached controller
- cachedController.close = () => {
- throw new Error("May not close cached Tor Controller as it may be in use");
- };
-
- tor.controllerCache.set(dest, cachedController);
- return cachedController;
-};
-
-// __wait_for_controller(avoidCache)
-// Same as controller() function, but explicitly waits until there is a tor daemon
-// to connect to (either launched by tor-launcher, or if we have an existing system
-// tor daemon)
-var wait_for_controller = function (avoidCache) {
- // if tor process is running (either ours or system) immediately return controller
- if (!TorMonitorService.ownsTorDaemon || TorMonitorService.isRunning) {
- return controller(avoidCache);
- }
-
- // otherwise we must wait for tor to finish launching before resolving
- return new Promise((resolve, reject) => {
- let observer = {
- observe: async (subject, topic, data) => {
- if (topic === TorTopics.ProcessIsReady) {
- try {
- resolve(await controller(avoidCache));
- } catch (err) {
- reject(err);
- }
- Services.obs.removeObserver(observer, TorTopics.ProcessIsReady);
- }
- },
- };
- Services.obs.addObserver(observer, TorTopics.ProcessIsReady);
- });
-};
-
-// Export functions for external use.
-var EXPORTED_SYMBOLS = [
- "configureControlPortModule",
- "controller",
- "wait_for_controller",
-];
=====================================
toolkit/torbutton/modules/utils.js deleted
=====================================
@@ -1,276 +0,0 @@
-// # Utils.js
-// Various helpful utility functions.
-
-// ### Import Mozilla Services
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-// ## Pref utils
-
-// __prefs__. A shortcut to Mozilla Services.prefs.
-let prefs = Services.prefs;
-
-// __getPrefValue(prefName)__
-// Returns the current value of a preference, regardless of its type.
-var getPrefValue = function (prefName) {
- switch (prefs.getPrefType(prefName)) {
- case prefs.PREF_BOOL:
- return prefs.getBoolPref(prefName);
- case prefs.PREF_INT:
- return prefs.getIntPref(prefName);
- case prefs.PREF_STRING:
- return prefs.getCharPref(prefName);
- default:
- return null;
- }
-};
-
-// __bindPref(prefName, prefHandler, init)__
-// Applies prefHandler whenever the value of the pref changes.
-// If init is true, applies prefHandler to the current value.
-// Returns a zero-arg function that unbinds the pref.
-var bindPref = function (prefName, prefHandler, init = false) {
- let update = () => {
- prefHandler(getPrefValue(prefName));
- },
- observer = {
- observe(subject, topic, data) {
- if (data === prefName) {
- update();
- }
- },
- };
- prefs.addObserver(prefName, observer);
- if (init) {
- update();
- }
- return () => {
- prefs.removeObserver(prefName, observer);
- };
-};
-
-// __bindPrefAndInit(prefName, prefHandler)__
-// Applies prefHandler to the current value of pref specified by prefName.
-// Re-applies prefHandler whenever the value of the pref changes.
-// Returns a zero-arg function that unbinds the pref.
-var bindPrefAndInit = (prefName, prefHandler) =>
- bindPref(prefName, prefHandler, true);
-
-// ## Observers
-
-// __observe(topic, callback)__.
-// Observe the given topic. When notification of that topic
-// occurs, calls callback(subject, data). Returns a zero-arg
-// function that stops observing.
-var observe = function (topic, callback) {
- let observer = {
- observe(aSubject, aTopic, aData) {
- if (topic === aTopic) {
- callback(aSubject, aData);
- }
- },
- };
- Services.obs.addObserver(observer, topic);
- return () => Services.obs.removeObserver(observer, topic);
-};
-
-// ## Environment variables
-
-// __getEnv(name)__.
-// Reads the environment variable of the given name.
-var getEnv = function (name) {
- return Services.env.exists(name) ? Services.env.get(name) : undefined;
-};
-
-// __getLocale
-// Returns the app locale to be used in tor-related urls.
-var getLocale = function () {
- const locale = Services.locale.appLocaleAsBCP47;
- if (locale === "ja-JP-macos") {
- // We don't want to distinguish the mac locale.
- return "ja";
- }
- return locale;
-};
-
-// ## Windows
-
-// __dialogsByName__.
-// Map of window names to dialogs.
-let dialogsByName = {};
-
-// __showDialog(parent, url, name, features, arg1, arg2, ...)__.
-// Like window.openDialog, but if the window is already
-// open, just focuses it instead of opening a new one.
-var showDialog = function (parent, url, name, features) {
- let existingDialog = dialogsByName[name];
- if (existingDialog && !existingDialog.closed) {
- existingDialog.focus();
- return existingDialog;
- }
- let newDialog = parent.openDialog.apply(parent, Array.slice(arguments, 1));
- dialogsByName[name] = newDialog;
- return newDialog;
-};
-
-// ## Tor control protocol utility functions
-
-let _torControl = {
- // Unescape Tor Control string aStr (removing surrounding "" and \ escapes).
- // Based on Vidalia's src/common/stringutil.cpp:string_unescape().
- // Returns the unescaped string. Throws upon failure.
- // Within Tor Launcher, the file components/tl-protocol.js also contains a
- // copy of _strUnescape().
- _strUnescape(aStr) {
- if (!aStr) {
- return aStr;
- }
-
- var len = aStr.length;
- if (len < 2 || '"' != aStr.charAt(0) || '"' != aStr.charAt(len - 1)) {
- return aStr;
- }
-
- const kHexRE = /[0-9A-Fa-f]{2}/;
- const kOctalRE = /[0-7]{3}/;
- var rv = "";
- var i = 1;
- var lastCharIndex = len - 2;
- while (i <= lastCharIndex) {
- var c = aStr.charAt(i);
- if ("\\" == c) {
- if (++i > lastCharIndex) {
- throw new Error("missing character after \\");
- }
-
- c = aStr.charAt(i);
- if ("n" == c) {
- rv += "\n";
- } else if ("r" == c) {
- rv += "\r";
- } else if ("t" == c) {
- rv += "\t";
- } else if ("x" == c) {
- if (i + 2 > lastCharIndex) {
- throw new Error("not enough hex characters");
- }
-
- let s = aStr.substr(i + 1, 2);
- if (!kHexRE.test(s)) {
- throw new Error("invalid hex characters");
- }
-
- let val = parseInt(s, 16);
- rv += String.fromCharCode(val);
- i += 3;
- } else if (this._isDigit(c)) {
- let s = aStr.substr(i, 3);
- if (i + 2 > lastCharIndex) {
- throw new Error("not enough octal characters");
- }
-
- if (!kOctalRE.test(s)) {
- throw new Error("invalid octal characters");
- }
-
- let val = parseInt(s, 8);
- rv += String.fromCharCode(val);
- i += 3;
- } // "\\" and others
- else {
- rv += c;
- ++i;
- }
- } else if ('"' == c) {
- throw new Error('unescaped " within string');
- } else {
- rv += c;
- ++i;
- }
- }
-
- // Convert from UTF-8 to Unicode. TODO: is UTF-8 always used in protocol?
- return decodeURIComponent(escape(rv));
- }, // _strUnescape()
-
- // Within Tor Launcher, the file components/tl-protocol.js also contains a
- // copy of _isDigit().
- _isDigit(aChar) {
- const kRE = /^\d$/;
- return aChar && kRE.test(aChar);
- },
-}; // _torControl
-
-// __unescapeTorString(str, resultObj)__.
-// Unescape Tor Control string str (removing surrounding "" and \ escapes).
-// Returns the unescaped string. Throws upon failure.
-var unescapeTorString = function (str) {
- return _torControl._strUnescape(str);
-};
-
-var m_tb_torlog = Cc["@torproject.org/torbutton-logger;1"].getService(
- Ci.nsISupports
-).wrappedJSObject;
-
-var m_tb_string_bundle = torbutton_get_stringbundle();
-
-function torbutton_safelog(nLevel, sMsg, scrub) {
- m_tb_torlog.safe_log(nLevel, sMsg, scrub);
- return true;
-}
-
-function torbutton_log(nLevel, sMsg) {
- m_tb_torlog.log(nLevel, sMsg);
-
- // So we can use it in boolean expressions to determine where the
- // short-circuit is..
- return true;
-}
-
-// load localization strings
-function torbutton_get_stringbundle() {
- var o_stringbundle = false;
-
- try {
- var oBundle = Services.strings;
- o_stringbundle = oBundle.createBundle(
- "chrome://torbutton/locale/torbutton.properties"
- );
- } catch (err) {
- o_stringbundle = false;
- }
- if (!o_stringbundle) {
- torbutton_log(5, "ERROR (init): failed to find torbutton-bundle");
- }
-
- return o_stringbundle;
-}
-
-function torbutton_get_property_string(propertyname) {
- try {
- if (!m_tb_string_bundle) {
- m_tb_string_bundle = torbutton_get_stringbundle();
- }
-
- return m_tb_string_bundle.GetStringFromName(propertyname);
- } catch (e) {
- torbutton_log(4, "Unlocalized string " + propertyname);
- }
-
- return propertyname;
-}
-
-// Export utility functions for external use.
-let EXPORTED_SYMBOLS = [
- "bindPref",
- "bindPrefAndInit",
- "getEnv",
- "getLocale",
- "getPrefValue",
- "observe",
- "showDialog",
- "show_torbrowser_manual",
- "unescapeTorString",
- "torbutton_safelog",
- "torbutton_log",
- "torbutton_get_property_string",
-];
=====================================
toolkit/torbutton/moz.build
=====================================
@@ -3,8 +3,4 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-JAR_MANIFESTS += ['jar.mn']
-
-XPCOM_MANIFESTS += [
- "components.conf",
-]
+JAR_MANIFESTS += ["jar.mn"]
=====================================
tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js
=====================================
@@ -90,11 +90,7 @@ function getGlobalScriptIncludes(scriptPath) {
"browser/components/screenshots/content/"
)
.replace("chrome://browser/content/", "browser/base/content/")
- .replace("chrome://global/content/", "toolkit/content/")
- .replace(
- "chrome://torbutton/content/",
- "toolkit/torbutton/chrome/content/"
- );
+ .replace("chrome://global/content/", "toolkit/content/");
for (let mapping of Object.getOwnPropertyNames(MAPPINGS)) {
if (sourceFile.includes(mapping)) {
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/f67d72…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/f67d72…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in...
by Pier Angelo Vendrame (@pierov) 04 Aug '23
by Pier Angelo Vendrame (@pierov) 04 Aug '23
04 Aug '23
Pier Angelo Vendrame pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
f67d72dd by Henry Wilkes at 2023-08-04T15:47:04+01:00
fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Bug 41964: Wait for both emoji resources before setting bridge emoji
attributes.
Also change annotations in response to a change in locale.
- - - - -
1 changed file:
- browser/components/torpreferences/content/connectionPane.js
Changes:
=====================================
browser/components/torpreferences/content/connectionPane.js
=====================================
@@ -448,10 +448,6 @@ const gConnectionPane = (function () {
const bridgeCards = prefpane.querySelector(selectors.bridges.cards);
const bridgeMenu = prefpane.querySelector(selectors.bridges.cardMenu);
- let emojiAnnotations;
- const emojiListPromise = fetch(
- "chrome://browser/content/torpreferences/bridgemoji/bridge-emojis.json"
- ).then(response => response.json());
this._addBridgeCard = bridgeString => {
const card = bridgeTemplate.cloneNode(true);
card.removeAttribute("id");
@@ -484,16 +480,8 @@ const gConnectionPane = (function () {
const emojis = makeBridgeId(bridgeString).map(emojiIndex => {
const img = document.createElement("img");
img.classList.add("emoji");
- emojiListPromise.then(emojiList => {
- const emoji = emojiList[emojiIndex];
- const cp = emoji.codePointAt(0).toString(16);
- img.setAttribute(
- "src",
- `chrome://browser/content/torpreferences/bridgemoji/svgs/${cp}.svg`
- );
- img.setAttribute("alt", emoji);
- img.setAttribute("title", emojiAnnotations[cp]);
- });
+ // Image is set in _updateBridgeEmojis.
+ img.dataset.emojiIndex = emojiIndex;
return img;
});
const idString = TorStrings.settings.bridgeId;
@@ -699,6 +687,9 @@ const gConnectionPane = (function () {
shownCards--;
}
+ // Newly added emojis.
+ this._updateBridgeEmojis();
+
// And finally update the buttons
removeAll.hidden = false;
showAll.classList.toggle("primary", TorSettings.bridges.enabled);
@@ -729,26 +720,7 @@ const gConnectionPane = (function () {
bridgeCards.classList.remove("list-collapsed");
}
};
- // Use a promise to avoid blocking the population of the page
- // FIXME: Stop using a JSON file, and switch to properties
- const annotationPromise = fetch(
- "chrome://browser/content/torpreferences/bridgemoji/annotations.json"
- );
- annotationPromise.then(async res => {
- const annotations = await res.json();
- const bcp47 = Services.locale.appLocaleAsBCP47;
- const dash = bcp47.indexOf("-");
- const lang = dash !== -1 ? bcp47.substring(0, dash) : bcp47;
- if (bcp47 in annotations) {
- emojiAnnotations = annotations[bcp47];
- } else if (lang in annotations) {
- emojiAnnotations = annotations[lang];
- } else {
- // At the moment, nb does not have annotations!
- emojiAnnotations = annotations.en;
- }
- this._populateBridgeCards();
- });
+ this._populateBridgeCards();
this._updateConnectedBridges = () => {
for (const card of bridgeCards.querySelectorAll(
".currently-connected"
@@ -785,7 +757,7 @@ const gConnectionPane = (function () {
this._updateConnectedBridges();
}
};
- annotationPromise.then(this._checkConnectedBridge.bind(this));
+ this._checkConnectedBridge();
// Add a new bridge
prefpane.querySelector(selectors.bridges.addHeader).textContent =
@@ -879,6 +851,7 @@ const gConnectionPane = (function () {
Services.obs.addObserver(this, TorConnectTopics.StateChange);
Services.obs.addObserver(this, TorMonitorTopics.BridgeChanged);
+ Services.obs.addObserver(this, "intl:app-locales-changed");
},
init() {
@@ -903,6 +876,7 @@ const gConnectionPane = (function () {
Services.obs.removeObserver(this, TorSettingsTopics.SettingChanged);
Services.obs.removeObserver(this, TorConnectTopics.StateChange);
Services.obs.removeObserver(this, TorMonitorTopics.BridgeChanged);
+ Services.obs.removeObserver(this, "intl:app-locales-changed");
},
// whether the page should be present in about:preferences
@@ -939,6 +913,60 @@ const gConnectionPane = (function () {
}
break;
}
+ case "intl:app-locales-changed": {
+ this._updateBridgeEmojis();
+ break;
+ }
+ }
+ },
+
+ /**
+ * Update the bridge emojis to show their corresponding emoji with an
+ * annotation that matches the current locale.
+ */
+ async _updateBridgeEmojis() {
+ if (!this._emojiPromise) {
+ this._emojiPromise = Promise.all([
+ fetch(
+ "chrome://browser/content/torpreferences/bridgemoji/bridge-emojis.json"
+ ).then(response => response.json()),
+ fetch(
+ "chrome://browser/content/torpreferences/bridgemoji/annotations.json"
+ ).then(response => response.json()),
+ ]);
+ }
+ const [emojiList, emojiAnnotations] = await this._emojiPromise;
+ let langCode;
+ // Find the first desired locale we have annotations for.
+ // Add "en" as a fallback.
+ for (const bcp47 of [...Services.locale.appLocalesAsBCP47, "en"]) {
+ langCode = bcp47;
+ if (langCode in emojiAnnotations) {
+ break;
+ }
+ // Remove everything after the dash, if there is one.
+ langCode = bcp47.replace(/-.*/, "");
+ if (langCode in emojiAnnotations) {
+ break;
+ }
+ }
+ for (const img of document.querySelectorAll(".emoji[data-emoji-index]")) {
+ const emoji = emojiList[img.dataset.emojiIndex];
+ if (!emoji) {
+ // Unexpected.
+ console.error(`No emoji for index ${img.dataset.emojiIndex}`);
+ img.removeAttribute("src");
+ img.removeAttribute("alt");
+ img.removeAttribute("title");
+ continue;
+ }
+ const cp = emoji.codePointAt(0).toString(16);
+ img.setAttribute(
+ "src",
+ `chrome://browser/content/torpreferences/bridgemoji/svgs/${cp}.svg`
+ );
+ img.setAttribute("alt", emoji);
+ img.setAttribute("title", emojiAnnotations[langCode][cp]);
}
},
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f67d72d…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f67d72d…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40880: add further required packages to README for debian for release
by richard (@richard) 03 Aug '23
by richard (@richard) 03 Aug '23
03 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
2db98ed5 by Dan Ballard at 2023-08-03T17:02:24+00:00
Bug 40880: add further required packages to README for debian for release
- - - - -
1 changed file:
- README
Changes:
=====================================
README
=====================================
@@ -34,6 +34,8 @@ You also need a few perl modules installed:
- Data::UUID
- Data::Dump
- DateTime
+- XML::Writer
+- Parallel::ForkManager
If you are running Debian or Ubuntu, you can install them with:
@@ -43,7 +45,8 @@ If you are running Debian or Ubuntu, you can install them with:
libstring-shellquote-perl libsort-versions-perl \
libdigest-sha-perl libdata-uuid-perl libdata-dump-perl \
libfile-copy-recursive-perl libfile-slurp-perl git \
- mercurial uidmap zstd
+ mercurial uidmap libxml-writer-perl \
+ libparallel-forkmanager-perl libxml-libxml-perl
If you are running an Arch based system, you should be able to install them with:
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/2…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/2…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][maint-12.5] Bug 40913: add boklm back to list of taggers in relevant projects
by richard (@richard) 03 Aug '23
by richard (@richard) 03 Aug '23
03 Aug '23
richard pushed to branch maint-12.5 at The Tor Project / Applications / tor-browser-build
Commits:
8a687e4f by Richard Pospesel at 2023-08-03T16:51:37+00:00
Bug 40913: add boklm back to list of taggers in relevant projects
- - - - -
4 changed files:
- projects/android-components/config
- projects/fenix/config
- projects/firefox/config
- projects/geckoview/config
Changes:
=====================================
projects/android-components/config
=====================================
@@ -5,6 +5,7 @@ git_hash: '[% project %]-[% c("var/android_components_version") %]-[% c("var/bro
git_url: https://gitlab.torproject.org/tpo/applications/android-components.git
tag_gpg_id: 1
gpg_keyring:
+ - boklm.gpg
- dan_b.gpg
- ma1.gpg
- pierov.gpg
=====================================
projects/fenix/config
=====================================
@@ -5,6 +5,7 @@ git_hash: 'tor-browser-[% c("var/fenix_version") %]-[% c("var/browser_branch") %
git_url: https://gitlab.torproject.org/tpo/applications/fenix.git
tag_gpg_id: 1
gpg_keyring:
+ - boklm.gpg
- dan_b.gpg
- ma1.gpg
- pierov.gpg
=====================================
projects/firefox/config
=====================================
@@ -5,6 +5,7 @@ git_hash: '[% c("var/project-name") %]-[% c("var/firefox_version") %]-[% c("var/
tag_gpg_id: 1
git_url: https://gitlab.torproject.org/tpo/applications/tor-browser.git
gpg_keyring:
+ - boklm.gpg
- dan_b.gpg
- ma1.gpg
- pierov.gpg
=====================================
projects/geckoview/config
=====================================
@@ -5,6 +5,7 @@ git_hash: 'tor-browser-[% c("var/geckoview_version") %]-[% c("var/browser_branch
tag_gpg_id: 1
git_url: https://gitlab.torproject.org/tpo/applications/tor-browser.git
gpg_keyring:
+ - boklm.gpg
- dan_b.gpg
- ma1.gpg
- pierov.gpg
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/8…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/8…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40913: add boklm back to list of taggers in relevant projects
by richard (@richard) 03 Aug '23
by richard (@richard) 03 Aug '23
03 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
67e1e2d3 by Richard Pospesel at 2023-08-03T15:47:27+00:00
Bug 40913: add boklm back to list of taggers in relevant projects
- - - - -
4 changed files:
- projects/android-components/config
- projects/fenix/config
- projects/firefox/config
- projects/geckoview/config
Changes:
=====================================
projects/android-components/config
=====================================
@@ -5,6 +5,7 @@ git_hash: '[% project %]-[% c("var/android_components_version") %]-[% c("var/bro
git_url: https://gitlab.torproject.org/tpo/applications/android-components.git
tag_gpg_id: 1
gpg_keyring:
+ - boklm.gpg
- dan_b.gpg
- ma1.gpg
- pierov.gpg
=====================================
projects/fenix/config
=====================================
@@ -5,6 +5,7 @@ git_hash: 'tor-browser-[% c("var/fenix_version") %]-[% c("var/browser_branch") %
git_url: https://gitlab.torproject.org/tpo/applications/fenix.git
tag_gpg_id: 1
gpg_keyring:
+ - boklm.gpg
- dan_b.gpg
- ma1.gpg
- pierov.gpg
=====================================
projects/firefox/config
=====================================
@@ -5,6 +5,7 @@ git_hash: '[% c("var/project-name") %]-[% c("var/firefox_version") %]-[% c("var/
tag_gpg_id: 1
git_url: https://gitlab.torproject.org/tpo/applications/tor-browser.git
gpg_keyring:
+ - boklm.gpg
- dan_b.gpg
- ma1.gpg
- pierov.gpg
=====================================
projects/geckoview/config
=====================================
@@ -5,6 +5,7 @@ git_hash: 'tor-browser-[% c("var/geckoview_version") %]-[% c("var/browser_branch
tag_gpg_id: 1
git_url: https://gitlab.torproject.org/tpo/applications/tor-browser.git
gpg_keyring:
+ - boklm.gpg
- dan_b.gpg
- ma1.gpg
- pierov.gpg
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/6…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/6…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-update-responses][main] release: new version, 12.5.2
by richard (@richard) 02 Aug '23
by richard (@richard) 02 Aug '23
02 Aug '23
richard pushed to branch main at The Tor Project / Applications / Tor Browser update responses
Commits:
a0a02e43 by Richard Pospesel at 2023-08-02T20:34:39+00:00
release: new version, 12.5.2
- - - - -
30 changed files:
- update_3/release/.htaccess
- − update_3/release/12.0.6-12.5.1-linux32-ALL.xml
- − update_3/release/12.0.6-12.5.1-linux64-ALL.xml
- − update_3/release/12.0.6-12.5.1-macos-ALL.xml
- − update_3/release/12.0.6-12.5.1-win32-ALL.xml
- − update_3/release/12.0.6-12.5.1-win64-ALL.xml
- − update_3/release/12.0.7-12.5.1-linux32-ALL.xml
- − update_3/release/12.0.7-12.5.1-linux64-ALL.xml
- − update_3/release/12.0.7-12.5.1-macos-ALL.xml
- − update_3/release/12.0.7-12.5.1-win32-ALL.xml
- − update_3/release/12.0.7-12.5.1-win64-ALL.xml
- − update_3/release/12.5-12.5.1-linux32-ALL.xml
- − update_3/release/12.5-12.5.1-linux64-ALL.xml
- − update_3/release/12.5-12.5.1-macos-ALL.xml
- − update_3/release/12.5-12.5.1-win32-ALL.xml
- − update_3/release/12.5-12.5.1-win64-ALL.xml
- + update_3/release/12.5-12.5.2-linux32-ALL.xml
- + update_3/release/12.5-12.5.2-linux64-ALL.xml
- + update_3/release/12.5-12.5.2-macos-ALL.xml
- + update_3/release/12.5-12.5.2-win32-ALL.xml
- + update_3/release/12.5-12.5.2-win64-ALL.xml
- + update_3/release/12.5.1-12.5.2-linux32-ALL.xml
- + update_3/release/12.5.1-12.5.2-linux64-ALL.xml
- + update_3/release/12.5.1-12.5.2-macos-ALL.xml
- + update_3/release/12.5.1-12.5.2-win32-ALL.xml
- + update_3/release/12.5.1-12.5.2-win64-ALL.xml
- − update_3/release/12.5.1-linux32-ALL.xml
- − update_3/release/12.5.1-linux64-ALL.xml
- − update_3/release/12.5.1-macos-ALL.xml
- − update_3/release/12.5.1-win32-ALL.xml
The diff was not included because it is too large.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-update-responses…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-update-responses…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build] Pushed new tag tbb-12.5.2-build1
by richard (@richard) 01 Aug '23
by richard (@richard) 01 Aug '23
01 Aug '23
richard pushed new tag tbb-12.5.2-build1 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/tbb…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build] Pushed new tag mb-12.5.2-build1
by richard (@richard) 01 Aug '23
by richard (@richard) 01 Aug '23
01 Aug '23
richard pushed new tag mb-12.5.2-build1 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/mb-…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][maint-12.5] Bugs 40896, 40897: Tor Browser and Mullvad Browser 12.5.2
by richard (@richard) 01 Aug '23
by richard (@richard) 01 Aug '23
01 Aug '23
richard pushed to branch maint-12.5 at The Tor Project / Applications / tor-browser-build
Commits:
25465ba5 by Richard Pospesel at 2023-08-01T15:08:38+00:00
Bugs 40896, 40897: Tor Browser and Mullvad Browser 12.5.2
- - - - -
11 changed files:
- projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
- projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
- projects/browser/allowed_addons.json
- projects/browser/config
- projects/firefox/config
- projects/geckoview/config
- projects/go/config
- projects/manual/config
- projects/tor/config
- projects/translation/config
- rbm.conf
Changes:
=====================================
projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
=====================================
@@ -1,3 +1,54 @@
+Mullvad Browser 12.5.2 - July 31 2023
+ * All Platforms
+ * Updated NoScript to 11.4.26
+ * Upated uBlock Origin to 1.51.0
+ * Updated Firefox to 102.14.0esr
+ * Bug 217: Rebase Mullvad Browser 12.5 stable on top of 102.14esr [mullvad-browser]
+ * Build System
+ * All Platforms
+ * Bug 40889: Add mullvad sha256sums URL to tools/signing/download-unsigned-sha256sums-gpg-signatures-from-people-tpo [tor-browser-build]
+ * Bug 40894: Fix format of keyring/boklm.gpg [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Windows
+ * Bug 31546: Create and expose PDB files for Tor Browser debugging on Windows [tor-browser-build]
+
+Mullvad Browser 13.0a1 - July 20 2023
+ * All Platforms
+ * Updated NoScript to 11.4.25
+ * Updated uBlock Origin to 1.50.0
+ * Updated mullvad-browser-extension to 0.8.3
+ * Updated Firefox to 115.0.2esr
+ * Bug 166: Enable built-in URL anti-tracking query parameters stripping [mullvad-browser]
+ * Bug 183: Rebase Mullvad Browser to Firefox 115 [mullvad-browser]
+ * Bug 213: Add search engines to the default list [mullvad-browser]
+ * Bug 214: Enable cross-tab identity leak protection in "quiet" mode [mullvad-browser]
+ * Bug 26277: When "Safest" setting is enabled searching using duckduckgo should always use the Non-Javascript site for searches [tor-browser]
+ * Bug 33955: Selecting "Copy image" from menu leaks the source URL to the clipboard. This data is often dereferenced by other applications. [tor-browser]
+ * Bug 41759: Rebase Base Browser to 115 nightly [tor-browser]
+ * Bug 41834: Hide "Can't Be Removed - learn more" menu line for uninstallable add-ons [tor-browser]
+ * Bug 41854: Download Spam Protection cannot be overridden to allow legitimate downloads [tor-browser]
+ * Bug 41874: Visual & A11 regressions in add-on badges [tor-browser]
+ * Windows
+ * Bug 41806: Prevent Private Browsing start menu item to be added automatically [tor-browser]
+ * Build System
+ * All Platforms
+ * Bug 40089: Clean up usage of get-moz-build-date script [tor-browser-build]
+ * Bug 40410: Get rid of python2 [tor-browser-build]
+ * Bug 40487: Bump Python version [tor-browser-build]
+ * Bug 40802: Drop the patch for making WASI reproducible [tor-browser-build]
+ * Bug 40836: Update do-all-signing script to also deploy mullvad-browser installer bins to dist.torproject.org [tor-browser-build]
+ * Bug 40868: Bump Rust to 1.69.0 [tor-browser-build]
+ * Bug 40881: do-all-signing is asking for nssdb7 password when signing mullvadbrowser [tor-browser-build]
+ * Bug 40882: Fix static-update-component command in issue_templates [tor-browser-build]
+ * Bug 40886: Update README with instructions for Arch linux [tor-browser-build]
+ * Bug 40889: Add mullvad sha256sums URL to tools/signing/download-unsigned-sha256sums-gpg-signatures-from-people-tpo [tor-browser-build]
+ * Bug 40894: Fix format of keyring/boklm.gpg [tor-browser-build]
+ * Bug 40898: Add doc from tor-browser-spec/processes/ReleaseProcess to gitlab issue templates [tor-browser-build]
+ * Windows
+ * Bug 40832: Unify mingw-w64-clang 32+64 bits [tor-browser-build]
+ * Linux
+ * Bug 40102: Move from Debian Jessie to Debian Stretch for our Linux builds [tor-browser-build]
+
Mullvad Browser 12.5.1 - July 5 2023
* All Platforms
* Updated Firefox to 102.13.0esr
=====================================
projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
=====================================
@@ -1,3 +1,66 @@
+Tor Browser 12.5.2 - July 31 2023
+ * All Platforms
+ * Updated Translations
+ * Updated NoScript to 11.4.26
+ * Bug 41908: Rebase stable 12.5 to 102.14esr [tor-browser]
+ * Windows + macOS + Linux
+ * Updated Firefox to 102.14.0esr
+ * Windows
+ * Bug 41761: xul.dll win crash tor-browser 12.5.1 (based on Mozilla Firefox 102.13.0esr) (64-Bit) [tor-browser]
+ * Android
+ * Updated GeckoView to 102.14.0esr
+ * Bug 41928: Backport Android-specific security fixes from Firefox 116 to ESR 102.14 / 115.1 - based Tor Browser [tor-browser]
+ * Build System
+ * All Platforms
+ * Updated Go to 1.20.6
+ * Bug 40889: Add mullvad sha256sums URL to tools/signing/download-unsigned-sha256sums-gpg-signatures-from-people-tpo [tor-browser-build]
+ * Bug 40894: Fix format of keyring/boklm.gpg [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Windows
+ * Bug 31546: Create and expose PDB files for Tor Browser debugging on Windows [tor-browser-build]
+
+Tor Browser 13.0a1 - July 20 2023
+ * All Platforms
+ * Updated Translations
+ * Updated NoScript to 11.4.25
+ * Updated OpenSSL to 3.0.9
+s * Updated tor to 0.4.8.2-alpha
+ * Bug 40577: Add "suggest url" in DDG onion's manifest [tor-browser]
+ * Bug 40885: Bump version of snowflake to v2.6.0 [tor-browser-build]
+ * Bug 40887: Update Webtunnel version to 38eb5505 [tor-browser-build]
+ * Bug 41092: Enable tracking query parameters stripping [tor-browser]
+ * Bug 41399: Update Mozilla's patch for Bug 1675054 to enable brotli encoding for HTTP onions as well [tor-browser]
+ * Bug 41759: Rebase Base Browser to 115 nightly [tor-browser]
+ * Bug 41796: Rebase Tor Browser to Firefox 115 [tor-browser]
+ * Windows + macOS + Linux
+ * Updated Firefox to 115.0.2esr
+ * Bug 26277: When "Safest" setting is enabled searching using duckduckgo should always use the Non-Javascript site for searches [tor-browser]
+ * Bug 33955: Selecting "Copy image" from menu leaks the source URL to the clipboard. This data is often dereferenced by other applications. [tor-browser]
+ * Bug 41741: Refactor the domain isolator and new circuit [tor-browser]
+ * Bug 41834: Hide "Can't Be Removed - learn more" menu line for uninstallable add-ons [tor-browser]
+ * Bug 41842: Remove the old removal logics from Torbutton [tor-browser]
+ * Bug 41845: Stop forcing (bad) pref values for non-PBM users [tor-browser]
+ * Bug 41854: Download Spam Protection cannot be overridden to allow legitimate downloads [tor-browser]
+ * Bug 41874: Visual & A11 regressions in add-on badges [tor-browser]
+ * Build System
+ * All Platforms
+ * Updated Go to 1.20.6
+ * Bug 40089: Clean up usage of get-moz-build-date script [tor-browser-build]
+ * Bug 40410: Get rid of python2 [tor-browser-build]
+ * Bug 40487: Bump Python version [tor-browser-build]
+ * Bug 40802: Drop the patch for making WASI reproducible [tor-browser-build]
+ * Bug 40854: Update to OpenSSL 3.0 [tor-browser-build]
+ * Bug 40855: Update toolchains for Mozilla 115 [tor-browser-build]
+ * Bug 40868: Bump Rust to 1.69.0 [tor-browser-build]
+ * Bug 40886: Update README with instructions for Arch linux [tor-browser-build]
+ * Bug 40889: Add mullvad sha256sums URL to tools/signing/download-unsigned-sha256sums-gpg-signatures-from-people-tpo [tor-browser-build]
+ * Bug 40894: Fix format of keyring/boklm.gpg [tor-browser-build]
+ * Bug 40898: Add doc from tor-browser-spec/processes/ReleaseProcess to gitlab issue templates [tor-browser-build]
+ * Windows
+ * Bug 40832: Unify mingw-w64-clang 32+64 bits [tor-browser-build]
+ * Linux
+ * Bug 40102: Move from Debian Jessie to Debian Stretch for our Linux builds [tor-browser-build]
+
Tor Browser 12.5.1 - July 5 2023
* All Platforms
* Updated Translations
=====================================
projects/browser/allowed_addons.json
=====================================
@@ -17,7 +17,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/34/9734/13299734/13299734.pn…"
}
],
- "average_daily_users": 976479,
+ "average_daily_users": 962930,
"categories": {
"android": [
"experimental",
@@ -221,10 +221,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.56,
- "bayesian_average": 4.5588356415270255,
- "count": 5048,
- "text_count": 1588
+ "average": 4.559,
+ "bayesian_average": 4.557842722028656,
+ "count": 5084,
+ "text_count": 1599
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/reviews/",
"requires_payment": false,
@@ -321,7 +321,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/versions/",
- "weekly_downloads": 22317
+ "weekly_downloads": 23323
},
"notes": null
},
@@ -337,7 +337,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/56/7656/6937656/6937656.png?…"
}
],
- "average_daily_users": 254700,
+ "average_daily_users": 249584,
"categories": {
"android": [
"security-privacy"
@@ -553,10 +553,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.8141,
- "bayesian_average": 4.809441565441486,
- "count": 1345,
- "text_count": 239
+ "average": 4.8164,
+ "bayesian_average": 4.811755496156948,
+ "count": 1351,
+ "text_count": 238
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/reviews/",
"requires_payment": false,
@@ -641,7 +641,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/versions/",
- "weekly_downloads": 3367
+ "weekly_downloads": 3565
},
"notes": null
},
@@ -657,7 +657,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/73/4073/5474073/5474073.png?…"
}
],
- "average_daily_users": 1106740,
+ "average_daily_users": 1076689,
"categories": {
"android": [
"security-privacy"
@@ -1180,10 +1180,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.8001,
- "bayesian_average": 4.797318686786584,
- "count": 2246,
- "text_count": 430
+ "average": 4.8004,
+ "bayesian_average": 4.797633259374899,
+ "count": 2260,
+ "text_count": 431
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/reviews/",
"requires_payment": false,
@@ -1207,7 +1207,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/versions/",
- "weekly_downloads": 16212
+ "weekly_downloads": 17062
},
"notes": null
},
@@ -1223,7 +1223,7 @@
"picture_url": null
}
],
- "average_daily_users": 6343757,
+ "average_daily_users": 6222480,
"categories": {
"android": [
"security-privacy"
@@ -1235,7 +1235,7 @@
"contributions_url": "",
"created": "2015-04-25T07:26:22Z",
"current_version": {
- "id": 5577564,
+ "id": 5596914,
"compatibility": {
"firefox": {
"min": "78.0",
@@ -1246,7 +1246,7 @@
"max": "*"
}
},
- "edit_url": "https://addons.mozilla.org/en-US/developers/addon/ublock-origin/versions/55…",
+ "edit_url": "https://addons.mozilla.org/en-US/developers/addon/ublock-origin/versions/55…",
"is_strict_compatibility_enabled": false,
"license": {
"id": 6,
@@ -1257,22 +1257,22 @@
"url": "http://www.gnu.org/licenses/gpl-3.0.html"
},
"release_notes": {
- "en-US": "See complete release notes for <a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/7fcd41188a6953809f0fad…" rel=\"nofollow\">1.50.0</a>.\n\n<b>Fixes / changes</b>\n\n<ul><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/977347fbc293beb1b00cf4…" rel=\"nofollow\">Add support to remove attributes in <code>xml-prune</code> scriptlet</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/c9e976bb7ba563f559cb84…" rel=\"nofollow\">Fix/improve <code>href-sanitizer</code> scriptlet</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/947fbffc69bbc18f1b4f8b…" rel=\"nofollow\">Add <code>evaldata-prune</code> scriptlet</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/6fabbf1578224a96f4235c…" rel=\"nofollow\">Add support for <code>xhr</code> in <code>xml-prune</code></a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/b9b7ca5319d3556ce0d3ed…" rel=\"nofollow\">Add <code>remove-node-text.js</code> scriptlet</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/da793e19009995cada9b48…" rel=\"nofollow\">Add <code>trusted-set-constant</code> scriptlet</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/66f1f4b1da03a7a8715f78…" rel=\"nofollow\">Support injecting scriptlet in MAIN or ISOLATED world</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/3fd6afc187b7b8c1500645…" rel=\"nofollow\">Add trusted-source support for privileged scriptlets (and add <code>replace-node-text</code> scriptlet)</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/6e4b972fd0290469b171e1…" rel=\"nofollow\">Add <code>spoof-css</code> scriptlet</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/4b36cc246f707b4874b088…" rel=\"nofollow\">Add back AdGuard Tracking Protection</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/8b57c0f1ab563a91cacf8b…" rel=\"nofollow\">Expand/harden some scriptlets</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/b09d7802d09b73339452c8…" rel=\"nofollow\">Return string when storage.sync.get() promise fails</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/2790102e66a40a639271d7…" rel=\"nofollow\">Do not bail out when <code>content-disposition</code> is <code>inline</code></a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/a84de6a39a7b337bb548e9…" rel=\"nofollow\">Fix improperly unselecting imported lists</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/a38daad10b996d4d90b5a8…" rel=\"nofollow\">Report injected scriptlets in troubleshooting information</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/38b66ee4efd37b2af4acfc…" rel=\"nofollow\">Fix rendering issue of row-filter icon in popup panel</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/554f8ab9f03ac96103840e…" rel=\"nofollow\">Add \"scriptlet\" filter expression to logger</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/ca5c705729c8d4abd4daa7…" rel=\"nofollow\">Fix hostname-detecting regex</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/73dff2c4f0d68678b7155e…" rel=\"nofollow\">Add support for sublists in \"Filter lists\" pane</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/bb5992c336ad2779412f27…" rel=\"nofollow\">Properly handle converted procedural filters in logger</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/ba761870d0433aa47eda9b…" rel=\"nofollow\">Mind small screen size in asset viewer</a></li><li>...</li></ul>\n<a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/5564d601607fa4079ea0e6…" rel=\"nofollow\">Commits history since last version</a>."
+ "en-US": "See complete release notes for <a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/57eadd553bcb629be4f757…" rel=\"nofollow\">1.51.0</a>.\n\n<b>Fixes / changes</b>\n\n<ul><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/a578674b791ed67b926b31…" rel=\"nofollow\">Remove obsolete web<em>accessible</em>resources</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/7071379ad6c88fec4e40b3…" rel=\"nofollow\">Add missing (deprecated) method to google ima</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/bf46690e10ed8a086d9d50…" rel=\"nofollow\">Fix regression in handling of experimental <code>header=</code> filter option</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/f2328063ec5c7512d5899f…" rel=\"nofollow\">Only already normalized CSS selectors can be fast path-compiled</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/e271cd306c9578e0ed17c7…" rel=\"nofollow\">Improve compatibility with AdGuard's scriptlets</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/f42373a2522c9739807b48…" rel=\"nofollow\">Add static network filter option: <code>permissions</code></a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/f502abdb178413a344a43d…" rel=\"nofollow\">Add <code>set-attr</code> scriptlet</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/46449872121aa9a39f8fe5…" rel=\"nofollow\">Do not bail too early when trapping properties in <code>acs</code> scriptlet</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/76b28688717445a36e5ee4…" rel=\"nofollow\">Fix regression in cloud storage import of \"Filter lists\" pane</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/eb0f0fdfbd50904d7cd8e2…" rel=\"nofollow\">Add <code>set-session-storage-item</code> scriptlet</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/f125a8290e220a02336532…" rel=\"nofollow\">Prevent negative position when widget size is greater than viewport size</a><ul><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/e49ec519e35ce90b1ce424…" rel=\"nofollow\">Ensure no negative value for <code>top</code> property of floating widget in logger</a></li></ul></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/2398a39690c82a85f3fb23…" rel=\"nofollow\">Add visual hint when not all sublists are enabled</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/f549cc526fa10665161c86…" rel=\"nofollow\">Add support for AdGuard's noop (<code>_</code>) network filter option</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/c649c1ddb8b1d7f280a068…" rel=\"nofollow\">Add \"tabless\" filter expression for logger output</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/3f7e7d0a06e918e4c8b3f8…" rel=\"nofollow\">Add support for logical expressions to <code>!#if</code> directive</a><ul><li>Also added support for <code>!#else</code></li></ul></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/f1e827e5c4e256a0c832f9…" rel=\"nofollow\">Add resource aliases for increased compatibility with AdGuard lists</a></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/6f42d070e087002ada8f37…" rel=\"nofollow\">Add compatibility with AdGuard's <code>#%#//scriptlet(...)</code> syntax</a><ul><li>Also added support for quoted parameters in <code>##+js(...)</code> syntax</li></ul></li><li><a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/50632bd7b34e7e7185c764…" rel=\"nofollow\">Fix syntax highlighter throwing with invalid patterns</a></li><li>...</li></ul>\n<a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/7c755863346ab5ff9e77ae…" rel=\"nofollow\">Commits history since last version</a>."
},
- "reviewed": "2023-06-12T17:49:10Z",
- "version": "1.50.0",
+ "reviewed": "2023-07-25T09:58:22Z",
+ "version": "1.51.0",
"files": [
{
- "id": 4121906,
- "created": "2023-06-07T14:50:07Z",
- "hash": "sha256:10618003e70b528c3f17996e373146d39e6b15f777ac4ca1f214da2ffdb7a5b3",
+ "id": 4141256,
+ "created": "2023-07-19T23:09:25Z",
+ "hash": "sha256:8b73468bc233a11dd2895219466381783d19123857dd0b6fd16a01820fca4834",
"is_restart_required": false,
"is_webextension": true,
"is_mozilla_signed_extension": false,
"platform": "all",
- "size": 3504841,
+ "size": 3538418,
"status": "public",
- "url": "https://addons.mozilla.org/firefox/downloads/file/4121906/ublock_origin-1.5…",
+ "url": "https://addons.mozilla.org/firefox/downloads/file/4141256/ublock_origin-1.5…",
"permissions": [
"dns",
"menus",
@@ -1388,7 +1388,7 @@
},
"is_disabled": false,
"is_experimental": false,
- "last_updated": "2023-06-29T19:20:30Z",
+ "last_updated": "2023-07-31T14:35:40Z",
"name": {
"ar": "uBlock Origin",
"bg": "uBlock Origin",
@@ -1533,10 +1533,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.7818,
- "bayesian_average": 4.781401064910532,
- "count": 15597,
- "text_count": 4062
+ "average": 4.7821,
+ "bayesian_average": 4.78170472062552,
+ "count": 15763,
+ "text_count": 4095
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/reviews/",
"requires_payment": false,
@@ -1598,7 +1598,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/versions/",
- "weekly_downloads": 124486
+ "weekly_downloads": 131014
},
"notes": null
},
@@ -1614,7 +1614,7 @@
"picture_url": null
}
],
- "average_daily_users": 168312,
+ "average_daily_users": 167538,
"categories": {
"android": [
"photos-media"
@@ -1713,10 +1713,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.492,
- "bayesian_average": 4.486860720506839,
- "count": 1122,
- "text_count": 420
+ "average": 4.4916,
+ "bayesian_average": 4.486471885365025,
+ "count": 1129,
+ "text_count": 422
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/re…",
"requires_payment": false,
@@ -1738,7 +1738,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/ve…",
- "weekly_downloads": 341
+ "weekly_downloads": 312
},
"notes": null
},
@@ -1754,7 +1754,7 @@
"picture_url": null
}
],
- "average_daily_users": 87436,
+ "average_daily_users": 85413,
"categories": {
"android": [
"experimental",
@@ -1867,9 +1867,9 @@
],
"promoted": null,
"ratings": {
- "average": 4.37,
- "bayesian_average": 4.356186612333998,
- "count": 400,
+ "average": 4.3766,
+ "bayesian_average": 4.362648333186986,
+ "count": 401,
"text_count": 112
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/reviews/",
@@ -1892,7 +1892,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/versions/",
- "weekly_downloads": 1599
+ "weekly_downloads": 1759
},
"notes": null
},
@@ -1908,7 +1908,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/64/9064/12929064/12929064.pn…"
}
],
- "average_daily_users": 259583,
+ "average_daily_users": 257591,
"categories": {
"android": [
"photos-media",
@@ -1922,7 +1922,7 @@
"contributions_url": "https://www.paypal.com/donate?hosted_button_id=GLL4UNSNU6SQN&utm_content=pr…",
"created": "2017-06-17T15:23:33Z",
"current_version": {
- "id": 5574786,
+ "id": 5588477,
"compatibility": {
"firefox": {
"min": "91.0",
@@ -1933,7 +1933,7 @@
"max": "*"
}
},
- "edit_url": "https://addons.mozilla.org/en-US/developers/addon/search_by_image/versions/…",
+ "edit_url": "https://addons.mozilla.org/en-US/developers/addon/search_by_image/versions/…",
"is_strict_compatibility_enabled": false,
"license": {
"id": 6,
@@ -1946,20 +1946,20 @@
"release_notes": {
"en-US": "Learn more about this release from the <a href=\"https://prod.outgoing.prod.webservices.mozgcp.net/v1/d50855f24f77fa6f2614b9…" rel=\"nofollow\">changelog</a>."
},
- "reviewed": "2023-06-13T17:09:40Z",
- "version": "5.6.0",
+ "reviewed": "2023-07-06T11:07:12Z",
+ "version": "5.7.0",
"files": [
{
- "id": 4119128,
- "created": "2023-06-01T20:36:45Z",
- "hash": "sha256:fb347a4756e87858fb7ad1e8cb44d3cc4374440d1abdb0fcb3d048c6d5b9c522",
+ "id": 4132819,
+ "created": "2023-07-02T12:35:20Z",
+ "hash": "sha256:9149335f16762c6d4f33ce39f036db763b8c4a3250f5e04e915b827da22a0eb1",
"is_restart_required": false,
"is_webextension": true,
"is_mozilla_signed_extension": false,
"platform": "all",
- "size": 1183625,
+ "size": 1198456,
"status": "public",
- "url": "https://addons.mozilla.org/firefox/downloads/file/4119128/search_by_image-5…",
+ "url": "https://addons.mozilla.org/firefox/downloads/file/4132819/search_by_image-5…",
"permissions": [
"alarms",
"clipboardRead",
@@ -2001,7 +2001,7 @@
},
"is_disabled": false,
"is_experimental": false,
- "last_updated": "2023-06-13T17:09:40Z",
+ "last_updated": "2023-07-06T11:07:12Z",
"name": {
"en-US": "Search by Image"
},
@@ -2127,10 +2127,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.6496,
- "bayesian_average": 4.6449294901011795,
- "count": 1287,
- "text_count": 248
+ "average": 4.6525,
+ "bayesian_average": 4.647859623714072,
+ "count": 1298,
+ "text_count": 250
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/reviews/",
"requires_payment": false,
@@ -2151,7 +2151,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/versions/",
- "weekly_downloads": 3731
+ "weekly_downloads": 3948
},
"notes": null
},
@@ -2174,7 +2174,7 @@
"picture_url": null
}
],
- "average_daily_users": 111659,
+ "average_daily_users": 111141,
"categories": {
"android": [
"other"
@@ -2457,10 +2457,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.4396,
- "bayesian_average": 4.43492961003513,
- "count": 1217,
- "text_count": 323
+ "average": 4.4207,
+ "bayesian_average": 4.416093483315006,
+ "count": 1229,
+ "text_count": 332
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/reviews/",
"requires_payment": false,
@@ -2480,7 +2480,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/versions/",
- "weekly_downloads": 17
+ "weekly_downloads": 52
},
"notes": null
},
@@ -2496,7 +2496,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/43/0143/143/143.png?modified…"
}
],
- "average_daily_users": 307856,
+ "average_daily_users": 301882,
"categories": {
"android": [
"performance",
@@ -2510,7 +2510,7 @@
"contributions_url": "https://www.paypal.com/donate/?hosted_button_id=9ERKTU5MBH4EW&utm_content=p…",
"created": "2005-05-13T10:51:32Z",
"current_version": {
- "id": 5587303,
+ "id": 5597003,
"compatibility": {
"firefox": {
"min": "59.0",
@@ -2521,7 +2521,7 @@
"max": "*"
}
},
- "edit_url": "https://addons.mozilla.org/en-US/developers/addon/noscript/versions/5587303",
+ "edit_url": "https://addons.mozilla.org/en-US/developers/addon/noscript/versions/5597003",
"is_strict_compatibility_enabled": false,
"license": {
"id": 13,
@@ -2532,22 +2532,22 @@
"url": "http://www.gnu.org/licenses/gpl-2.0.html"
},
"release_notes": {
- "en-US": "v 11.4.24\n============================================================\nx [XSS] Fix Base64 hash checks interfering with query string\n checks (thanks barbaz for reporting)\nx [TabGuard] Stop exempting domains bidirectionally by\n default\nx [TabGuard] Fix destination domain being reported as the\n trigger of a warning prompt when all the other tab-tied\n domains have been exempted (thanks barbaz for report)"
+ "en-US": "v 11.4.26\n============================================================\nx [Android] Fixed regression preventing NoScript prompts\n from being shown\nx [XSS] Fallback to execute most demanding regular\n expressions asynchronously\nx [XSS] Removed obsolete Flash-related checks\nx [XSS] Make InjectionChecker's regular expressions easier\n to debug\nx [XSS] Updated OpenID regexp"
},
- "reviewed": "2023-06-29T16:56:20Z",
- "version": "11.4.24",
+ "reviewed": "2023-07-25T09:58:54Z",
+ "version": "11.4.26",
"files": [
{
- "id": 4131645,
- "created": "2023-06-29T15:56:08Z",
- "hash": "sha256:e4b69777d7b9e06e93fcba93d065a246a53c2fa5e113605207836374e48a4fb5",
+ "id": 4141345,
+ "created": "2023-07-20T07:16:01Z",
+ "hash": "sha256:283db0eaebbd2888c1a852f5acabaa8e0225ff1eb1a97a25bceaedfd14d9f44c",
"is_restart_required": false,
"is_webextension": true,
"is_mozilla_signed_extension": false,
"platform": "all",
- "size": 950051,
+ "size": 952442,
"status": "public",
- "url": "https://addons.mozilla.org/firefox/downloads/file/4131645/noscript-11.4.24.…",
+ "url": "https://addons.mozilla.org/firefox/downloads/file/4141345/noscript-11.4.26.…",
"permissions": [
"contextMenus",
"storage",
@@ -2614,7 +2614,7 @@
},
"is_disabled": false,
"is_experimental": false,
- "last_updated": "2023-06-29T16:56:20Z",
+ "last_updated": "2023-07-25T09:58:54Z",
"name": {
"de": "NoScript",
"el": "NoScript",
@@ -2686,10 +2686,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.4062,
- "bayesian_average": 4.403489797240767,
- "count": 2080,
- "text_count": 807
+ "average": 4.4024,
+ "bayesian_average": 4.399703348010946,
+ "count": 2090,
+ "text_count": 811
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/reviews/",
"requires_payment": false,
@@ -2733,7 +2733,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/versions/",
- "weekly_downloads": 6997
+ "weekly_downloads": 7947
},
"notes": null
},
@@ -2749,7 +2749,7 @@
"picture_url": null
}
],
- "average_daily_users": 150711,
+ "average_daily_users": 149808,
"categories": {
"android": [
"performance",
@@ -2864,10 +2864,10 @@
"category": "recommended"
},
"ratings": {
- "average": 3.901,
- "bayesian_average": 3.8967503054386836,
- "count": 1141,
- "text_count": 403
+ "average": 3.9005,
+ "bayesian_average": 3.89624887816371,
+ "count": 1146,
+ "text_count": 406
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/revi…",
"requires_payment": false,
@@ -2886,7 +2886,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/vers…",
- "weekly_downloads": 1697
+ "weekly_downloads": 1645
},
"notes": null
}
=====================================
projects/browser/config
=====================================
@@ -103,12 +103,12 @@ input_files:
enable: '[% ! c("var/android") %]'
- filename: Bundle-Data
enable: '[% ! c("var/android") %]'
- - URL: https://addons.mozilla.org/firefox/downloads/file/4131645/noscript-11.4.24.…
+ - URL: https://addons.mozilla.org/firefox/downloads/file/4141345/noscript-11.4.26.…
name: noscript
- sha256sum: e4b69777d7b9e06e93fcba93d065a246a53c2fa5e113605207836374e48a4fb5
- - URL: https://addons.mozilla.org/firefox/downloads/file/4121906/ublock_origin-1.5…
+ sha256sum: 283db0eaebbd2888c1a852f5acabaa8e0225ff1eb1a97a25bceaedfd14d9f44c
+ - URL: https://addons.mozilla.org/firefox/downloads/file/4141256/ublock_origin-1.5…
name: ublock-origin
- sha256sum: 10618003e70b528c3f17996e373146d39e6b15f777ac4ca1f214da2ffdb7a5b3
+ sha256sum: 8b73468bc233a11dd2895219466381783d19123857dd0b6fd16a01820fca4834
enable: '[% c("var/mullvad-browser") %]'
- URL: https://github.com/mullvad/browser-extension/releases/download/v0.8.3-firef…
name: mullvad-extension
=====================================
projects/firefox/config
=====================================
@@ -13,7 +13,7 @@ container:
use_container: 1
var:
- firefox_platform_version: 102.13.0
+ firefox_platform_version: 102.14.0
firefox_version: '[% c("var/firefox_platform_version") %]esr'
browser_series: '12.5'
browser_branch: '[% c("var/browser_series") %]-1'
=====================================
projects/geckoview/config
=====================================
@@ -13,7 +13,7 @@ container:
use_container: 1
var:
- geckoview_version: 102.13.0esr
+ geckoview_version: 102.14.0esr
browser_branch: 12.5-1
browser_build: 2
copyright_year: '[% exec("git show -s --format=%ci").remove("-.*") %]'
=====================================
projects/go/config
=====================================
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 1.20.5
+version: 1.20.6
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.gz'
container:
use_container: 1
@@ -119,7 +119,7 @@ input_files:
enable: '[% ! c("var/linux") %]'
- URL: 'https://golang.org/dl/go[% c("version") %].src.tar.gz'
name: go
- sha256sum: 9a15c133ba2cfafe79652f4815b62e7cfc267f68df1b9454c6ab2a3ca8b96a88
+ sha256sum: 62ee5bc6fb55b8bae8f705e0cb8df86d6453626b4ecf93279e2867092e0b7f70
- project: go-bootstrap
name: go-bootstrap
target_replace:
=====================================
projects/manual/config
=====================================
@@ -1,7 +1,7 @@
# vim: filetype=yaml sw=2
# To update, see doc/how-to-update-the-manual.txt
# Remember to update also the package's hash, with the version!
-version: 86602
+version: 88998
filename: 'manual-[% c("version") %]-[% c("var/build_id") %].tar.gz'
container:
use_container: 1
@@ -17,8 +17,8 @@ var:
input_files:
- project: container-image
- - URL: 'https://people.torproject.org/~pierov/tbb_files/manual_[% c("version") %].zip'
+ - URL: 'https://people.torproject.org/~richard/tbb_files/manual_[% c("version") %].zip'
name: manual
- sha256sum: ee3c5b7fbe9aa3dfaca546e2f6a57f2f740fb3d6332e8c827e5ba6cbf99299b3
+ sha256sum: 1be6cf35a3c9f243998b7611e9531d4a99591cc257cb55f3924f504b9ead71a7
- filename: packagemanual.py
name: package_script
=====================================
projects/tor/config
=====================================
@@ -1,6 +1,6 @@
# vim: filetype=yaml sw=2
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.gz'
-version: 0.4.7.13
+version: 0.4.7.14
git_hash: 'tor-[% c("version") %]'
git_url: https://gitlab.torproject.org/tpo/core/tor.git
git_submodule: 1
=====================================
projects/translation/config
=====================================
@@ -6,19 +6,19 @@ version: '[% c("abbrev") %]'
steps:
base-browser:
base-browser: '[% INCLUDE build %]'
- git_hash: f43ccce2bc26ec711d94ef3e0246000f16d012df
+ git_hash: 3fd9777d984fb3f3c31f92c1a6be957413c3c4c7
targets:
nightly:
git_hash: 'base-browser'
base-browser-fluent:
base-browser-fluent: '[% INCLUDE build %]'
- git_hash: 1d597e0a5b6f1402c1170e2f65642810bdd586e3
+ git_hash: 6f64004400616a5956d1823a0e8176b60d212090
targets:
nightly:
git_hash: 'basebrowser-newidentityftl'
tor-browser:
tor-browser: '[% INCLUDE build %]'
- git_hash: ac9790fa9367c36fc0e2771409b1f0f3661d168d
+ git_hash: 70f7283dc31e6bd7b7ab954296ac960301d474f4
targets:
nightly:
git_hash: 'tor-browser'
@@ -26,7 +26,7 @@ steps:
fenix: '[% INCLUDE build %]'
# We need to bump the commit before releasing but just pointing to a branch
# might cause too much rebuidling of the Firefox part.
- git_hash: 399d0fcee55eacdc7e2d68cd84c824c4d924357f
+ git_hash: e037147c72348192ddd38c2a21712c65031b912c
targets:
nightly:
git_hash: 'fenix-torbrowserstringsxml'
=====================================
rbm.conf
=====================================
@@ -94,12 +94,11 @@ buildconf:
git_signtag_opt: '-s'
var:
- torbrowser_version: '12.5.1'
+ torbrowser_version: '12.5.2'
torbrowser_build: 'build1'
torbrowser_incremental_from:
+ - 12.5.1
- 12.5
- - 12.0.7
- - 12.0.6
updater_enabled: 1
build_mar: 1
mar_channel_id: '[% c("var/projectname") %]-torproject-[% c("var/channel") %]'
@@ -277,7 +276,6 @@ targets:
exe_name: mullvadbrowser
mar_channel_id: '[% c("var/projectname") %]-mullvad-[% c("var/channel") %]'
locales: []
- torbrowser_build: 'build1'
torbrowser-testbuild:
- testbuild
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/2…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/2…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag base-browser-115.1.0esr-13.0-1-build2
by ma1 (@ma1) 01 Aug '23
by ma1 (@ma1) 01 Aug '23
01 Aug '23
ma1 pushed new tag base-browser-115.1.0esr-13.0-1-build2 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/base-brow…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-115.1.0esr-13.0-1-build2
by ma1 (@ma1) 01 Aug '23
by ma1 (@ma1) 01 Aug '23
01 Aug '23
ma1 pushed new tag tor-browser-115.1.0esr-13.0-1-build2 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-115.1.0esr-13.0-1] 4 commits: Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
by ma1 (@ma1) 01 Aug '23
by ma1 (@ma1) 01 Aug '23
01 Aug '23
ma1 pushed to branch base-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
45b89a93 by Edgar Chen at 2023-08-01T17:39:54+02:00
Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
Fullscreen/PointerLock warnings are initialized with hidden="true", but
change to hidden="" after being shown and hidden again. I think this
started happening when we began using HTML elements instead of XUL as
they handle hidden attribute differently.
Differential Revision: https://phabricator.services.mozilla.com/D177790
- - - - -
1b031e60 by Edgar Chen at 2023-08-01T17:39:54+02:00
Bug 1821884 - Reshow initial fullscreen notification; r=Gijs
Depends on D177790
Differential Revision: https://phabricator.services.mozilla.com/D178339
- - - - -
33af6b34 by Eitan Isaacson at 2023-08-01T17:39:55+02:00
Bug 1819160 - Map Android ids to doc/accessible id pairs. r=Jamie
Differential Revision: https://phabricator.services.mozilla.com/D179737
- - - - -
65183e25 by Jon Coppeard at 2023-08-01T17:39:55+02:00
Bug 1828024 - Require the helper thread lock in the GC helper thread count getter r=sfink
This makes us take a lock to read this state (we already lock when writing it).
Also it adds a release assert in case something goes wrong with the thread
count calculations, as a crash is preferable to the potential deadlock.
Differential Revision: https://phabricator.services.mozilla.com/D181257
- - - - -
12 changed files:
- accessible/android/SessionAccessibility.cpp
- accessible/android/SessionAccessibility.h
- accessible/ipc/DocAccessibleParent.cpp
- accessible/ipc/DocAccessibleParent.h
- accessible/ipc/moz.build
- browser/base/content/browser-fullScreenAndPointerLock.js
- browser/base/content/fullscreen-and-pointerlock.inc.xhtml
- browser/base/content/test/fullscreen/browser_fullscreen_warning.js
- dom/tests/browser/browser_pointerlock_warning.js
- js/src/gc/GC.cpp
- js/src/gc/ParallelMarking.cpp
- js/src/vm/HelperThreadState.h
Changes:
=====================================
accessible/android/SessionAccessibility.cpp
=====================================
@@ -269,12 +269,9 @@ RefPtr<SessionAccessibility> SessionAccessibility::GetInstanceFor(
return GetInstanceFor(doc->GetPresShell());
}
} else {
- DocAccessibleParent* remoteDoc = aAccessible->AsRemote()->Document();
- if (remoteDoc->mSessionAccessibility) {
- return remoteDoc->mSessionAccessibility;
- }
dom::CanonicalBrowsingContext* cbc =
- static_cast<dom::BrowserParent*>(remoteDoc->Manager())
+ static_cast<dom::BrowserParent*>(
+ aAccessible->AsRemote()->Document()->Manager())
->GetBrowsingContext()
->Top();
dom::BrowserParent* bp = cbc->GetBrowserParent();
@@ -285,10 +282,7 @@ RefPtr<SessionAccessibility> SessionAccessibility::GetInstanceFor(
if (auto element = bp->GetOwnerElement()) {
if (auto doc = element->OwnerDoc()) {
if (nsPresContext* presContext = doc->GetPresContext()) {
- RefPtr<SessionAccessibility> sessionAcc =
- GetInstanceFor(presContext->PresShell());
- remoteDoc->mSessionAccessibility = sessionAcc;
- return sessionAcc;
+ return GetInstanceFor(presContext->PresShell());
}
} else {
MOZ_ASSERT_UNREACHABLE(
@@ -684,14 +678,7 @@ void SessionAccessibility::PopulateNodeInfo(
}
Accessible* SessionAccessibility::GetAccessibleByID(int32_t aID) const {
- Accessible* accessible = mIDToAccessibleMap.Get(aID);
- if (accessible && accessible->IsLocal() &&
- accessible->AsLocal()->IsDefunct()) {
- MOZ_ASSERT_UNREACHABLE("Registered accessible is defunct!");
- return nullptr;
- }
-
- return accessible;
+ return mIDToAccessibleMap.Get(aID);
}
#ifdef DEBUG
@@ -705,6 +692,58 @@ static bool IsDetachedDoc(Accessible* aAccessible) {
}
#endif
+SessionAccessibility::IDMappingEntry::IDMappingEntry(Accessible* aAccessible)
+ : mInternalID(0) {
+ *this = aAccessible;
+}
+
+SessionAccessibility::IDMappingEntry&
+SessionAccessibility::IDMappingEntry::operator=(Accessible* aAccessible) {
+ mInternalID = aAccessible->ID();
+ MOZ_ASSERT(!(mInternalID & IS_REMOTE), "First bit is used in accessible ID!");
+ if (aAccessible->IsRemote()) {
+ mInternalID |= IS_REMOTE;
+ }
+
+ Accessible* docAcc = nsAccUtils::DocumentFor(aAccessible);
+ MOZ_ASSERT(docAcc);
+ if (docAcc) {
+ MOZ_ASSERT(docAcc->IsRemote() == aAccessible->IsRemote());
+ if (docAcc->IsRemote()) {
+ mDoc = docAcc->AsRemote()->AsDoc();
+ } else {
+ mDoc = docAcc->AsLocal();
+ }
+ }
+
+ return *this;
+}
+
+SessionAccessibility::IDMappingEntry::operator Accessible*() const {
+ if (mInternalID == 0) {
+ return static_cast<LocalAccessible*>(mDoc.get());
+ }
+
+ if (mInternalID == IS_REMOTE) {
+ return static_cast<DocAccessibleParent*>(mDoc.get());
+ }
+
+ if (mInternalID & IS_REMOTE) {
+ return static_cast<DocAccessibleParent*>(mDoc.get())
+ ->GetAccessible(mInternalID & ~IS_REMOTE);
+ }
+
+ Accessible* accessible =
+ static_cast<LocalAccessible*>(mDoc.get())
+ ->AsDoc()
+ ->GetAccessibleByUniqueID(reinterpret_cast<void*>(mInternalID));
+ // If the accessible is retrievable from the DocAccessible, it can't be
+ // defunct.
+ MOZ_ASSERT(!accessible->AsLocal()->IsDefunct());
+
+ return accessible;
+}
+
void SessionAccessibility::RegisterAccessible(Accessible* aAccessible) {
if (IPCAccessibilityActive()) {
// Don't register accessible in content process.
@@ -766,7 +805,6 @@ void SessionAccessibility::UnregisterAccessible(Accessible* aAccessible) {
}
RefPtr<SessionAccessibility> sessionAcc = GetInstanceFor(aAccessible);
- MOZ_ASSERT(sessionAcc, "Need SessionAccessibility to unregister Accessible!");
if (sessionAcc) {
Accessible* registeredAcc =
sessionAcc->mIDToAccessibleMap.Get(virtualViewID);
=====================================
accessible/android/SessionAccessibility.h
=====================================
@@ -110,10 +110,34 @@ class SessionAccessibility final
jni::NativeWeakPtr<widget::GeckoViewSupport> mWindow; // Parent only
java::SessionAccessibility::NativeProvider::GlobalRef mSessionAccessibility;
+ class IDMappingEntry {
+ public:
+ explicit IDMappingEntry(Accessible* aAccessible);
+
+ IDMappingEntry& operator=(Accessible* aAccessible);
+
+ operator Accessible*() const;
+
+ private:
+ // A strong reference to a DocAccessible or DocAccessibleParent. They don't
+ // share any useful base class except nsISupports, so we use that.
+ // When we retrieve the document from this reference we cast it to
+ // LocalAccessible in the DocAccessible case because DocAccessible has
+ // multiple inheritance paths for nsISupports.
+ RefPtr<nsISupports> mDoc;
+ // The ID of the accessible as used in the internal doc mapping.
+ // We rely on this ID being pointer derived and therefore divisible by two
+ // so we can use the first bit to mark if it is remote or not.
+ uint64_t mInternalID;
+
+ static const uintptr_t IS_REMOTE = 0x1;
+ };
+
/*
* This provides a mapping from 32 bit id to accessible objects.
*/
- nsTHashMap<nsUint32HashKey, Accessible*> mIDToAccessibleMap;
+ nsBaseHashtable<nsUint32HashKey, IDMappingEntry, Accessible*>
+ mIDToAccessibleMap;
};
} // namespace a11y
=====================================
accessible/ipc/DocAccessibleParent.cpp
=====================================
@@ -29,7 +29,6 @@
#endif
#if defined(ANDROID)
-# include "mozilla/a11y/SessionAccessibility.h"
# define ACQUIRE_ANDROID_LOCK \
MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
#else
=====================================
accessible/ipc/DocAccessibleParent.h
=====================================
@@ -29,10 +29,6 @@ class xpcAccessibleGeneric;
class DocAccessiblePlatformExtParent;
#endif
-#ifdef ANDROID
-class SessionAccessibility;
-#endif
-
/*
* These objects live in the main process and comunicate with and represent
* an accessible document in a content process.
@@ -348,10 +344,6 @@ class DocAccessibleParent : public RemoteAccessible,
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) override;
-#ifdef ANDROID
- RefPtr<SessionAccessibility> mSessionAccessibility;
-#endif
-
private:
~DocAccessibleParent();
=====================================
accessible/ipc/moz.build
=====================================
@@ -24,11 +24,6 @@ else:
LOCAL_INCLUDES += [
"/accessible/mac",
]
- elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
- LOCAL_INCLUDES += [
- "/accessible/android",
- "/widget/android",
- ]
else:
LOCAL_INCLUDES += [
"/accessible/other",
=====================================
browser/base/content/browser-fullScreenAndPointerLock.js
=====================================
@@ -62,9 +62,14 @@ var PointerlockFsWarning = {
this._element = document.getElementById(elementId);
// Setup event listeners
this._element.addEventListener("transitionend", this);
+ this._element.addEventListener("transitioncancel", this);
window.addEventListener("mousemove", this, true);
+ window.addEventListener("activate", this);
+ window.addEventListener("deactivate", this);
// The timeout to hide the warning box after a while.
this._timeoutHide = new this.Timeout(() => {
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
this._state = "hidden";
}, timeout);
// The timeout to show the warning box when the pointer is at the top
@@ -116,11 +121,10 @@ var PointerlockFsWarning = {
return;
}
- // Explicitly set the last state to hidden to avoid the warning
- // box being hidden immediately because of mousemove.
- this._state = "onscreen";
- this._lastState = "hidden";
- this._timeoutHide.start();
+ if (Services.focus.activeWindow == window) {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ }
},
/**
@@ -148,7 +152,10 @@ var PointerlockFsWarning = {
this._element.hidden = true;
// Remove all event listeners
this._element.removeEventListener("transitionend", this);
+ this._element.removeEventListener("transitioncancel", this);
window.removeEventListener("mousemove", this, true);
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
// Clear fields
this._element = null;
this._timeoutHide = null;
@@ -186,7 +193,7 @@ var PointerlockFsWarning = {
}
if (newState != "hidden") {
if (currentState != "hidden") {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
} else {
// When the previous state is hidden, the display was none,
// thus no box was constructed. We need to wait for the new
@@ -197,7 +204,7 @@ var PointerlockFsWarning = {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (this._element) {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
}
});
});
@@ -217,7 +224,7 @@ var PointerlockFsWarning = {
} else if (this._timeoutShow.delay >= 0) {
this._timeoutShow.start();
}
- } else {
+ } else if (state != "onscreen") {
let elemRect = this._element.getBoundingClientRect();
if (state == "hiding" && this._lastState != "hidden") {
// If we are on the hiding transition, and the pointer
@@ -239,12 +246,23 @@ var PointerlockFsWarning = {
}
break;
}
- case "transitionend": {
+ case "transitionend":
+ case "transitioncancel": {
if (this._state == "hiding") {
this._element.hidden = true;
}
break;
}
+ case "activate": {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ break;
+ }
+ case "deactivate": {
+ this._state = "hidden";
+ this._timeoutHide.cancel();
+ break;
+ }
}
},
};
=====================================
browser/base/content/fullscreen-and-pointerlock.inc.xhtml
=====================================
@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<html:div id="fullscreen-and-pointerlock-wrapper">
- <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
@@ -20,7 +20,7 @@
</html:button>
</html:div>
- <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
=====================================
browser/base/content/test/fullscreen/browser_fullscreen_warning.js
=====================================
@@ -3,14 +3,35 @@
"use strict";
-add_task(async function test_fullscreen_display_none() {
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
+add_setup(async function init() {
await SpecialPowers.pushPrefEnv({
set: [
["full-screen-api.enabled", true],
["full-screen-api.allow-trusted-requests-only", false],
],
});
+});
+add_task(async function test_fullscreen_display_none() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
@@ -30,11 +51,13 @@ add_task(async function test_fullscreen_display_none() {
},
async function (browser) {
let warning = document.getElementById("fullscreen-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
warning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
);
+
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
// Enter fullscreen
await SpecialPowers.spawn(browser, [], async () => {
let frame = content.document.querySelector("iframe");
@@ -54,39 +77,33 @@ add_task(async function test_fullscreen_display_none() {
);
document.getElementById("fullscreen-exit-button").click();
await exitFullscreenPromise;
+
+ checkWarningState(
+ warning,
+ "hidden",
+ "Should hide fullscreen warning after exiting fullscreen"
+ );
}
);
});
add_task(async function test_fullscreen_pointerlock_conflict() {
- await SpecialPowers.pushPrefEnv({
- set: [
- ["full-screen-api.enabled", true],
- ["full-screen-api.allow-trusted-requests-only", false],
- ],
- });
-
await BrowserTestUtils.withNewTab("https://example.com", async browser => {
let fsWarning = document.getElementById("fullscreen-warning");
let plWarning = document.getElementById("pointerlock-warning");
- is(
- fsWarning.getAttribute("onscreen"),
- null,
- "Should not show full screen warning initially."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning initially."
- );
-
- let fsWarningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
fsWarning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
+ );
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning initially"
);
+ let fsWarningShownPromise = waitForWarningState(fsWarning, "onscreen");
info("Entering full screen and pointer lock.");
await SpecialPowers.spawn(browser, [], async () => {
await content.document.body.requestFullscreen();
@@ -94,15 +111,10 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
});
await fsWarningShownPromise;
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should show full screen warning."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
info("Exiting pointerlock");
@@ -110,18 +122,19 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
await content.document.exitPointerLock();
});
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should still show full screen warning."
+ checkWarningState(
+ fsWarning,
+ "onscreen",
+ "Should still show full screen warning"
);
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
// Cleanup
+ info("Exiting fullscreen");
await document.exitFullscreen();
});
});
=====================================
dom/tests/browser/browser_pointerlock_warning.js
=====================================
@@ -15,6 +15,25 @@ const FRAME_TEST_URL =
encodeURI(BODY_URL) +
'"></iframe></body>';
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
// Make sure the pointerlock warning is shown and exited with the escape key
add_task(async function show_pointerlock_warning_escape() {
let urls = [TEST_URL, FRAME_TEST_URL];
@@ -24,11 +43,7 @@ add_task(async function show_pointerlock_warning_escape() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
let warning = document.getElementById("pointerlock-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
- warning,
- "true"
- );
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
let expectedWarningText;
@@ -49,11 +64,7 @@ add_task(async function show_pointerlock_warning_escape() {
ok(true, "Pointerlock warning shown");
- let warningHiddenPromise = BrowserTestUtils.waitForAttribute(
- "hidden",
- warning,
- ""
- );
+ let warningHiddenPromise = waitForWarningState(warning, "hidden");
await BrowserTestUtils.waitForCondition(
() => warning.innerText == expectedWarningText,
=====================================
js/src/gc/GC.cpp
=====================================
@@ -1331,6 +1331,11 @@ void GCRuntime::assertNoMarkingWork() const {
}
#endif
+static size_t GetGCParallelThreadCount() {
+ AutoLockHelperThreadState lock;
+ return HelperThreadState().getGCParallelThreadCount(lock);
+}
+
bool GCRuntime::updateMarkersVector() {
MOZ_ASSERT(helperThreadCount >= 1,
"There must always be at least one mark task");
@@ -1339,8 +1344,8 @@ bool GCRuntime::updateMarkersVector() {
// Limit worker count to number of GC parallel tasks that can run
// concurrently, otherwise one thread can deadlock waiting on another.
- size_t targetCount = std::min(markingWorkerCount(),
- HelperThreadState().getGCParallelThreadCount());
+ size_t targetCount =
+ std::min(markingWorkerCount(), GetGCParallelThreadCount());
if (markers.length() > targetCount) {
return markers.resize(targetCount);
=====================================
js/src/gc/ParallelMarking.cpp
=====================================
@@ -103,6 +103,10 @@ bool ParallelMarker::markOneColor(MarkColor color, SliceBudget& sliceBudget) {
{
AutoLockHelperThreadState lock;
+ // There should always be enough parallel tasks to run our marking work.
+ MOZ_RELEASE_ASSERT(HelperThreadState().getGCParallelThreadCount(lock) >=
+ workerCount());
+
for (size_t i = 0; i < workerCount(); i++) {
gc->startTask(*tasks[i], lock);
}
=====================================
js/src/vm/HelperThreadState.h
=====================================
@@ -333,9 +333,11 @@ class GlobalHelperThreadState {
GCParallelTaskList& gcParallelWorklist() { return gcParallelWorklist_; }
- size_t getGCParallelThreadCount() const { return gcParallelThreadCount; }
+ size_t getGCParallelThreadCount(const AutoLockHelperThreadState& lock) const {
+ return gcParallelThreadCount;
+ }
void setGCParallelThreadCount(size_t count,
- const AutoLockHelperThreadState&) {
+ const AutoLockHelperThreadState& lock) {
MOZ_ASSERT(count >= 1);
MOZ_ASSERT(count <= threadCount);
gcParallelThreadCount = count;
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/c847c7…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/c847c7…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][maint-12.5] Bug 31546 (fix): Remove the -linux64 suffix from geckodriver
by richard (@richard) 01 Aug '23
by richard (@richard) 01 Aug '23
01 Aug '23
richard pushed to branch maint-12.5 at The Tor Project / Applications / tor-browser-build
Commits:
ed3f291b by Pier Angelo Vendrame at 2023-08-01T16:59:15+02:00
Bug 31546 (fix): Remove the -linux64 suffix from geckodriver
We removed the -linux64 suffix from GeckoDriver, because we intend
enabling it also for other platforms than Linux-x86_64 and for more
consistency with the other artifacts.
However, I forgot to update the filename in projects/firefox/build on
my previous commit.
- - - - -
1 changed file:
- projects/firefox/build
Changes:
=====================================
projects/firefox/build
=====================================
@@ -351,7 +351,7 @@ END;
[% IF c("var/linux-x86_64") && !c("var/asan") -%]
[% c('tar', {
tar_src => [ 'geckodriver' ],
- tar_args => '-cJf ' _ dest_dir _ '/' _ c('filename') _ '/geckodriver-linux64.tar.xz',
+ tar_args => '-cJf ' _ dest_dir _ '/' _ c('filename') _ '/geckodriver.tar.xz',
}) %]
[% END %]
[% ELSIF c("var/windows") -%]
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/e…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/e…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] 4 commits: Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
by ma1 (@ma1) 01 Aug '23
by ma1 (@ma1) 01 Aug '23
01 Aug '23
ma1 pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
a9279174 by Edgar Chen at 2023-07-31T23:49:06+02:00
Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
Fullscreen/PointerLock warnings are initialized with hidden="true", but
change to hidden="" after being shown and hidden again. I think this
started happening when we began using HTML elements instead of XUL as
they handle hidden attribute differently.
Differential Revision: https://phabricator.services.mozilla.com/D177790
- - - - -
9f2eaedb by Edgar Chen at 2023-07-31T23:49:06+02:00
Bug 1821884 - Reshow initial fullscreen notification; r=Gijs
Depends on D177790
Differential Revision: https://phabricator.services.mozilla.com/D178339
- - - - -
30d19aa0 by Eitan Isaacson at 2023-07-31T23:49:07+02:00
Bug 1819160 - Map Android ids to doc/accessible id pairs. r=Jamie
Differential Revision: https://phabricator.services.mozilla.com/D179737
- - - - -
efee8978 by Jon Coppeard at 2023-07-31T23:49:07+02:00
Bug 1828024 - Require the helper thread lock in the GC helper thread count getter r=sfink
This makes us take a lock to read this state (we already lock when writing it).
Also it adds a release assert in case something goes wrong with the thread
count calculations, as a crash is preferable to the potential deadlock.
Differential Revision: https://phabricator.services.mozilla.com/D181257
- - - - -
12 changed files:
- accessible/android/SessionAccessibility.cpp
- accessible/android/SessionAccessibility.h
- accessible/ipc/DocAccessibleParent.cpp
- accessible/ipc/DocAccessibleParent.h
- accessible/ipc/moz.build
- browser/base/content/browser-fullScreenAndPointerLock.js
- browser/base/content/fullscreen-and-pointerlock.inc.xhtml
- browser/base/content/test/fullscreen/browser_fullscreen_warning.js
- dom/tests/browser/browser_pointerlock_warning.js
- js/src/gc/GC.cpp
- js/src/gc/ParallelMarking.cpp
- js/src/vm/HelperThreadState.h
Changes:
=====================================
accessible/android/SessionAccessibility.cpp
=====================================
@@ -269,12 +269,9 @@ RefPtr<SessionAccessibility> SessionAccessibility::GetInstanceFor(
return GetInstanceFor(doc->GetPresShell());
}
} else {
- DocAccessibleParent* remoteDoc = aAccessible->AsRemote()->Document();
- if (remoteDoc->mSessionAccessibility) {
- return remoteDoc->mSessionAccessibility;
- }
dom::CanonicalBrowsingContext* cbc =
- static_cast<dom::BrowserParent*>(remoteDoc->Manager())
+ static_cast<dom::BrowserParent*>(
+ aAccessible->AsRemote()->Document()->Manager())
->GetBrowsingContext()
->Top();
dom::BrowserParent* bp = cbc->GetBrowserParent();
@@ -285,10 +282,7 @@ RefPtr<SessionAccessibility> SessionAccessibility::GetInstanceFor(
if (auto element = bp->GetOwnerElement()) {
if (auto doc = element->OwnerDoc()) {
if (nsPresContext* presContext = doc->GetPresContext()) {
- RefPtr<SessionAccessibility> sessionAcc =
- GetInstanceFor(presContext->PresShell());
- remoteDoc->mSessionAccessibility = sessionAcc;
- return sessionAcc;
+ return GetInstanceFor(presContext->PresShell());
}
} else {
MOZ_ASSERT_UNREACHABLE(
@@ -684,14 +678,7 @@ void SessionAccessibility::PopulateNodeInfo(
}
Accessible* SessionAccessibility::GetAccessibleByID(int32_t aID) const {
- Accessible* accessible = mIDToAccessibleMap.Get(aID);
- if (accessible && accessible->IsLocal() &&
- accessible->AsLocal()->IsDefunct()) {
- MOZ_ASSERT_UNREACHABLE("Registered accessible is defunct!");
- return nullptr;
- }
-
- return accessible;
+ return mIDToAccessibleMap.Get(aID);
}
#ifdef DEBUG
@@ -705,6 +692,58 @@ static bool IsDetachedDoc(Accessible* aAccessible) {
}
#endif
+SessionAccessibility::IDMappingEntry::IDMappingEntry(Accessible* aAccessible)
+ : mInternalID(0) {
+ *this = aAccessible;
+}
+
+SessionAccessibility::IDMappingEntry&
+SessionAccessibility::IDMappingEntry::operator=(Accessible* aAccessible) {
+ mInternalID = aAccessible->ID();
+ MOZ_ASSERT(!(mInternalID & IS_REMOTE), "First bit is used in accessible ID!");
+ if (aAccessible->IsRemote()) {
+ mInternalID |= IS_REMOTE;
+ }
+
+ Accessible* docAcc = nsAccUtils::DocumentFor(aAccessible);
+ MOZ_ASSERT(docAcc);
+ if (docAcc) {
+ MOZ_ASSERT(docAcc->IsRemote() == aAccessible->IsRemote());
+ if (docAcc->IsRemote()) {
+ mDoc = docAcc->AsRemote()->AsDoc();
+ } else {
+ mDoc = docAcc->AsLocal();
+ }
+ }
+
+ return *this;
+}
+
+SessionAccessibility::IDMappingEntry::operator Accessible*() const {
+ if (mInternalID == 0) {
+ return static_cast<LocalAccessible*>(mDoc.get());
+ }
+
+ if (mInternalID == IS_REMOTE) {
+ return static_cast<DocAccessibleParent*>(mDoc.get());
+ }
+
+ if (mInternalID & IS_REMOTE) {
+ return static_cast<DocAccessibleParent*>(mDoc.get())
+ ->GetAccessible(mInternalID & ~IS_REMOTE);
+ }
+
+ Accessible* accessible =
+ static_cast<LocalAccessible*>(mDoc.get())
+ ->AsDoc()
+ ->GetAccessibleByUniqueID(reinterpret_cast<void*>(mInternalID));
+ // If the accessible is retrievable from the DocAccessible, it can't be
+ // defunct.
+ MOZ_ASSERT(!accessible->AsLocal()->IsDefunct());
+
+ return accessible;
+}
+
void SessionAccessibility::RegisterAccessible(Accessible* aAccessible) {
if (IPCAccessibilityActive()) {
// Don't register accessible in content process.
@@ -766,7 +805,6 @@ void SessionAccessibility::UnregisterAccessible(Accessible* aAccessible) {
}
RefPtr<SessionAccessibility> sessionAcc = GetInstanceFor(aAccessible);
- MOZ_ASSERT(sessionAcc, "Need SessionAccessibility to unregister Accessible!");
if (sessionAcc) {
Accessible* registeredAcc =
sessionAcc->mIDToAccessibleMap.Get(virtualViewID);
=====================================
accessible/android/SessionAccessibility.h
=====================================
@@ -110,10 +110,34 @@ class SessionAccessibility final
jni::NativeWeakPtr<widget::GeckoViewSupport> mWindow; // Parent only
java::SessionAccessibility::NativeProvider::GlobalRef mSessionAccessibility;
+ class IDMappingEntry {
+ public:
+ explicit IDMappingEntry(Accessible* aAccessible);
+
+ IDMappingEntry& operator=(Accessible* aAccessible);
+
+ operator Accessible*() const;
+
+ private:
+ // A strong reference to a DocAccessible or DocAccessibleParent. They don't
+ // share any useful base class except nsISupports, so we use that.
+ // When we retrieve the document from this reference we cast it to
+ // LocalAccessible in the DocAccessible case because DocAccessible has
+ // multiple inheritance paths for nsISupports.
+ RefPtr<nsISupports> mDoc;
+ // The ID of the accessible as used in the internal doc mapping.
+ // We rely on this ID being pointer derived and therefore divisible by two
+ // so we can use the first bit to mark if it is remote or not.
+ uint64_t mInternalID;
+
+ static const uintptr_t IS_REMOTE = 0x1;
+ };
+
/*
* This provides a mapping from 32 bit id to accessible objects.
*/
- nsTHashMap<nsUint32HashKey, Accessible*> mIDToAccessibleMap;
+ nsBaseHashtable<nsUint32HashKey, IDMappingEntry, Accessible*>
+ mIDToAccessibleMap;
};
} // namespace a11y
=====================================
accessible/ipc/DocAccessibleParent.cpp
=====================================
@@ -29,7 +29,6 @@
#endif
#if defined(ANDROID)
-# include "mozilla/a11y/SessionAccessibility.h"
# define ACQUIRE_ANDROID_LOCK \
MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
#else
=====================================
accessible/ipc/DocAccessibleParent.h
=====================================
@@ -29,10 +29,6 @@ class xpcAccessibleGeneric;
class DocAccessiblePlatformExtParent;
#endif
-#ifdef ANDROID
-class SessionAccessibility;
-#endif
-
/*
* These objects live in the main process and comunicate with and represent
* an accessible document in a content process.
@@ -348,10 +344,6 @@ class DocAccessibleParent : public RemoteAccessible,
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) override;
-#ifdef ANDROID
- RefPtr<SessionAccessibility> mSessionAccessibility;
-#endif
-
private:
~DocAccessibleParent();
=====================================
accessible/ipc/moz.build
=====================================
@@ -24,11 +24,6 @@ else:
LOCAL_INCLUDES += [
"/accessible/mac",
]
- elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
- LOCAL_INCLUDES += [
- "/accessible/android",
- "/widget/android",
- ]
else:
LOCAL_INCLUDES += [
"/accessible/other",
=====================================
browser/base/content/browser-fullScreenAndPointerLock.js
=====================================
@@ -62,9 +62,14 @@ var PointerlockFsWarning = {
this._element = document.getElementById(elementId);
// Setup event listeners
this._element.addEventListener("transitionend", this);
+ this._element.addEventListener("transitioncancel", this);
window.addEventListener("mousemove", this, true);
+ window.addEventListener("activate", this);
+ window.addEventListener("deactivate", this);
// The timeout to hide the warning box after a while.
this._timeoutHide = new this.Timeout(() => {
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
this._state = "hidden";
}, timeout);
// The timeout to show the warning box when the pointer is at the top
@@ -116,11 +121,10 @@ var PointerlockFsWarning = {
return;
}
- // Explicitly set the last state to hidden to avoid the warning
- // box being hidden immediately because of mousemove.
- this._state = "onscreen";
- this._lastState = "hidden";
- this._timeoutHide.start();
+ if (Services.focus.activeWindow == window) {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ }
},
/**
@@ -148,7 +152,10 @@ var PointerlockFsWarning = {
this._element.hidden = true;
// Remove all event listeners
this._element.removeEventListener("transitionend", this);
+ this._element.removeEventListener("transitioncancel", this);
window.removeEventListener("mousemove", this, true);
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
// Clear fields
this._element = null;
this._timeoutHide = null;
@@ -186,7 +193,7 @@ var PointerlockFsWarning = {
}
if (newState != "hidden") {
if (currentState != "hidden") {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
} else {
// When the previous state is hidden, the display was none,
// thus no box was constructed. We need to wait for the new
@@ -197,7 +204,7 @@ var PointerlockFsWarning = {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (this._element) {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
}
});
});
@@ -217,7 +224,7 @@ var PointerlockFsWarning = {
} else if (this._timeoutShow.delay >= 0) {
this._timeoutShow.start();
}
- } else {
+ } else if (state != "onscreen") {
let elemRect = this._element.getBoundingClientRect();
if (state == "hiding" && this._lastState != "hidden") {
// If we are on the hiding transition, and the pointer
@@ -239,12 +246,23 @@ var PointerlockFsWarning = {
}
break;
}
- case "transitionend": {
+ case "transitionend":
+ case "transitioncancel": {
if (this._state == "hiding") {
this._element.hidden = true;
}
break;
}
+ case "activate": {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ break;
+ }
+ case "deactivate": {
+ this._state = "hidden";
+ this._timeoutHide.cancel();
+ break;
+ }
}
},
};
=====================================
browser/base/content/fullscreen-and-pointerlock.inc.xhtml
=====================================
@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<html:div id="fullscreen-and-pointerlock-wrapper">
- <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
@@ -20,7 +20,7 @@
</html:button>
</html:div>
- <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
=====================================
browser/base/content/test/fullscreen/browser_fullscreen_warning.js
=====================================
@@ -3,14 +3,35 @@
"use strict";
-add_task(async function test_fullscreen_display_none() {
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
+add_setup(async function init() {
await SpecialPowers.pushPrefEnv({
set: [
["full-screen-api.enabled", true],
["full-screen-api.allow-trusted-requests-only", false],
],
});
+});
+add_task(async function test_fullscreen_display_none() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
@@ -30,11 +51,13 @@ add_task(async function test_fullscreen_display_none() {
},
async function (browser) {
let warning = document.getElementById("fullscreen-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
warning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
);
+
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
// Enter fullscreen
await SpecialPowers.spawn(browser, [], async () => {
let frame = content.document.querySelector("iframe");
@@ -54,39 +77,33 @@ add_task(async function test_fullscreen_display_none() {
);
document.getElementById("fullscreen-exit-button").click();
await exitFullscreenPromise;
+
+ checkWarningState(
+ warning,
+ "hidden",
+ "Should hide fullscreen warning after exiting fullscreen"
+ );
}
);
});
add_task(async function test_fullscreen_pointerlock_conflict() {
- await SpecialPowers.pushPrefEnv({
- set: [
- ["full-screen-api.enabled", true],
- ["full-screen-api.allow-trusted-requests-only", false],
- ],
- });
-
await BrowserTestUtils.withNewTab("https://example.com", async browser => {
let fsWarning = document.getElementById("fullscreen-warning");
let plWarning = document.getElementById("pointerlock-warning");
- is(
- fsWarning.getAttribute("onscreen"),
- null,
- "Should not show full screen warning initially."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning initially."
- );
-
- let fsWarningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
fsWarning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
+ );
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning initially"
);
+ let fsWarningShownPromise = waitForWarningState(fsWarning, "onscreen");
info("Entering full screen and pointer lock.");
await SpecialPowers.spawn(browser, [], async () => {
await content.document.body.requestFullscreen();
@@ -94,15 +111,10 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
});
await fsWarningShownPromise;
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should show full screen warning."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
info("Exiting pointerlock");
@@ -110,18 +122,19 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
await content.document.exitPointerLock();
});
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should still show full screen warning."
+ checkWarningState(
+ fsWarning,
+ "onscreen",
+ "Should still show full screen warning"
);
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
// Cleanup
+ info("Exiting fullscreen");
await document.exitFullscreen();
});
});
=====================================
dom/tests/browser/browser_pointerlock_warning.js
=====================================
@@ -15,6 +15,25 @@ const FRAME_TEST_URL =
encodeURI(BODY_URL) +
'"></iframe></body>';
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
// Make sure the pointerlock warning is shown and exited with the escape key
add_task(async function show_pointerlock_warning_escape() {
let urls = [TEST_URL, FRAME_TEST_URL];
@@ -24,11 +43,7 @@ add_task(async function show_pointerlock_warning_escape() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
let warning = document.getElementById("pointerlock-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
- warning,
- "true"
- );
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
let expectedWarningText;
@@ -49,11 +64,7 @@ add_task(async function show_pointerlock_warning_escape() {
ok(true, "Pointerlock warning shown");
- let warningHiddenPromise = BrowserTestUtils.waitForAttribute(
- "hidden",
- warning,
- ""
- );
+ let warningHiddenPromise = waitForWarningState(warning, "hidden");
await BrowserTestUtils.waitForCondition(
() => warning.innerText == expectedWarningText,
=====================================
js/src/gc/GC.cpp
=====================================
@@ -1331,6 +1331,11 @@ void GCRuntime::assertNoMarkingWork() const {
}
#endif
+static size_t GetGCParallelThreadCount() {
+ AutoLockHelperThreadState lock;
+ return HelperThreadState().getGCParallelThreadCount(lock);
+}
+
bool GCRuntime::updateMarkersVector() {
MOZ_ASSERT(helperThreadCount >= 1,
"There must always be at least one mark task");
@@ -1339,8 +1344,8 @@ bool GCRuntime::updateMarkersVector() {
// Limit worker count to number of GC parallel tasks that can run
// concurrently, otherwise one thread can deadlock waiting on another.
- size_t targetCount = std::min(markingWorkerCount(),
- HelperThreadState().getGCParallelThreadCount());
+ size_t targetCount =
+ std::min(markingWorkerCount(), GetGCParallelThreadCount());
if (markers.length() > targetCount) {
return markers.resize(targetCount);
=====================================
js/src/gc/ParallelMarking.cpp
=====================================
@@ -103,6 +103,10 @@ bool ParallelMarker::markOneColor(MarkColor color, SliceBudget& sliceBudget) {
{
AutoLockHelperThreadState lock;
+ // There should always be enough parallel tasks to run our marking work.
+ MOZ_RELEASE_ASSERT(HelperThreadState().getGCParallelThreadCount(lock) >=
+ workerCount());
+
for (size_t i = 0; i < workerCount(); i++) {
gc->startTask(*tasks[i], lock);
}
=====================================
js/src/vm/HelperThreadState.h
=====================================
@@ -333,9 +333,11 @@ class GlobalHelperThreadState {
GCParallelTaskList& gcParallelWorklist() { return gcParallelWorklist_; }
- size_t getGCParallelThreadCount() const { return gcParallelThreadCount; }
+ size_t getGCParallelThreadCount(const AutoLockHelperThreadState& lock) const {
+ return gcParallelThreadCount;
+ }
void setGCParallelThreadCount(size_t count,
- const AutoLockHelperThreadState&) {
+ const AutoLockHelperThreadState& lock) {
MOZ_ASSERT(count >= 1);
MOZ_ASSERT(count <= threadCount);
gcParallelThreadCount = count;
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/c4e8cd…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/c4e8cd…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40905: Avoid replacing .*browser-testbuild targets
by boklm (@boklm) 01 Aug '23
by boklm (@boklm) 01 Aug '23
01 Aug '23
boklm pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
d8fe33ad by Nicolas Vigier at 2023-08-01T10:53:55+02:00
Bug 40905: Avoid replacing .*browser-testbuild targets
In some places we use target_replace to replace .*browser-.* targets, in
order to replace the torbrowser-$os-$arch targets. However the regexp we
used would also replace the torbrowser-testbuild target, so we need to
update the regexp to exclude the testbuild target.
- - - - -
7 changed files:
- projects/conjure/config
- projects/go/config
- projects/lyrebird/config
- projects/snowflake/config
- projects/tor-android-service/config
- projects/tor-onion-proxy-library/config
- projects/webtunnel/config
Changes:
=====================================
projects/conjure/config
=====================================
@@ -21,4 +21,4 @@ steps:
norec:
sha256sum: 2b403d6edf075777003bf2194a43fb178a28a4eaa7d23ec8f104563d9bbd7e53
target_replace:
- '^torbrowser-.*': 'torbrowser-linux-x86_64'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-linux-x86_64'
=====================================
projects/go/config
=====================================
@@ -123,6 +123,6 @@ input_files:
- project: go-bootstrap
name: go-bootstrap
target_replace:
- '^.*browser-.*': 'basebrowser-linux-x86_64'
+ '^.*browser-(?!testbuild).*': 'basebrowser-linux-x86_64'
- filename: 0001-Use-fixed-go-build-tmp-directory.patch
enable: '[% c("var/android") %]'
=====================================
projects/lyrebird/config
=====================================
@@ -35,4 +35,4 @@ steps:
norec:
sha256sum: '[% c("var/go_vendor_sha256sum") %]'
target_replace:
- '^torbrowser-.*': 'torbrowser-linux-x86_64'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-linux-x86_64'
=====================================
projects/snowflake/config
=====================================
@@ -22,4 +22,4 @@ steps:
norec:
sha256sum: 62f9065881f5a3cbe5ea5a9802e961e1821d9e468cb2a85ebab1e8afcc0cc953
target_replace:
- '^torbrowser-.*': 'torbrowser-linux-x86_64'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-linux-x86_64'
=====================================
projects/tor-android-service/config
=====================================
@@ -26,19 +26,19 @@ input_files:
- project: tor-expert-bundle
name: tor-expert-bundle-armv7
target_replace:
- '^torbrowser-.*': 'torbrowser-android-armv7'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-android-armv7'
- project: tor-expert-bundle
name: tor-expert-bundle-aarch64
target_replace:
- '^torbrowser-.*': 'torbrowser-android-aarch64'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-android-aarch64'
- project: tor-expert-bundle
name: tor-expert-bundle-x86
target_replace:
- '^torbrowser-.*': 'torbrowser-android-x86'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-android-x86'
- project: tor-expert-bundle
name: tor-expert-bundle-x86_64
target_replace:
- '^torbrowser-.*': 'torbrowser-android-x86_64'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-android-x86_64'
- URL: 'https://dl.google.com/dl/android/studio/jetifier-zips/1.0.0-beta10/jetifier…'
name: jetifier
sha256sum: 38186db9c9d1b745890b3d35c0667da1cac146ceb3c26aae5bf0802119472c1b
=====================================
projects/tor-onion-proxy-library/config
=====================================
@@ -21,19 +21,19 @@ input_files:
- project: tor-expert-bundle
name: tor-expert-bundle-armv7
target_replace:
- '^torbrowser-.*': 'torbrowser-android-armv7'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-android-armv7'
- project: tor-expert-bundle
name: tor-expert-bundle-aarch64
target_replace:
- '^torbrowser-.*': 'torbrowser-android-aarch64'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-android-aarch64'
- project: tor-expert-bundle
name: tor-expert-bundle-x86
target_replace:
- '^torbrowser-.*': 'torbrowser-android-x86'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-android-x86'
- project: tor-expert-bundle
name: tor-expert-bundle-x86_64
target_replace:
- '^torbrowser-.*': 'torbrowser-android-x86_64'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-android-x86_64'
- filename: 'gradle-dependencies-[% c("var/gradle_dependencies_version") %]'
name: gradle-dependencies
exec: '[% INCLUDE "fetch-gradle-dependencies" %]'
=====================================
projects/webtunnel/config
=====================================
@@ -21,4 +21,4 @@ steps:
norec:
sha256sum: e3b5a9b3c3939aafa5389246f3a7a7e78d70fe623bed495f99c39cc37bbbe645
target_replace:
- '^torbrowser-.*': 'torbrowser-linux-x86_64'
+ '^torbrowser-(?!testbuild).*': 'torbrowser-linux-x86_64'
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/d…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/d…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40615: Add a README for the bundled font directory.
by Pier Angelo Vendrame (@pierov) 01 Aug '23
by Pier Angelo Vendrame (@pierov) 01 Aug '23
01 Aug '23
Pier Angelo Vendrame pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
571d86aa by FlexFoot at 2023-08-01T08:47:20+00:00
Bug 40615: Add a README for the bundled font directory.
Include a README in the final fonts directory to discourage users from
modifying the bundled fonts and to warn them about the consequences
this could have.
- - - - -
3 changed files:
- + projects/fonts/README.txt
- projects/fonts/build
- projects/fonts/config
Changes:
=====================================
projects/fonts/README.txt
=====================================
@@ -0,0 +1,7 @@
+DO NOT MODIFY THE CONTENTS OF THIS DIRECTORY
+
+Any adjustment to bundled fonts will result in an altered fingerprint. Font
+fingerprinting is more than just detecting what fonts you have, it also includes
+font fallbacks and characters (unicode code points) and any change in those can
+be measured.
+
=====================================
projects/fonts/build
=====================================
@@ -32,6 +32,7 @@ mv noto-fonts-* noto-fonts
[% IF c("var/linux") %]
cp {NotoSansJP-Regular.otf,NotoSansKR-Regular.otf,NotoSansSC-Regular.otf,NotoSansTC-Regular.otf} $distdir/
[% END %]
+cp README.txt "$distdir/000_README.txt"
cd /var/tmp/dist
[% c('tar', {
tar_src => [ 'fonts' ],
=====================================
projects/fonts/config
=====================================
@@ -161,6 +161,7 @@ var:
input_files:
- project: container-image
+ - filename: README.txt
- filename: 'noto-fonts-[% c("var/noto_git_hash") %]-[% c("version") %]'
name: noto-fonts
exec: '[% INCLUDE "fetch-noto-fonts" %]'
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/5…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/5…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][maint-12.5] Bug 31546: Copy Firefox PDBs for Windows
by richard (@richard) 31 Jul '23
by richard (@richard) 31 Jul '23
31 Jul '23
richard pushed to branch maint-12.5 at The Tor Project / Applications / tor-browser-build
Commits:
3ba21986 by Pier Angelo Vendrame at 2023-07-31T20:37:13+00:00
Bug 31546: Copy Firefox PDBs for Windows
Also copy debug symbols after stripping on Linux 32-bit (we only copied
them in Linux 64-bit) and for all our browsers (previously we copied
them only for Tor Browser).
Include the headers directory with the symbols, because some of them
are generated during the build, but they are needed for debugging.
- - - - -
2 changed files:
- projects/browser/build
- projects/firefox/build
Changes:
=====================================
projects/browser/build
=====================================
@@ -397,13 +397,13 @@ SCRIPT_EOF
[% IF c("var/updater_enabled") -%]
cp $rootdir/[% c('input_files_by_name/firefox') %]/mar-tools-*.zip "$OUTDIR"/
[% END -%]
-[% IF c("var/linux-x86_64") -%]
- [% IF c("var/tor-browser") -%]
- cp $rootdir/[% c('input_files_by_name/firefox') %]/browser-debug.tar.xz "$OUTDIR"/[% c("var/project-name") %]-[% c("var/mar_osname") %]-debug.tar.xz
- [% END -%]
- [% IF !c("var/asan") -%]
- cp $rootdir/[% c('input_files_by_name/firefox') %]/geckodriver-linux64.tar.xz "$OUTDIR"/
+[% IF c("var/linux") -%]
+ cp $rootdir/[% c('input_files_by_name/firefox') %]/browser-debug.tar.xz "$OUTDIR/[% c('var/project-name') %]-[% c('var/mar_osname') %]-debug.tar.xz"
+ [% IF c("var/linux-x86_64") && !c("var/asan") -%]
+ cp $rootdir/[% c('input_files_by_name/firefox') %]/geckodriver.tar.xz "$OUTDIR/geckodriver-[% c('var/mar_osname') %].tar.xz"
[% END -%]
+[% ELSIF c("var/windows") -%]
+ cp $rootdir/[% c('input_files_by_name/firefox') %]/browser-debug.zip "$OUTDIR/[% c('var/project-name') %]-[% c('var/mar_osname') %]-debug.zip"
[% END -%]
[%IF c("var/tor-browser") -%]
tor_expert_bundle_src="[% c("input_files_by_name/tor-expert-bundle") %]"
=====================================
projects/firefox/build
=====================================
@@ -211,6 +211,10 @@ export LANG=C.UTF-8
cp obj-*/testing/geckodriver/x86_64-unknown-linux-gnu/release/geckodriver $distdir
[% END %]
cp -a obj-*/dist/[% c('var/exe_name') %]/* $distdir/Browser/
+ mkdir -p $distdir/Debug
+ # Some include files are symlinks, so use -Lr, or the tarball will fail
+ # silently. Also, on Linux we populate the debug symbols by stripping later.
+ cp -Lr obj-*/dist/include $distdir/Debug/
# Remove firefox-bin (we don't use it, see ticket #10126)
rm -f "$distdir/Browser/[% c('var/exe_name') %]-bin"
# TODO: There goes FIPS-140.. We could upload these somewhere unique and
@@ -232,6 +236,11 @@ RBM_TB_EOF
[% ELSE %]
cp -a /var/tmp/dist/fxc2/bin/d3dcompiler_47.dll $distdir/Browser
[% END %]
+ mkdir -p $distdir/Debug/Browser
+ pushd obj-*
+ cp -Lr dist/include $distdir/Debug/
+ find . \( -path ./dist -o -path ./_tests \) -prune -o -name '*.pdb' -exec cp -l {} $distdir/Debug/Browser/ \;
+ popd
[% END %]
[% IF c("var/updater_enabled") -%]
@@ -279,8 +288,8 @@ RBM_TB_EOF
cd $distdir
-[% IF c("var/linux-x86_64") %]
- [% IF !c("var/asan") %]
+[% IF c("var/linux") -%]
+ [% IF c("var/linux-x86_64") && !c("var/asan") -%]
# No need for an unstripped geckodriver
strip geckodriver
[% END %]
@@ -334,17 +343,22 @@ END;
tar_args => '-czf ' _ dest_dir _ '/' _ c('filename') _ '/browser.tar.gz',
}) %]
-[% IF c("var/linux-x86_64") %]
+[% IF c("var/linux") -%]
[% c('tar', {
tar_src => [ 'Debug' ],
tar_args => '-cJf ' _ dest_dir _ '/' _ c('filename') _ '/browser-debug.tar.xz',
}) %]
- [% IF !c("var/asan") %]
+ [% IF c("var/linux-x86_64") && !c("var/asan") -%]
[% c('tar', {
tar_src => [ 'geckodriver' ],
tar_args => '-cJf ' _ dest_dir _ '/' _ c('filename') _ '/geckodriver-linux64.tar.xz',
}) %]
[% END %]
+[% ELSIF c("var/windows") -%]
+ [% c('zip', {
+ zip_src => [ 'Debug' ],
+ zip_args => dest_dir _ '/' _ c('filename') _ '/browser-debug.zip',
+ }) %]
[% END %]
[% IF c("var/updater_enabled") -%]
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/3…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/3…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][maint-12.5] Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects
by richard (@richard) 31 Jul '23
by richard (@richard) 31 Jul '23
31 Jul '23
richard pushed to branch maint-12.5 at The Tor Project / Applications / tor-browser-build
Commits:
5f27b741 by Richard Pospesel at 2023-07-31T19:23:09+00:00
Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects
- - - - -
4 changed files:
- projects/android-components/config
- projects/fenix/config
- projects/firefox/config
- projects/geckoview/config
Changes:
=====================================
projects/android-components/config
=====================================
@@ -5,6 +5,8 @@ git_hash: '[% project %]-[% c("var/android_components_version") %]-[% c("var/bro
git_url: https://gitlab.torproject.org/tpo/applications/android-components.git
tag_gpg_id: 1
gpg_keyring:
+ - dan_b.gpg
+ - ma1.gpg
- pierov.gpg
- richard.gpg
variant: '[% IF c("var/release") %]Release[% ELSE %]Beta[% END %]'
=====================================
projects/fenix/config
=====================================
@@ -5,6 +5,8 @@ git_hash: 'tor-browser-[% c("var/fenix_version") %]-[% c("var/browser_branch") %
git_url: https://gitlab.torproject.org/tpo/applications/fenix.git
tag_gpg_id: 1
gpg_keyring:
+ - dan_b.gpg
+ - ma1.gpg
- pierov.gpg
- richard.gpg
variant: Beta
=====================================
projects/firefox/config
=====================================
@@ -5,6 +5,8 @@ git_hash: '[% c("var/project-name") %]-[% c("var/firefox_version") %]-[% c("var/
tag_gpg_id: 1
git_url: https://gitlab.torproject.org/tpo/applications/tor-browser.git
gpg_keyring:
+ - dan_b.gpg
+ - ma1.gpg
- pierov.gpg
- richard.gpg
container:
=====================================
projects/geckoview/config
=====================================
@@ -5,6 +5,8 @@ git_hash: 'tor-browser-[% c("var/geckoview_version") %]-[% c("var/browser_branch
tag_gpg_id: 1
git_url: https://gitlab.torproject.org/tpo/applications/tor-browser.git
gpg_keyring:
+ - dan_b.gpg
+ - ma1.gpg
- pierov.gpg
- richard.gpg
container:
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/5…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/5…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects
by richard (@richard) 31 Jul '23
by richard (@richard) 31 Jul '23
31 Jul '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
13122472 by Richard Pospesel at 2023-07-31T19:19:03+00:00
Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects
- - - - -
4 changed files:
- projects/android-components/config
- projects/fenix/config
- projects/firefox/config
- projects/geckoview/config
Changes:
=====================================
projects/android-components/config
=====================================
@@ -5,6 +5,8 @@ git_hash: '[% project %]-[% c("var/android_components_version") %]-[% c("var/bro
git_url: https://gitlab.torproject.org/tpo/applications/android-components.git
tag_gpg_id: 1
gpg_keyring:
+ - dan_b.gpg
+ - ma1.gpg
- pierov.gpg
- richard.gpg
variant: '[% IF c("var/release") %]Release[% ELSE %]Beta[% END %]'
=====================================
projects/fenix/config
=====================================
@@ -5,6 +5,8 @@ git_hash: 'tor-browser-[% c("var/fenix_version") %]-[% c("var/browser_branch") %
git_url: https://gitlab.torproject.org/tpo/applications/fenix.git
tag_gpg_id: 1
gpg_keyring:
+ - dan_b.gpg
+ - ma1.gpg
- pierov.gpg
- richard.gpg
variant: Beta
=====================================
projects/firefox/config
=====================================
@@ -5,6 +5,8 @@ git_hash: '[% c("var/project-name") %]-[% c("var/firefox_version") %]-[% c("var/
tag_gpg_id: 1
git_url: https://gitlab.torproject.org/tpo/applications/tor-browser.git
gpg_keyring:
+ - dan_b.gpg
+ - ma1.gpg
- pierov.gpg
- richard.gpg
container:
=====================================
projects/geckoview/config
=====================================
@@ -5,6 +5,8 @@ git_hash: 'tor-browser-[% c("var/geckoview_version") %]-[% c("var/browser_branch
tag_gpg_id: 1
git_url: https://gitlab.torproject.org/tpo/applications/tor-browser.git
gpg_keyring:
+ - dan_b.gpg
+ - ma1.gpg
- pierov.gpg
- richard.gpg
container:
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/1…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/1…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag base-browser-102.14.0esr-12.5-1-build2
by ma1 (@ma1) 31 Jul '23
by ma1 (@ma1) 31 Jul '23
31 Jul '23
ma1 pushed new tag base-browser-102.14.0esr-12.5-1-build2 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/base-brow…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-102.14.0esr-12.5-1] 2 commits: Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
by ma1 (@ma1) 31 Jul '23
by ma1 (@ma1) 31 Jul '23
31 Jul '23
ma1 pushed to branch base-browser-102.14.0esr-12.5-1 at The Tor Project / Applications / Tor Browser
Commits:
f24d6cd4 by Edgar Chen at 2023-07-31T21:15:57+02:00
Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
Fullscreen/PointerLock warnings are initialized with hidden="true", but
change to hidden="" after being shown and hidden again. I think this
started happening when we began using HTML elements instead of XUL as
they handle hidden attribute differently.
Differential Revision: https://phabricator.services.mozilla.com/D177790
- - - - -
53142727 by Edgar Chen at 2023-07-31T21:16:28+02:00
Bug 1821884 - Reshow initial fullscreen notification; r=Gijs
Depends on D177790
Differential Revision: https://phabricator.services.mozilla.com/D178339
- - - - -
4 changed files:
- browser/base/content/browser-fullScreenAndPointerLock.js
- browser/base/content/fullscreen-and-pointerlock.inc.xhtml
- browser/base/content/test/fullscreen/browser_fullscreen_warning.js
- dom/tests/browser/browser_pointerlock_warning.js
Changes:
=====================================
browser/base/content/browser-fullScreenAndPointerLock.js
=====================================
@@ -62,9 +62,14 @@ var PointerlockFsWarning = {
this._element = document.getElementById(elementId);
// Setup event listeners
this._element.addEventListener("transitionend", this);
+ this._element.addEventListener("transitioncancel", this);
window.addEventListener("mousemove", this, true);
+ window.addEventListener("activate", this);
+ window.addEventListener("deactivate", this);
// The timeout to hide the warning box after a while.
this._timeoutHide = new this.Timeout(() => {
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
this._state = "hidden";
}, timeout);
// The timeout to show the warning box when the pointer is at the top
@@ -116,11 +121,10 @@ var PointerlockFsWarning = {
return;
}
- // Explicitly set the last state to hidden to avoid the warning
- // box being hidden immediately because of mousemove.
- this._state = "onscreen";
- this._lastState = "hidden";
- this._timeoutHide.start();
+ if (Services.focus.activeWindow == window) {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ }
},
/**
@@ -148,7 +152,10 @@ var PointerlockFsWarning = {
this._element.hidden = true;
// Remove all event listeners
this._element.removeEventListener("transitionend", this);
+ this._element.removeEventListener("transitioncancel", this);
window.removeEventListener("mousemove", this, true);
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
// Clear fields
this._element = null;
this._timeoutHide = null;
@@ -186,7 +193,7 @@ var PointerlockFsWarning = {
}
if (newState != "hidden") {
if (currentState != "hidden") {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
} else {
// When the previous state is hidden, the display was none,
// thus no box was constructed. We need to wait for the new
@@ -197,7 +204,7 @@ var PointerlockFsWarning = {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (this._element) {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
}
});
});
@@ -217,7 +224,7 @@ var PointerlockFsWarning = {
} else if (this._timeoutShow.delay >= 0) {
this._timeoutShow.start();
}
- } else {
+ } else if (state != "onscreen") {
let elemRect = this._element.getBoundingClientRect();
if (state == "hiding" && this._lastState != "hidden") {
// If we are on the hiding transition, and the pointer
@@ -239,12 +246,23 @@ var PointerlockFsWarning = {
}
break;
}
- case "transitionend": {
+ case "transitionend":
+ case "transitioncancel": {
if (this._state == "hiding") {
this._element.hidden = true;
}
break;
}
+ case "activate": {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ break;
+ }
+ case "deactivate": {
+ this._state = "hidden";
+ this._timeoutHide.cancel();
+ break;
+ }
}
},
};
=====================================
browser/base/content/fullscreen-and-pointerlock.inc.xhtml
=====================================
@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<html:div id="fullscreen-and-pointerlock-wrapper">
- <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
@@ -20,7 +20,7 @@
</html:button>
</html:div>
- <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
=====================================
browser/base/content/test/fullscreen/browser_fullscreen_warning.js
=====================================
@@ -3,14 +3,35 @@
"use strict";
-add_task(async function test_fullscreen_display_none() {
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
+add_setup(async function init() {
await SpecialPowers.pushPrefEnv({
set: [
["full-screen-api.enabled", true],
["full-screen-api.allow-trusted-requests-only", false],
],
});
+});
+add_task(async function test_fullscreen_display_none() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
@@ -30,11 +51,13 @@ add_task(async function test_fullscreen_display_none() {
},
async function(browser) {
let warning = document.getElementById("fullscreen-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
warning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
);
+
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
// Enter fullscreen
await SpecialPowers.spawn(browser, [], async () => {
let frame = content.document.querySelector("iframe");
@@ -54,39 +77,33 @@ add_task(async function test_fullscreen_display_none() {
);
document.getElementById("fullscreen-exit-button").click();
await exitFullscreenPromise;
+
+ checkWarningState(
+ warning,
+ "hidden",
+ "Should hide fullscreen warning after exiting fullscreen"
+ );
}
);
});
add_task(async function test_fullscreen_pointerlock_conflict() {
- await SpecialPowers.pushPrefEnv({
- set: [
- ["full-screen-api.enabled", true],
- ["full-screen-api.allow-trusted-requests-only", false],
- ],
- });
-
await BrowserTestUtils.withNewTab("https://example.com", async browser => {
let fsWarning = document.getElementById("fullscreen-warning");
let plWarning = document.getElementById("pointerlock-warning");
- is(
- fsWarning.getAttribute("onscreen"),
- null,
- "Should not show full screen warning initially."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning initially."
- );
-
- let fsWarningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
fsWarning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
+ );
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning initially"
);
+ let fsWarningShownPromise = waitForWarningState(fsWarning, "onscreen");
info("Entering full screen and pointer lock.");
await SpecialPowers.spawn(browser, [], async () => {
await content.document.body.requestFullscreen();
@@ -94,15 +111,10 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
});
await fsWarningShownPromise;
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should show full screen warning."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
info("Exiting pointerlock");
@@ -110,18 +122,19 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
await content.document.exitPointerLock();
});
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should still show full screen warning."
+ checkWarningState(
+ fsWarning,
+ "onscreen",
+ "Should still show full screen warning"
);
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
// Cleanup
+ info("Exiting fullscreen");
await document.exitFullscreen();
});
});
=====================================
dom/tests/browser/browser_pointerlock_warning.js
=====================================
@@ -15,6 +15,25 @@ const FRAME_TEST_URL =
encodeURI(BODY_URL) +
'"></iframe></body>';
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
// Make sure the pointerlock warning is shown and exited with the escape key
add_task(async function show_pointerlock_warning_escape() {
let urls = [TEST_URL, FRAME_TEST_URL];
@@ -24,11 +43,7 @@ add_task(async function show_pointerlock_warning_escape() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
let warning = document.getElementById("pointerlock-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
- warning,
- "true"
- );
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
let expectedWarningText;
@@ -49,11 +64,7 @@ add_task(async function show_pointerlock_warning_escape() {
ok(true, "Pointerlock warning shown");
- let warningHiddenPromise = BrowserTestUtils.waitForAttribute(
- "hidden",
- warning,
- ""
- );
+ let warningHiddenPromise = waitForWarningState(warning, "hidden");
await BrowserTestUtils.waitForCondition(
() => warning.innerText == expectedWarningText,
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/fadc59…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/fadc59…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-102.14.0esr-12.5-1-build2
by ma1 (@ma1) 31 Jul '23
by ma1 (@ma1) 31 Jul '23
31 Jul '23
ma1 pushed new tag tor-browser-102.14.0esr-12.5-1-build2 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] 9 commits: fixup! Add TorStrings module for localization
by Pier Angelo Vendrame (@pierov) 31 Jul '23
by Pier Angelo Vendrame (@pierov) 31 Jul '23
31 Jul '23
Pier Angelo Vendrame pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
c3d2496b by Pier Angelo Vendrame at 2023-07-27T21:07:52+02:00
fixup! Add TorStrings module for localization
Move the `getLocale` function here from TorButton util.js and stop
importing it.
- - - - -
642df684 by Pier Angelo Vendrame at 2023-07-27T21:07:53+02:00
fixup! Bug 40933: Add tor-launcher functionality
Fix a couple of problems in TorLauncherUtil and TorParsers.
Also, moved the function to parse bridges in TorParsers.
- - - - -
9e825b61 by Pier Angelo Vendrame at 2023-07-27T21:07:53+02:00
fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Use the bridge line parser from TorParsers.
- - - - -
8061f810 by Pier Angelo Vendrame at 2023-07-27T21:07:53+02:00
fixup! Bug 40933: Add tor-launcher functionality
Use actual private members for TorProcess.
- - - - -
f5a3f4af by Pier Angelo Vendrame at 2023-07-27T21:07:54+02:00
fixup! Bug 40933: Add tor-launcher functionality
Group arg pushes in one line (keys and values together).
- - - - -
71b08c4e by Pier Angelo Vendrame at 2023-07-27T21:07:54+02:00
fixup! Bug 40933: Add tor-launcher functionality
TorProcess: use real private properties instead of _, and removed the
dependency on TorProtocolService (temporarily moved it to
TorMonitorService, but eventually we should unify TorProtocolService
and TorMonitorService, to then split them again in a smarter way).
- - - - -
e1a69b4e by Pier Angelo Vendrame at 2023-07-27T21:07:55+02:00
fixup! Bug 10760: Integrate TorButton to TorBrowser core
Move the SOCKS preference updater to TorProtocolService.
We will need to refactor all this kind of stuff, but at least let's get
it in a single place.
Also, since this was the last bit of the startup service, remove the
file, the component and what else was needed to add them.
- - - - -
d457b6f8 by Pier Angelo Vendrame at 2023-07-31T20:42:54+02:00
fixup! Bug 40933: Add tor-launcher functionality
Hashing the control port password is needed only on the process, so move
this function to TorProcess.
- - - - -
c4e8cd0f by Pier Angelo Vendrame at 2023-07-31T20:42:57+02:00
fixup! Bug 10760: Integrate TorButton to TorBrowser core
The hashpassword parameter has been removed.
- - - - -
13 changed files:
- browser/components/torpreferences/content/connectionPane.js
- browser/installer/package-manifest.in
- browser/modules/TorStrings.jsm
- toolkit/components/tor-launcher/TorLauncherUtil.sys.mjs
- toolkit/components/tor-launcher/TorMonitorService.sys.mjs
- toolkit/components/tor-launcher/TorParsers.sys.mjs
- toolkit/components/tor-launcher/TorProcess.sys.mjs
- toolkit/components/tor-launcher/TorProtocolService.sys.mjs
- toolkit/torbutton/chrome/content/torbutton.js
- toolkit/torbutton/components.conf
- − toolkit/torbutton/modules/TorbuttonStartupObserver.jsm
- toolkit/torbutton/moz.build
- − toolkit/torbutton/torbutton.manifest
Changes:
=====================================
browser/components/torpreferences/content/connectionPane.js
=====================================
@@ -14,8 +14,11 @@ const { setTimeout, clearTimeout } = ChromeUtils.import(
const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource } =
ChromeUtils.import("resource:///modules/TorSettings.jsm");
-const { TorProtocolService } = ChromeUtils.import(
- "resource://gre/modules/TorProtocolService.jsm"
+const { TorParsers } = ChromeUtils.importESModule(
+ "resource://gre/modules/TorParsers.sys.mjs"
+);
+const { TorProtocolService } = ChromeUtils.importESModule(
+ "resource://gre/modules/TorProtocolService.sys.mjs"
);
const { TorMonitorService, TorMonitorTopics } = ChromeUtils.import(
"resource://gre/modules/TorMonitorService.jsm"
@@ -495,7 +498,7 @@ const gConnectionPane = (function () {
});
const idString = TorStrings.settings.bridgeId;
const id = card.querySelector(selectors.bridges.cardId);
- const details = parseBridgeLine(bridgeString);
+ const details = TorParsers.parseBridgeLine(bridgeString);
if (details && details.id !== undefined) {
card.setAttribute("data-bridge-id", details.id);
}
@@ -1111,23 +1114,3 @@ function makeBridgeId(bridgeString) {
hash & 0x000000ff,
];
}
-
-function parseBridgeLine(line) {
- const re =
- /^\s*(\S+\s+)?([0-9a-fA-F\.\[\]\:]+:\d{1,5})(\s+[0-9a-fA-F]{40})?(\s+.+)?/;
- const matches = line.match(re);
- if (!matches) {
- return null;
- }
- let bridge = { addr: matches[2] };
- if (matches[1] !== undefined) {
- bridge.transport = matches[1].trim();
- }
- if (matches[3] !== undefined) {
- bridge.id = matches[3].trim().toUpperCase();
- }
- if (matches[4] !== undefined) {
- bridge.args = matches[4].trim();
- }
- return bridge;
-}
=====================================
browser/installer/package-manifest.in
=====================================
@@ -228,7 +228,6 @@
@RESPATH@/components/tor-launcher.manifest
@RESPATH@/chrome/torbutton.manifest
@RESPATH@/chrome/torbutton/*
-@RESPATH@/components/torbutton.manifest
@RESPATH@/chrome/toolkit@JAREXT@
@RESPATH@/chrome/toolkit.manifest
#ifdef MOZ_GTK
=====================================
browser/modules/TorStrings.jsm
=====================================
@@ -11,9 +11,11 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
-const { getLocale } = ChromeUtils.import(
- "resource://torbutton/modules/utils.js"
-);
+
+function getLocale() {
+ const locale = Services.locale.appLocaleAsBCP47;
+ return locale === "ja-JP-macos" ? "ja" : locale;
+}
/*
Tor Property String Bundle
=====================================
toolkit/components/tor-launcher/TorLauncherUtil.sys.mjs
=====================================
@@ -5,6 +5,12 @@
* Tor Launcher Util JS Module
*************************************************************************/
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ FileUtils: "resource://gre/modules/FileUtils.sys.jsm",
+});
+
const kPropBundleURI = "chrome://torbutton/locale/torlauncher.properties";
const kPropNamePrefix = "torlauncher.";
const kIPCDirPrefName = "extensions.torlauncher.tmp_ipc_dir";
@@ -209,14 +215,15 @@ class TorFile {
// and return a file object. The control and SOCKS IPC objects will be
// created by tor.
normalize() {
- if (!this.file.exists() && !this.isIPC) {
+ if (this.file.exists()) {
+ try {
+ this.file.normalize();
+ } catch (e) {
+ console.warn("Normalization of the path failed", e);
+ }
+ } else if (!this.isIPC) {
throw new Error(`${this.fileType} file not found: ${this.file.path}`);
}
- try {
- this.file.normalize();
- } catch (e) {
- console.warn("Normalization of the path failed", e);
- }
// Ensure that the IPC path length is short enough for use by the
// operating system. If not, create and use a unique directory under
@@ -452,6 +459,154 @@ export const TorLauncherUtil = Object.freeze({
return result ? result : "";
},
+ /**
+ * Determine what kind of SOCKS port has been requested for this session or
+ * the browser has been configured for.
+ * On Windows (where Unix domain sockets are not supported), TCP is always
+ * used.
+ *
+ * The following environment variables are supported and take precedence over
+ * preferences:
+ * TOR_TRANSPROXY (do not use a proxy)
+ * TOR_SOCKS_IPC_PATH (file system path; ignored on Windows)
+ * TOR_SOCKS_HOST
+ * TOR_SOCKS_PORT
+ *
+ * The following preferences are consulted:
+ * network.proxy.socks
+ * network.proxy.socks_port
+ * extensions.torlauncher.socks_port_use_ipc (Boolean)
+ * extensions.torlauncher.socks_ipc_path (file system path)
+ * If extensions.torlauncher.socks_ipc_path is empty, a default path is used.
+ *
+ * When using TCP, if a value is not defined via an env variable it is
+ * taken from the corresponding browser preference if possible. The
+ * exceptions are:
+ * If network.proxy.socks contains a file: URL, a default value of
+ * "127.0.0.1" is used instead.
+ * If the network.proxy.socks_port value is not valid (outside the
+ * (0; 65535] range), a default value of 9150 is used instead.
+ *
+ * The SOCKS configuration will not influence the launch of a tor daemon and
+ * the configuration of the control port in any way.
+ * When a SOCKS configuration is required without TOR_SKIP_LAUNCH, the browser
+ * will try to configure the tor instance to use the required configuration.
+ * This also applies to TOR_TRANSPROXY (at least for now): tor will be
+ * launched with its defaults.
+ *
+ * TODO: add a preference to ignore the current configuration, and let tor
+ * listen on any free port. Then, the browser will prompt the daemon the port
+ * to use through the control port (even though this is quite dangerous at the
+ * moment, because with network disabled tor will disable also the SOCKS
+ * listeners, so it means that we will have to check it every time we change
+ * the network status).
+ */
+ getPreferredSocksConfiguration() {
+ if (Services.env.exists("TOR_TRANSPROXY")) {
+ Services.prefs.setBoolPref("network.proxy.socks_remote_dns", false);
+ Services.prefs.setIntPref("network.proxy.type", 0);
+ Services.prefs.setIntPref("network.proxy.socks_port", 0);
+ Services.prefs.setCharPref("network.proxy.socks", "");
+ return { transproxy: true };
+ }
+
+ let useIPC;
+ const socksPortInfo = {
+ transproxy: false,
+ };
+
+ if (!this.isWindows && Services.env.exists("TOR_SOCKS_IPC_PATH")) {
+ useIPC = true;
+ const ipcPath = Services.env.get("TOR_SOCKS_IPC_PATH");
+ if (ipcPath) {
+ socksPortInfo.ipcFile = new lazy.FileUtils.File(ipcPath);
+ }
+ } else {
+ // Check for TCP host and port environment variables.
+ if (Services.env.exists("TOR_SOCKS_HOST")) {
+ socksPortInfo.host = Services.env.get("TOR_SOCKS_HOST");
+ useIPC = false;
+ }
+ if (Services.env.exists("TOR_SOCKS_PORT")) {
+ const port = parseInt(Services.env.get("TOR_SOCKS_PORT"), 10);
+ if (Number.isInteger(port) && port > 0 && port <= 65535) {
+ socksPortInfo.port = port;
+ useIPC = false;
+ }
+ }
+ }
+
+ if (useIPC === undefined) {
+ socksPortInfo.useIPC =
+ !this.isWindows &&
+ Services.prefs.getBoolPref(
+ "extensions.torlauncher.socks_port_use_ipc",
+ false
+ );
+ }
+
+ // Fill in missing SOCKS info from prefs.
+ if (socksPortInfo.useIPC) {
+ if (!socksPortInfo.ipcFile) {
+ socksPortInfo.ipcFile = TorLauncherUtil.getTorFile("socks_ipc", false);
+ }
+ } else {
+ if (!socksPortInfo.host) {
+ let socksAddr = Services.prefs.getCharPref(
+ "network.proxy.socks",
+ "127.0.0.1"
+ );
+ let socksAddrHasHost = socksAddr && !socksAddr.startsWith("file:");
+ socksPortInfo.host = socksAddrHasHost ? socksAddr : "127.0.0.1";
+ }
+
+ if (!socksPortInfo.port) {
+ let socksPort = Services.prefs.getIntPref(
+ "network.proxy.socks_port",
+ 0
+ );
+ // This pref is set as 0 by default in Firefox, use 9150 if we get 0.
+ socksPortInfo.port =
+ socksPort > 0 && socksPort <= 65535 ? socksPort : 9150;
+ }
+ }
+
+ return socksPortInfo;
+ },
+
+ setProxyConfiguration(socksPortInfo) {
+ if (socksPortInfo.transproxy) {
+ return;
+ }
+
+ if (socksPortInfo.useIPC) {
+ const fph = Services.io
+ .getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+ const fileURI = fph.newFileURI(socksPortInfo.ipcFile);
+ Services.prefs.setCharPref("network.proxy.socks", fileURI.spec);
+ Services.prefs.setIntPref("network.proxy.socks_port", 0);
+ } else {
+ if (socksPortInfo.host) {
+ Services.prefs.setCharPref("network.proxy.socks", socksPortInfo.host);
+ }
+ if (socksPortInfo.port) {
+ Services.prefs.setIntPref(
+ "network.proxy.socks_port",
+ socksPortInfo.port
+ );
+ }
+ }
+
+ if (socksPortInfo.ipcFile || socksPortInfo.host || socksPortInfo.port) {
+ Services.prefs.setBoolPref("network.proxy.socks_remote_dns", true);
+ Services.prefs.setIntPref("network.proxy.type", 1);
+ }
+
+ // Force prefs to be synced to disk
+ Services.prefs.savePrefFile(null);
+ },
+
get shouldStartAndOwnTor() {
const kPrefStartTor = "extensions.torlauncher.start_tor";
try {
=====================================
toolkit/components/tor-launcher/TorMonitorService.sys.mjs
=====================================
@@ -13,6 +13,10 @@ import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs"
const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
+});
+
ChromeUtils.defineModuleGetter(
lazy,
"controller",
@@ -233,7 +237,10 @@ export const TorMonitorService = {
// TorProcess should be instanced once, then always reused and restarted
// only through the prompt it exposes when the controlled process dies.
if (!this._torProcess) {
- this._torProcess = new TorProcess();
+ this._torProcess = new TorProcess(
+ lazy.TorProtocolService.torControlPortInfo,
+ lazy.TorProtocolService.torSOCKSPortInfo
+ );
this._torProcess.onExit = () => {
this._shutDownEventMonitor();
Services.obs.notifyObservers(null, TorTopics.ProcessExited);
@@ -254,6 +261,7 @@ export const TorMonitorService = {
await this._torProcess.start();
if (this._torProcess.isRunning) {
logger.info("tor started");
+ this._torProcessStartTime = Date.now();
}
} catch (e) {
// TorProcess already logs the error.
=====================================
toolkit/components/tor-launcher/TorParsers.sys.mjs
=====================================
@@ -267,4 +267,18 @@ export const TorParsers = Object.freeze({
rv += aStr.substring(lastAdded, aStr.length - 1);
return rv;
},
+
+ parseBridgeLine(line) {
+ const re =
+ /\s*(?:(?<transport>\S+)\s+)?(?<addr>[0-9a-fA-F\.\[\]\:]+:\d{1,5})(?:\s+(?<id>[0-9a-fA-F]{40}))?(?:\s+(?<args>.+))?/;
+ const match = re.exec(line);
+ if (!match) {
+ throw new Error("Invalid bridge line.");
+ }
+ const bridge = match.groups;
+ if (!bridge.transport) {
+ bridge.transport = "vanilla";
+ }
+ return bridge;
+ },
});
=====================================
toolkit/components/tor-launcher/TorProcess.sys.mjs
=====================================
@@ -1,21 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
import { setTimeout } from "resource://gre/modules/Timer.sys.mjs";
import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
import { Subprocess } from "resource://gre/modules/Subprocess.sys.mjs";
const lazy = {};
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorProtocolService",
- "resource://gre/modules/TorProtocolService.jsm"
-);
-const { TorLauncherUtil } = ChromeUtils.import(
- "resource://gre/modules/TorLauncherUtil.jsm"
-);
-
-const { TorParsers } = ChromeUtils.import(
- "resource://gre/modules/TorParsers.jsm"
-);
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
+ TorParsers: "resource://gre/modules/TorParsers.sys.mjs",
+});
const TorProcessStatus = Object.freeze({
Unknown: 0,
@@ -30,53 +26,86 @@ const logger = new ConsoleAPI({
});
export class TorProcess {
- _exeFile = null;
- _dataDir = null;
- _args = [];
- _subprocess = null;
- _status = TorProcessStatus.Unknown;
- _torProcessStartTime = null; // JS Date.now()
- _didConnectToTorControlPort = false; // Have we ever made a connection?
+ #controlSettings;
+ #socksSettings;
+ #exeFile = null;
+ #dataDir = null;
+ #args = [];
+ #subprocess = null;
+ #status = TorProcessStatus.Unknown;
+ // Have we ever made a connection on the control port?
+ #didConnectToTorControlPort = false;
+
+ onExit = exitCode => {};
+ onRestart = () => {};
+
+ constructor(controlSettings, socksSettings) {
+ if (
+ controlSettings &&
+ !controlSettings.password &&
+ !controlSettings.cookieFilePath
+ ) {
+ throw new Error("Unauthenticated control port is not supported");
+ }
- onExit = null;
- onRestart = null;
+ const checkPort = port =>
+ port === undefined ||
+ (Number.isInteger(controlSettings.port) &&
+ controlSettings.port > 0 &&
+ controlSettings.port < 65535);
+ if (!checkPort(controlSettings?.port)) {
+ throw new Error("Invalid control port");
+ }
+ if (!checkPort(socksSettings.port)) {
+ throw new Error("Invalid port specified for the SOCKS port");
+ }
+
+ this.#controlSettings = { ...controlSettings };
+ const ipcFileToString = file =>
+ "unix:" + lazy.TorParsers.escapeString(file.path);
+ if (controlSettings.ipcFile) {
+ this.#controlSettings.ipcFile = ipcFileToString(controlSettings.ipcFile);
+ }
+ this.#socksSettings = { ...socksSettings };
+ if (socksSettings.ipcFile) {
+ this.#socksSettings.ipcFile = ipcFileToString(socksSettings.ipcFile);
+ }
+ }
get status() {
- return this._status;
+ return this.#status;
}
get isRunning() {
return (
- this._status === TorProcessStatus.Starting ||
- this._status === TorProcessStatus.Running
+ this.#status === TorProcessStatus.Starting ||
+ this.#status === TorProcessStatus.Running
);
}
async start() {
- if (this._subprocess) {
+ if (this.#subprocess) {
return;
}
- this._status = TorProcessStatus.Unknown;
+ this.#status = TorProcessStatus.Unknown;
try {
- this._makeArgs();
- this._addControlPortArg();
- this._addSocksPortArg();
+ this.#makeArgs();
+ this.#addControlPortArgs();
+ this.#addSocksPortArg();
const pid = Services.appinfo.processID;
if (pid !== 0) {
- this._args.push("__OwningControllerProcess");
- this._args.push("" + pid);
+ this.#args.push("__OwningControllerProcess", pid.toString());
}
- if (TorLauncherUtil.shouldShowNetworkSettings) {
- this._args.push("DisableNetwork");
- this._args.push("1");
+ if (lazy.TorLauncherUtil.shouldShowNetworkSettings) {
+ this.#args.push("DisableNetwork", "1");
}
- this._status = TorProcessStatus.Starting;
- this._didConnectToTorControlPort = false;
+ this.#status = TorProcessStatus.Starting;
+ this.#didConnectToTorControlPort = false;
// useful for simulating slow tor daemon launch
const kPrefTorDaemonLaunchDelay = "extensions.torlauncher.launch_delay";
@@ -88,29 +117,31 @@ export class TorProcess {
await new Promise(resolve => setTimeout(() => resolve(), launchDelay));
}
- logger.debug(`Starting ${this._exeFile.path}`, this._args);
+ logger.debug(`Starting ${this.#exeFile.path}`, this.#args);
const options = {
- command: this._exeFile.path,
- arguments: this._args,
+ command: this.#exeFile.path,
+ arguments: this.#args,
stderr: "stdout",
- workdir: TorLauncherUtil.getTorFile("pt-startup-dir", false).path,
+ workdir: lazy.TorLauncherUtil.getTorFile("pt-startup-dir", false).path,
};
- this._subprocess = await Subprocess.call(options);
- this._dumpStdout();
- this._watchProcess();
- this._status = TorProcessStatus.Running;
- this._torProcessStartTime = Date.now();
+ this.#subprocess = await Subprocess.call(options);
+ this.#status = TorProcessStatus.Running;
} catch (e) {
- this._status = TorProcessStatus.Exited;
- this._subprocess = null;
+ this.#status = TorProcessStatus.Exited;
+ this.#subprocess = null;
logger.error("startTor error:", e);
throw e;
}
+
+ // Do not await the following functions, as they will return only when the
+ // process exits.
+ this.#dumpStdout();
+ this.#watchProcess();
}
// Forget about a process.
//
- // Instead of killing the tor process, we rely on the TAKEOWNERSHIP feature
+ // Instead of killing the tor process, we rely on the TAKEOWNERSHIP feature
// to shut down tor when we close the control port connection.
//
// Previously, we sent a SIGNAL HALT command to the tor control port,
@@ -123,36 +154,38 @@ export class TorProcess {
// Still, before closing the owning connection, this class should forget about
// the process, so that future notifications will be ignored.
forget() {
- this._subprocess = null;
- this._status = TorProcessStatus.Exited;
+ this.#subprocess = null;
+ this.#status = TorProcessStatus.Exited;
}
// The owner of the process can use this function to tell us that they
// successfully connected to the control port. This information will be used
// only to decide which text to show in the confirmation dialog if tor exits.
connectionWorked() {
- this._didConnectToTorControlPort = true;
+ this.#didConnectToTorControlPort = true;
}
- async _dumpStdout() {
+ async #dumpStdout() {
let string;
while (
- this._subprocess &&
- (string = await this._subprocess.stdout.readString())
+ this.#subprocess &&
+ (string = await this.#subprocess.stdout.readString())
) {
dump(string);
}
}
- async _watchProcess() {
- const watched = this._subprocess;
+ async #watchProcess() {
+ const watched = this.#subprocess;
if (!watched) {
return;
}
+ let processExitCode;
try {
const { exitCode } = await watched.wait();
+ processExitCode = exitCode;
- if (watched !== this._subprocess) {
+ if (watched !== this.#subprocess) {
logger.debug(`A Tor process exited with code ${exitCode}.`);
} else if (exitCode) {
logger.warn(`The watched Tor process exited with code ${exitCode}.`);
@@ -163,30 +196,31 @@ export class TorProcess {
logger.error("Failed to watch the tor process", e);
}
- if (watched === this._subprocess) {
- this._processExitedUnexpectedly();
+ if (watched === this.#subprocess) {
+ this.#processExitedUnexpectedly(processExitCode);
}
}
- _processExitedUnexpectedly() {
- this._subprocess = null;
- this._status = TorProcessStatus.Exited;
+ #processExitedUnexpectedly(exitCode) {
+ this.#subprocess = null;
+ this.#status = TorProcessStatus.Exited;
// TODO: Move this logic somewhere else?
let s;
- if (!this._didConnectToTorControlPort) {
+ if (!this.#didConnectToTorControlPort) {
// tor might be misconfigured, becauser we could never connect to it
const key = "tor_exited_during_startup";
- s = TorLauncherUtil.getLocalizedString(key);
+ s = lazy.TorLauncherUtil.getLocalizedString(key);
} else {
// tor exited suddenly, so configuration should be okay
s =
- TorLauncherUtil.getLocalizedString("tor_exited") +
+ lazy.TorLauncherUtil.getLocalizedString("tor_exited") +
"\n\n" +
- TorLauncherUtil.getLocalizedString("tor_exited2");
+ lazy.TorLauncherUtil.getLocalizedString("tor_exited2");
}
logger.info(s);
- const defaultBtnLabel = TorLauncherUtil.getLocalizedString("restart_tor");
+ const defaultBtnLabel =
+ lazy.TorLauncherUtil.getLocalizedString("restart_tor");
let cancelBtnLabel = "OK";
try {
const kSysBundleURI = "chrome://global/locale/commonDialogs.properties";
@@ -196,51 +230,43 @@ export class TorProcess {
logger.warn("Could not localize the cancel button", e);
}
- const restart = TorLauncherUtil.showConfirm(
+ const restart = lazy.TorLauncherUtil.showConfirm(
null,
s,
defaultBtnLabel,
cancelBtnLabel
);
if (restart) {
- this.start().then(() => {
- if (this.onRestart) {
- this.onRestart();
- }
- });
- } else if (this.onExit) {
- this.onExit();
+ this.start().then(this.onRestart);
+ } else {
+ this.onExit(exitCode);
}
}
- _makeArgs() {
- // Ideally, we would cd to the Firefox application directory before
- // starting tor (but we don't know how to do that). Instead, we
- // rely on the TBB launcher to start Firefox from the right place.
-
+ #makeArgs() {
+ this.#exeFile = lazy.TorLauncherUtil.getTorFile("tor", false);
+ const torrcFile = lazy.TorLauncherUtil.getTorFile("torrc", true);
// Get the Tor data directory first so it is created before we try to
// construct paths to files that will be inside it.
- this._exeFile = TorLauncherUtil.getTorFile("tor", false);
- const torrcFile = TorLauncherUtil.getTorFile("torrc", true);
- this._dataDir = TorLauncherUtil.getTorFile("tordatadir", true);
- const onionAuthDir = TorLauncherUtil.getTorFile("toronionauthdir", true);
- const hashedPassword = lazy.TorProtocolService.torGetPassword(true);
+ this.#dataDir = lazy.TorLauncherUtil.getTorFile("tordatadir", true);
+ const onionAuthDir = lazy.TorLauncherUtil.getTorFile(
+ "toronionauthdir",
+ true
+ );
let detailsKey;
- if (!this._exeFile) {
+ if (!this.#exeFile) {
detailsKey = "tor_missing";
} else if (!torrcFile) {
detailsKey = "torrc_missing";
- } else if (!this._dataDir) {
+ } else if (!this.#dataDir) {
detailsKey = "datadir_missing";
} else if (!onionAuthDir) {
detailsKey = "onionauthdir_missing";
- } else if (!hashedPassword) {
- detailsKey = "password_hash_missing";
}
if (detailsKey) {
- const details = TorLauncherUtil.getLocalizedString(detailsKey);
+ const details = lazy.TorLauncherUtil.getLocalizedString(detailsKey);
const key = "unable_to_start_tor";
- const err = TorLauncherUtil.getFormattedLocalizedString(
+ const err = lazy.TorLauncherUtil.getFormattedLocalizedString(
key,
[details],
1
@@ -248,7 +274,7 @@ export class TorProcess {
throw new Error(err);
}
- const torrcDefaultsFile = TorLauncherUtil.getTorFile(
+ const torrcDefaultsFile = lazy.TorLauncherUtil.getTorFile(
"torrc-defaults",
false
);
@@ -258,77 +284,131 @@ export class TorProcess {
const geoip6File = torrcDefaultsFile.clone();
geoip6File.leafName = "geoip6";
- this._args = [];
+ this.#args = [];
if (torrcDefaultsFile) {
- this._args.push("--defaults-torrc");
- this._args.push(torrcDefaultsFile.path);
+ this.#args.push("--defaults-torrc", torrcDefaultsFile.path);
}
- this._args.push("-f");
- this._args.push(torrcFile.path);
- this._args.push("DataDirectory");
- this._args.push(this._dataDir.path);
- this._args.push("ClientOnionAuthDir");
- this._args.push(onionAuthDir.path);
- this._args.push("GeoIPFile");
- this._args.push(geoipFile.path);
- this._args.push("GeoIPv6File");
- this._args.push(geoip6File.path);
- this._args.push("HashedControlPassword");
- this._args.push(hashedPassword);
+ this.#args.push("-f", torrcFile.path);
+ this.#args.push("DataDirectory", this.#dataDir.path);
+ this.#args.push("ClientOnionAuthDir", onionAuthDir.path);
+ this.#args.push("GeoIPFile", geoipFile.path);
+ this.#args.push("GeoIPv6File", geoip6File.path);
}
- _addControlPortArg() {
- // Include a ControlPort argument to support switching between
- // a TCP port and an IPC port (e.g., a Unix domain socket). We
- // include a "+__" prefix so that (1) this control port is added
- // to any control ports that the user has defined in their torrc
- // file and (2) it is never written to torrc.
+ /**
+ * Add all the arguments related to the control port.
+ * We use the + prefix so that the the port is added to any other port already
+ * defined in the torrc, and the __ prefix so that it is never written to
+ * torrc.
+ */
+ #addControlPortArgs() {
+ if (!this.#controlSettings) {
+ return;
+ }
+
let controlPortArg;
- const controlIPCFile = lazy.TorProtocolService.torGetControlIPCFile();
- const controlPort = lazy.TorProtocolService.torGetControlPort();
- if (controlIPCFile) {
- controlPortArg = this._ipcPortArg(controlIPCFile);
- } else if (controlPort) {
- controlPortArg = "" + controlPort;
+ if (this.#controlSettings.ipcFile) {
+ controlPortArg = this.#controlSettings.ipcFile;
+ } else if (this.#controlSettings.port) {
+ controlPortArg = this.#controlSettings.host
+ ? `${this.#controlSettings.host}:${this.#controlSettings.port}`
+ : this.#controlSettings.port.toString();
}
if (controlPortArg) {
- this._args.push("+__ControlPort");
- this._args.push(controlPortArg);
+ this.#args.push("+__ControlPort", controlPortArg);
+ }
+
+ if (this.#controlSettings.password) {
+ this.#args.push(
+ "HashedControlPassword",
+ this.#hashPassword(this.#controlSettings.password)
+ );
+ }
+ if (this.#controlSettings.cookieFilePath) {
+ this.#args.push("CookieAuthentication", "1");
+ this.#args.push("CookieAuthFile", this.#controlSettings.cookieFilePath);
}
}
- _addSocksPortArg() {
- // Include a SocksPort argument to support switching between
- // a TCP port and an IPC port (e.g., a Unix domain socket). We
- // include a "+__" prefix so that (1) this SOCKS port is added
- // to any SOCKS ports that the user has defined in their torrc
- // file and (2) it is never written to torrc.
- const socksPortInfo = lazy.TorProtocolService.torGetSOCKSPortInfo();
- if (socksPortInfo) {
- let socksPortArg;
- if (socksPortInfo.ipcFile) {
- socksPortArg = this._ipcPortArg(socksPortInfo.ipcFile);
- } else if (socksPortInfo.host && socksPortInfo.port != 0) {
- socksPortArg = socksPortInfo.host + ":" + socksPortInfo.port;
- }
- if (socksPortArg) {
- let socksPortFlags = Services.prefs.getCharPref(
- "extensions.torlauncher.socks_port_flags",
- "IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth"
- );
- if (socksPortFlags) {
- socksPortArg += " " + socksPortFlags;
- }
- this._args.push("+__SocksPort");
- this._args.push(socksPortArg);
+ /**
+ * Add the argument related to the control port.
+ * We use the + prefix so that the the port is added to any other port already
+ * defined in the torrc, and the __ prefix so that it is never written to
+ * torrc.
+ */
+ #addSocksPortArg() {
+ let socksPortArg;
+ if (this.#socksSettings.ipcFile) {
+ socksPortArg = this.#socksSettings.ipcFile;
+ } else if (this.#socksSettings.port != 0) {
+ socksPortArg = this.#socksSettings.host
+ ? `${this.#socksSettings.host}:${this.#socksSettings.port}`
+ : this.#socksSettings.port.toString();
+ }
+ if (socksPortArg) {
+ const socksPortFlags = Services.prefs.getCharPref(
+ "extensions.torlauncher.socks_port_flags",
+ "IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth"
+ );
+ if (socksPortFlags) {
+ socksPortArg += " " + socksPortFlags;
}
+ this.#args.push("+__SocksPort", socksPortArg);
+ }
+ }
+
+ // Based on Vidalia's TorSettings::hashPassword().
+ #hashPassword(aHexPassword) {
+ if (!aHexPassword) {
+ return null;
}
+
+ // Generate a random, 8 byte salt value.
+ const salt = Array.from(crypto.getRandomValues(new Uint8Array(8)));
+
+ // Convert hex-encoded password to an array of bytes.
+ const password = [];
+ for (let i = 0; i < aHexPassword.length; i += 2) {
+ password.push(parseInt(aHexPassword.substring(i, i + 2), 16));
+ }
+
+ // Run through the S2K algorithm and convert to a string.
+ const toHex = v => v.toString(16).padStart(2, "0");
+ const arrayToHex = aArray => aArray.map(toHex).join("");
+ const kCodedCount = 96;
+ const hashVal = this.#cryptoSecretToKey(password, salt, kCodedCount);
+ return "16:" + arrayToHex(salt) + toHex(kCodedCount) + arrayToHex(hashVal);
}
- // Return a ControlPort or SocksPort argument for aIPCFile (an nsIFile).
- // The result is unix:/path or unix:"/path with spaces" with appropriate
- // C-style escaping within the path portion.
- _ipcPortArg(aIPCFile) {
- return "unix:" + TorParsers.escapeString(aIPCFile.path);
+ // #cryptoSecretToKey() is similar to Vidalia's crypto_secret_to_key().
+ // It generates and returns a hash of aPassword by following the iterated
+ // and salted S2K algorithm (see RFC 2440 section 3.6.1.3).
+ // See also https://gitlab.torproject.org/tpo/core/torspec/-/blob/main/control-spec.txt….
+ // Returns an array of bytes.
+ #cryptoSecretToKey(aPassword, aSalt, aCodedCount) {
+ const inputArray = aSalt.concat(aPassword);
+
+ // Subtle crypto only has the final digest, and does not allow incremental
+ // updates.
+ const hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
+ Ci.nsICryptoHash
+ );
+ hasher.init(hasher.SHA1);
+ const kEXPBIAS = 6;
+ let count = (16 + (aCodedCount & 15)) << ((aCodedCount >> 4) + kEXPBIAS);
+ while (count > 0) {
+ if (count > inputArray.length) {
+ hasher.update(inputArray, inputArray.length);
+ count -= inputArray.length;
+ } else {
+ const finalArray = inputArray.slice(0, count);
+ hasher.update(finalArray, finalArray.length);
+ count = 0;
+ }
+ }
+ return hasher
+ .finish(false)
+ .split("")
+ .map(b => b.charCodeAt(0));
}
}
=====================================
toolkit/components/tor-launcher/TorProtocolService.sys.mjs
=====================================
@@ -289,9 +289,8 @@ export const TorProtocolService = {
// are also used in torbutton.
// Returns Tor password string or null if an error occurs.
- torGetPassword(aPleaseHash) {
- const pw = this._controlPassword;
- return aPleaseHash ? this._hashPassword(pw) : pw;
+ torGetPassword() {
+ return this._controlPassword;
},
torGetControlIPCFile() {
@@ -306,6 +305,24 @@ export const TorProtocolService = {
return this._SOCKSPortInfo;
},
+ get torControlPortInfo() {
+ const info = {
+ password: this._controlPassword,
+ };
+ if (this._controlIPCFile) {
+ info.ipcFile = this._controlIPCFile?.clone();
+ }
+ if (this._controlPort) {
+ info.host = this._controlHost;
+ info.port = this._controlPort;
+ }
+ return info;
+ },
+
+ get torSOCKSPortInfo() {
+ return this._SOCKSPortInfo;
+ },
+
// Public, but called only internally
// Executes a command on the control port.
@@ -469,115 +486,8 @@ export const TorProtocolService = {
this._controlPassword = this._generateRandomPassword();
}
- // Determine what kind of SOCKS port Tor and the browser will use.
- // On Windows (where Unix domain sockets are not supported), TCP is
- // always used.
- //
- // The following environment variables are supported and take
- // precedence over preferences:
- // TOR_SOCKS_IPC_PATH (file system path; ignored on Windows)
- // TOR_SOCKS_HOST
- // TOR_SOCKS_PORT
- //
- // The following preferences are consulted:
- // network.proxy.socks
- // network.proxy.socks_port
- // extensions.torlauncher.socks_port_use_ipc (Boolean)
- // extensions.torlauncher.socks_ipc_path (file system path)
- // If extensions.torlauncher.socks_ipc_path is empty, a default
- // path is used (<tor-data-directory>/socks.socket).
- //
- // When using TCP, if a value is not defined via an env variable it is
- // taken from the corresponding browser preference if possible. The
- // exceptions are:
- // If network.proxy.socks contains a file: URL, a default value of
- // "127.0.0.1" is used instead.
- // If the network.proxy.socks_port value is 0, a default value of
- // 9150 is used instead.
- //
- // Supported scenarios:
- // 1. By default, an IPC object at a default path is used.
- // 2. If extensions.torlauncher.socks_port_use_ipc is set to false,
- // a TCP socket at 127.0.0.1:9150 is used, unless different values
- // are set in network.proxy.socks and network.proxy.socks_port.
- // 3. If the TOR_SOCKS_IPC_PATH env var is set, an IPC object at that
- // path is used (e.g., a Unix domain socket).
- // 4. If the TOR_SOCKS_HOST and/or TOR_SOCKS_PORT env vars are set, TCP
- // is used. Values not set via env vars will be taken from the
- // network.proxy.socks and network.proxy.socks_port prefs as described
- // above.
- // 5. If extensions.torlauncher.socks_port_use_ipc is true and
- // extensions.torlauncher.socks_ipc_path is set, an IPC object at
- // the specified path is used.
- // 6. Tor Launcher is disabled. Torbutton will respect the env vars if
- // present; if not, the values in network.proxy.socks and
- // network.proxy.socks_port are used without modification.
-
- let useIPC;
- this._SOCKSPortInfo = { ipcFile: undefined, host: undefined, port: 0 };
- if (!isWindows && Services.env.exists("TOR_SOCKS_IPC_PATH")) {
- let ipcPath = Services.env.get("TOR_SOCKS_IPC_PATH");
- this._SOCKSPortInfo.ipcFile = new lazy.FileUtils.File(ipcPath);
- useIPC = true;
- } else {
- // Check for TCP host and port environment variables.
- if (Services.env.exists("TOR_SOCKS_HOST")) {
- this._SOCKSPortInfo.host = Services.env.get("TOR_SOCKS_HOST");
- useIPC = false;
- }
- if (Services.env.exists("TOR_SOCKS_PORT")) {
- this._SOCKSPortInfo.port = parseInt(
- Services.env.get("TOR_SOCKS_PORT"),
- 10
- );
- useIPC = false;
- }
- }
-
- if (useIPC === undefined) {
- useIPC =
- !isWindows &&
- Services.prefs.getBoolPref(
- "extensions.torlauncher.socks_port_use_ipc",
- false
- );
- }
-
- // Fill in missing SOCKS info from prefs.
- if (useIPC) {
- if (!this._SOCKSPortInfo.ipcFile) {
- this._SOCKSPortInfo.ipcFile = TorLauncherUtil.getTorFile(
- "socks_ipc",
- false
- );
- }
- } else {
- if (!this._SOCKSPortInfo.host) {
- let socksAddr = Services.prefs.getCharPref(
- "network.proxy.socks",
- "127.0.0.1"
- );
- let socksAddrHasHost = socksAddr && !socksAddr.startsWith("file:");
- this._SOCKSPortInfo.host = socksAddrHasHost ? socksAddr : "127.0.0.1";
- }
-
- if (!this._SOCKSPortInfo.port) {
- let socksPort = Services.prefs.getIntPref(
- "network.proxy.socks_port",
- 0
- );
- // This pref is set as 0 by default in Firefox, use 9150 if we get 0.
- this._SOCKSPortInfo.port = socksPort != 0 ? socksPort : 9150;
- }
- }
-
- logger.info("SOCKS port type: " + (useIPC ? "IPC" : "TCP"));
- if (useIPC) {
- logger.info(`ipcFile: ${this._SOCKSPortInfo.ipcFile.path}`);
- } else {
- logger.info(`SOCKS host: ${this._SOCKSPortInfo.host}`);
- logger.info(`SOCKS port: ${this._SOCKSPortInfo.port}`);
- }
+ this._SOCKSPortInfo = TorLauncherUtil.getPreferredSocksConfiguration();
+ TorLauncherUtil.setProxyConfiguration(this._SOCKSPortInfo);
// Set the global control port info parameters.
// These values may be overwritten by torbutton when it initializes, but
@@ -781,38 +691,6 @@ export const TorProtocolService = {
return pwd;
},
- // Based on Vidalia's TorSettings::hashPassword().
- _hashPassword(aHexPassword) {
- if (!aHexPassword) {
- return null;
- }
-
- // Generate a random, 8 byte salt value.
- const salt = Array.from(crypto.getRandomValues(new Uint8Array(8)));
-
- // Convert hex-encoded password to an array of bytes.
- const password = [];
- for (let i = 0; i < aHexPassword.length; i += 2) {
- password.push(parseInt(aHexPassword.substring(i, i + 2), 16));
- }
-
- // Run through the S2K algorithm and convert to a string.
- const kCodedCount = 96;
- const hashVal = this._cryptoSecretToKey(password, salt, kCodedCount);
- if (!hashVal) {
- logger.error("_cryptoSecretToKey() failed");
- return null;
- }
-
- const arrayToHex = aArray =>
- aArray.map(item => this._toHex(item, 2)).join("");
- let rv = "16:";
- rv += arrayToHex(salt);
- rv += this._toHex(kCodedCount, 2);
- rv += arrayToHex(hashVal);
- return rv;
- },
-
// Returns -1 upon failure.
_cryptoRandInt(aMax) {
// Based on tor's crypto_rand_int().
@@ -831,43 +709,6 @@ export const TorProtocolService = {
return val % aMax;
},
- // _cryptoSecretToKey() is similar to Vidalia's crypto_secret_to_key().
- // It generates and returns a hash of aPassword by following the iterated
- // and salted S2K algorithm (see RFC 2440 section 3.6.1.3).
- // Returns an array of bytes.
- _cryptoSecretToKey(aPassword, aSalt, aCodedCount) {
- if (!aPassword || !aSalt) {
- return null;
- }
-
- const inputArray = aSalt.concat(aPassword);
-
- // Subtle crypto only has the final digest, and does not allow incremental
- // updates. Also, it is async, so we should hash and keep the hash in a
- // variable if we wanted to switch to getters.
- // So, keeping this implementation should be okay for now.
- const hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
- Ci.nsICryptoHash
- );
- hasher.init(hasher.SHA1);
- const kEXPBIAS = 6;
- let count = (16 + (aCodedCount & 15)) << ((aCodedCount >> 4) + kEXPBIAS);
- while (count > 0) {
- if (count > inputArray.length) {
- hasher.update(inputArray, inputArray.length);
- count -= inputArray.length;
- } else {
- const finalArray = inputArray.slice(0, count);
- hasher.update(finalArray, finalArray.length);
- count = 0;
- }
- }
- return hasher
- .finish(false)
- .split("")
- .map(b => b.charCodeAt(0));
- },
-
_toHex(aValue, aMinLen) {
return aValue.toString(16).padStart(aMinLen, "0");
},
=====================================
toolkit/torbutton/chrome/content/torbutton.js
=====================================
@@ -60,7 +60,7 @@ var torbutton_init;
} else {
try {
// Try to get password from Tor Launcher.
- m_tb_control_pass = TorProtocolService.torGetPassword(false);
+ m_tb_control_pass = TorProtocolService.torGetPassword();
} catch (e) {}
}
=====================================
toolkit/torbutton/components.conf
=====================================
@@ -1,12 +1,4 @@
Classes = [
- {
- "cid": "{06322def-6fde-4c06-aef6-47ae8e799629}",
- "contract_ids": [
- "@torproject.org/startup-observer;1"
- ],
- "jsm": "resource://torbutton/modules/TorbuttonStartupObserver.jsm",
- "constructor": "StartupObserver",
- },
{
"cid": "{f36d72c9-9718-4134-b550-e109638331d7}",
"contract_ids": [
=====================================
toolkit/torbutton/modules/TorbuttonStartupObserver.jsm deleted
=====================================
@@ -1,138 +0,0 @@
-// Bug 1506 P1-3: This code is mostly hackish remnants of session store
-// support. There are a couple of observer events that *might* be worth
-// listening to. Search for 1506 in the code.
-
-/*************************************************************************
- * Startup observer (JavaScript XPCOM component)
- *
- * Cases tested (each during Tor and Non-Tor, FF4 and FF3.6)
- * 1. Crash
- * 2. Upgrade
- * 3. Fresh install
- *
- *************************************************************************/
-
-var EXPORTED_SYMBOLS = ["StartupObserver"];
-
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const { XPCOMUtils } = ChromeUtils.import(
- "resource://gre/modules/XPCOMUtils.jsm"
-);
-
-const { TorProtocolService } = ChromeUtils.import(
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
-const lazy = {};
-
-XPCOMUtils.defineLazyModuleGetters(lazy, {
- FileUtils: "resource://gre/modules/FileUtils.jsm",
-});
-
-function StartupObserver() {
- this.logger = Cc["@torproject.org/torbutton-logger;1"].getService(
- Ci.nsISupports
- ).wrappedJSObject;
- this._prefs = Services.prefs;
- this.logger.log(3, "Startup Observer created");
-
- try {
- // XXX: We're in a race with HTTPS-Everywhere to update our proxy settings
- // before the initial SSL-Observatory test... If we lose the race, Firefox
- // caches the old proxy settings for check.tp.o somehwere, and it never loads :(
- this.setProxySettings();
- } catch (e) {
- this.logger.log(
- 4,
- "Early proxy change failed. Will try again at profile load. Error: " + e
- );
- }
-}
-
-StartupObserver.prototype = {
- // Bug 6803: We need to get the env vars early due to
- // some weird proxy caching code that showed up in FF15.
- // Otherwise, homepage domain loads fail forever.
- setProxySettings() {
- // Bug 1506: Still want to get these env vars
- if (Services.env.exists("TOR_TRANSPROXY")) {
- this.logger.log(3, "Resetting Tor settings to transproxy");
- this._prefs.setBoolPref("network.proxy.socks_remote_dns", false);
- this._prefs.setIntPref("network.proxy.type", 0);
- this._prefs.setIntPref("network.proxy.socks_port", 0);
- this._prefs.setCharPref("network.proxy.socks", "");
- } else {
- // Try to retrieve SOCKS proxy settings from Tor Launcher.
- let socksPortInfo;
- try {
- socksPortInfo = TorProtocolService.torGetSOCKSPortInfo();
- } catch (e) {
- this.logger.log(3, "tor launcher failed " + e);
- }
-
- // If Tor Launcher is not available, check environment variables.
- if (!socksPortInfo) {
- socksPortInfo = { ipcFile: undefined, host: undefined, port: 0 };
-
- let isWindows = Services.appinfo.OS === "WINNT";
- if (!isWindows && Services.env.exists("TOR_SOCKS_IPC_PATH")) {
- socksPortInfo.ipcFile = new lazy.FileUtils.File(
- Services.env.get("TOR_SOCKS_IPC_PATH")
- );
- } else {
- if (Services.env.exists("TOR_SOCKS_HOST")) {
- socksPortInfo.host = Services.env.get("TOR_SOCKS_HOST");
- }
- if (Services.env.exists("TOR_SOCKS_PORT")) {
- socksPortInfo.port = parseInt(Services.env.get("TOR_SOCKS_PORT"));
- }
- }
- }
-
- // Adjust network.proxy prefs.
- if (socksPortInfo.ipcFile) {
- let fph = Services.io
- .getProtocolHandler("file")
- .QueryInterface(Ci.nsIFileProtocolHandler);
- let fileURI = fph.newFileURI(socksPortInfo.ipcFile);
- this.logger.log(3, "Reset socks to " + fileURI.spec);
- this._prefs.setCharPref("network.proxy.socks", fileURI.spec);
- this._prefs.setIntPref("network.proxy.socks_port", 0);
- } else {
- if (socksPortInfo.host) {
- this._prefs.setCharPref("network.proxy.socks", socksPortInfo.host);
- this.logger.log(3, "Reset socks host to " + socksPortInfo.host);
- }
- if (socksPortInfo.port) {
- this._prefs.setIntPref(
- "network.proxy.socks_port",
- socksPortInfo.port
- );
- this.logger.log(3, "Reset socks port to " + socksPortInfo.port);
- }
- }
-
- if (socksPortInfo.ipcFile || socksPortInfo.host || socksPortInfo.port) {
- this._prefs.setBoolPref("network.proxy.socks_remote_dns", true);
- this._prefs.setIntPref("network.proxy.type", 1);
- }
- }
-
- // Force prefs to be synced to disk
- Services.prefs.savePrefFile(null);
-
- this.logger.log(3, "Synced network settings to environment.");
- },
-
- observe(subject, topic, data) {
- if (topic == "profile-after-change") {
- this.setProxySettings();
- }
-
- // In all cases, force prefs to be synced to disk
- Services.prefs.savePrefFile(null);
- },
-
- // Hack to get us registered early to observe recovery
- _xpcom_categories: [{ category: "profile-after-change" }],
-};
=====================================
toolkit/torbutton/moz.build
=====================================
@@ -8,7 +8,3 @@ JAR_MANIFESTS += ['jar.mn']
XPCOM_MANIFESTS += [
"components.conf",
]
-
-EXTRA_COMPONENTS += [
- "torbutton.manifest",
-]
=====================================
toolkit/torbutton/torbutton.manifest deleted
=====================================
@@ -1 +0,0 @@
-category profile-after-change StartupObserver @torproject.org/startup-observer;1
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/f0493f…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/f0493f…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-115.0.2esr-13.0-1] Deleted 2 commits: Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
by richard (@richard) 31 Jul '23
by richard (@richard) 31 Jul '23
31 Jul '23
richard pushed to branch base-browser-115.0.2esr-13.0-1 at The Tor Project / Applications / Tor Browser
WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below.
Deleted commits:
60fefea4 by Edgar Chen at 2023-07-31T17:56:34+00:00
Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
Fullscreen/PointerLock warnings are initialized with hidden="true", but
change to hidden="" after being shown and hidden again. I think this
started happening when we began using HTML elements instead of XUL as
they handle hidden attribute differently.
Differential Revision: https://phabricator.services.mozilla.com/D177790
- - - - -
524c7622 by Edgar Chen at 2023-07-31T17:56:38+00:00
Bug 1821884 - Reshow initial fullscreen notification; r=Gijs
Depends on D177790
Differential Revision: https://phabricator.services.mozilla.com/D178339
- - - - -
4 changed files:
- browser/base/content/browser-fullScreenAndPointerLock.js
- browser/base/content/fullscreen-and-pointerlock.inc.xhtml
- browser/base/content/test/fullscreen/browser_fullscreen_warning.js
- dom/tests/browser/browser_pointerlock_warning.js
Changes:
=====================================
browser/base/content/browser-fullScreenAndPointerLock.js
=====================================
@@ -62,9 +62,14 @@ var PointerlockFsWarning = {
this._element = document.getElementById(elementId);
// Setup event listeners
this._element.addEventListener("transitionend", this);
+ this._element.addEventListener("transitioncancel", this);
window.addEventListener("mousemove", this, true);
+ window.addEventListener("activate", this);
+ window.addEventListener("deactivate", this);
// The timeout to hide the warning box after a while.
this._timeoutHide = new this.Timeout(() => {
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
this._state = "hidden";
}, timeout);
// The timeout to show the warning box when the pointer is at the top
@@ -116,11 +121,10 @@ var PointerlockFsWarning = {
return;
}
- // Explicitly set the last state to hidden to avoid the warning
- // box being hidden immediately because of mousemove.
- this._state = "onscreen";
- this._lastState = "hidden";
- this._timeoutHide.start();
+ if (Services.focus.activeWindow == window) {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ }
},
/**
@@ -148,7 +152,10 @@ var PointerlockFsWarning = {
this._element.hidden = true;
// Remove all event listeners
this._element.removeEventListener("transitionend", this);
+ this._element.removeEventListener("transitioncancel", this);
window.removeEventListener("mousemove", this, true);
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
// Clear fields
this._element = null;
this._timeoutHide = null;
@@ -186,7 +193,7 @@ var PointerlockFsWarning = {
}
if (newState != "hidden") {
if (currentState != "hidden") {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
} else {
// When the previous state is hidden, the display was none,
// thus no box was constructed. We need to wait for the new
@@ -197,7 +204,7 @@ var PointerlockFsWarning = {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (this._element) {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
}
});
});
@@ -217,7 +224,7 @@ var PointerlockFsWarning = {
} else if (this._timeoutShow.delay >= 0) {
this._timeoutShow.start();
}
- } else {
+ } else if (state != "onscreen") {
let elemRect = this._element.getBoundingClientRect();
if (state == "hiding" && this._lastState != "hidden") {
// If we are on the hiding transition, and the pointer
@@ -239,12 +246,23 @@ var PointerlockFsWarning = {
}
break;
}
- case "transitionend": {
+ case "transitionend":
+ case "transitioncancel": {
if (this._state == "hiding") {
this._element.hidden = true;
}
break;
}
+ case "activate": {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ break;
+ }
+ case "deactivate": {
+ this._state = "hidden";
+ this._timeoutHide.cancel();
+ break;
+ }
}
},
};
=====================================
browser/base/content/fullscreen-and-pointerlock.inc.xhtml
=====================================
@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<html:div id="fullscreen-and-pointerlock-wrapper">
- <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
@@ -20,7 +20,7 @@
</html:button>
</html:div>
- <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
=====================================
browser/base/content/test/fullscreen/browser_fullscreen_warning.js
=====================================
@@ -3,14 +3,35 @@
"use strict";
-add_task(async function test_fullscreen_display_none() {
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
+add_setup(async function init() {
await SpecialPowers.pushPrefEnv({
set: [
["full-screen-api.enabled", true],
["full-screen-api.allow-trusted-requests-only", false],
],
});
+});
+add_task(async function test_fullscreen_display_none() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
@@ -30,11 +51,13 @@ add_task(async function test_fullscreen_display_none() {
},
async function (browser) {
let warning = document.getElementById("fullscreen-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
warning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
);
+
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
// Enter fullscreen
await SpecialPowers.spawn(browser, [], async () => {
let frame = content.document.querySelector("iframe");
@@ -54,39 +77,33 @@ add_task(async function test_fullscreen_display_none() {
);
document.getElementById("fullscreen-exit-button").click();
await exitFullscreenPromise;
+
+ checkWarningState(
+ warning,
+ "hidden",
+ "Should hide fullscreen warning after exiting fullscreen"
+ );
}
);
});
add_task(async function test_fullscreen_pointerlock_conflict() {
- await SpecialPowers.pushPrefEnv({
- set: [
- ["full-screen-api.enabled", true],
- ["full-screen-api.allow-trusted-requests-only", false],
- ],
- });
-
await BrowserTestUtils.withNewTab("https://example.com", async browser => {
let fsWarning = document.getElementById("fullscreen-warning");
let plWarning = document.getElementById("pointerlock-warning");
- is(
- fsWarning.getAttribute("onscreen"),
- null,
- "Should not show full screen warning initially."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning initially."
- );
-
- let fsWarningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
fsWarning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
+ );
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning initially"
);
+ let fsWarningShownPromise = waitForWarningState(fsWarning, "onscreen");
info("Entering full screen and pointer lock.");
await SpecialPowers.spawn(browser, [], async () => {
await content.document.body.requestFullscreen();
@@ -94,15 +111,10 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
});
await fsWarningShownPromise;
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should show full screen warning."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
info("Exiting pointerlock");
@@ -110,18 +122,19 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
await content.document.exitPointerLock();
});
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should still show full screen warning."
+ checkWarningState(
+ fsWarning,
+ "onscreen",
+ "Should still show full screen warning"
);
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
// Cleanup
+ info("Exiting fullscreen");
await document.exitFullscreen();
});
});
=====================================
dom/tests/browser/browser_pointerlock_warning.js
=====================================
@@ -15,6 +15,25 @@ const FRAME_TEST_URL =
encodeURI(BODY_URL) +
'"></iframe></body>';
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
// Make sure the pointerlock warning is shown and exited with the escape key
add_task(async function show_pointerlock_warning_escape() {
let urls = [TEST_URL, FRAME_TEST_URL];
@@ -24,11 +43,7 @@ add_task(async function show_pointerlock_warning_escape() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
let warning = document.getElementById("pointerlock-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
- warning,
- "true"
- );
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
let expectedWarningText;
@@ -49,11 +64,7 @@ add_task(async function show_pointerlock_warning_escape() {
ok(true, "Pointerlock warning shown");
- let warningHiddenPromise = BrowserTestUtils.waitForAttribute(
- "hidden",
- warning,
- ""
- );
+ let warningHiddenPromise = waitForWarningState(warning, "hidden");
await BrowserTestUtils.waitForCondition(
() => warning.innerText == expectedWarningText,
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/f3faeb…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/f3faeb…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.0.2esr-13.0-1] Deleted 2 commits: Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
by richard (@richard) 31 Jul '23
by richard (@richard) 31 Jul '23
31 Jul '23
richard pushed to branch tor-browser-115.0.2esr-13.0-1 at The Tor Project / Applications / Tor Browser
WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below.
Deleted commits:
6a6c68cf by Edgar Chen at 2023-07-31T17:56:02+00:00
Bug 1821884 - Ensure consistent state for fullscreen/pointerlock warnings; r=Gijs
Fullscreen/PointerLock warnings are initialized with hidden="true", but
change to hidden="" after being shown and hidden again. I think this
started happening when we began using HTML elements instead of XUL as
they handle hidden attribute differently.
Differential Revision: https://phabricator.services.mozilla.com/D177790
- - - - -
5e8c1b08 by Edgar Chen at 2023-07-31T17:56:07+00:00
Bug 1821884 - Reshow initial fullscreen notification; r=Gijs
Depends on D177790
Differential Revision: https://phabricator.services.mozilla.com/D178339
- - - - -
4 changed files:
- browser/base/content/browser-fullScreenAndPointerLock.js
- browser/base/content/fullscreen-and-pointerlock.inc.xhtml
- browser/base/content/test/fullscreen/browser_fullscreen_warning.js
- dom/tests/browser/browser_pointerlock_warning.js
Changes:
=====================================
browser/base/content/browser-fullScreenAndPointerLock.js
=====================================
@@ -62,9 +62,14 @@ var PointerlockFsWarning = {
this._element = document.getElementById(elementId);
// Setup event listeners
this._element.addEventListener("transitionend", this);
+ this._element.addEventListener("transitioncancel", this);
window.addEventListener("mousemove", this, true);
+ window.addEventListener("activate", this);
+ window.addEventListener("deactivate", this);
// The timeout to hide the warning box after a while.
this._timeoutHide = new this.Timeout(() => {
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
this._state = "hidden";
}, timeout);
// The timeout to show the warning box when the pointer is at the top
@@ -116,11 +121,10 @@ var PointerlockFsWarning = {
return;
}
- // Explicitly set the last state to hidden to avoid the warning
- // box being hidden immediately because of mousemove.
- this._state = "onscreen";
- this._lastState = "hidden";
- this._timeoutHide.start();
+ if (Services.focus.activeWindow == window) {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ }
},
/**
@@ -148,7 +152,10 @@ var PointerlockFsWarning = {
this._element.hidden = true;
// Remove all event listeners
this._element.removeEventListener("transitionend", this);
+ this._element.removeEventListener("transitioncancel", this);
window.removeEventListener("mousemove", this, true);
+ window.removeEventListener("activate", this);
+ window.removeEventListener("deactivate", this);
// Clear fields
this._element = null;
this._timeoutHide = null;
@@ -186,7 +193,7 @@ var PointerlockFsWarning = {
}
if (newState != "hidden") {
if (currentState != "hidden") {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
} else {
// When the previous state is hidden, the display was none,
// thus no box was constructed. We need to wait for the new
@@ -197,7 +204,7 @@ var PointerlockFsWarning = {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (this._element) {
- this._element.setAttribute(newState, true);
+ this._element.setAttribute(newState, "");
}
});
});
@@ -217,7 +224,7 @@ var PointerlockFsWarning = {
} else if (this._timeoutShow.delay >= 0) {
this._timeoutShow.start();
}
- } else {
+ } else if (state != "onscreen") {
let elemRect = this._element.getBoundingClientRect();
if (state == "hiding" && this._lastState != "hidden") {
// If we are on the hiding transition, and the pointer
@@ -239,12 +246,23 @@ var PointerlockFsWarning = {
}
break;
}
- case "transitionend": {
+ case "transitionend":
+ case "transitioncancel": {
if (this._state == "hiding") {
this._element.hidden = true;
}
break;
}
+ case "activate": {
+ this._state = "onscreen";
+ this._timeoutHide.start();
+ break;
+ }
+ case "deactivate": {
+ this._state = "hidden";
+ this._timeoutHide.cancel();
+ break;
+ }
}
},
};
=====================================
browser/base/content/fullscreen-and-pointerlock.inc.xhtml
=====================================
@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<html:div id="fullscreen-and-pointerlock-wrapper">
- <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
@@ -20,7 +20,7 @@
</html:button>
</html:div>
- <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true">
+ <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="">
<html:div class="pointerlockfswarning-domain-text">
<html:span class="pointerlockfswarning-domain" data-l10n-name="domain"/>
</html:div>
=====================================
browser/base/content/test/fullscreen/browser_fullscreen_warning.js
=====================================
@@ -3,14 +3,35 @@
"use strict";
-add_task(async function test_fullscreen_display_none() {
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
+add_setup(async function init() {
await SpecialPowers.pushPrefEnv({
set: [
["full-screen-api.enabled", true],
["full-screen-api.allow-trusted-requests-only", false],
],
});
+});
+add_task(async function test_fullscreen_display_none() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
@@ -30,11 +51,13 @@ add_task(async function test_fullscreen_display_none() {
},
async function (browser) {
let warning = document.getElementById("fullscreen-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
warning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
);
+
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
// Enter fullscreen
await SpecialPowers.spawn(browser, [], async () => {
let frame = content.document.querySelector("iframe");
@@ -54,39 +77,33 @@ add_task(async function test_fullscreen_display_none() {
);
document.getElementById("fullscreen-exit-button").click();
await exitFullscreenPromise;
+
+ checkWarningState(
+ warning,
+ "hidden",
+ "Should hide fullscreen warning after exiting fullscreen"
+ );
}
);
});
add_task(async function test_fullscreen_pointerlock_conflict() {
- await SpecialPowers.pushPrefEnv({
- set: [
- ["full-screen-api.enabled", true],
- ["full-screen-api.allow-trusted-requests-only", false],
- ],
- });
-
await BrowserTestUtils.withNewTab("https://example.com", async browser => {
let fsWarning = document.getElementById("fullscreen-warning");
let plWarning = document.getElementById("pointerlock-warning");
- is(
- fsWarning.getAttribute("onscreen"),
- null,
- "Should not show full screen warning initially."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning initially."
- );
-
- let fsWarningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
+ checkWarningState(
fsWarning,
- "true"
+ "hidden",
+ "Should not show full screen warning initially"
+ );
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning initially"
);
+ let fsWarningShownPromise = waitForWarningState(fsWarning, "onscreen");
info("Entering full screen and pointer lock.");
await SpecialPowers.spawn(browser, [], async () => {
await content.document.body.requestFullscreen();
@@ -94,15 +111,10 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
});
await fsWarningShownPromise;
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should show full screen warning."
- );
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
info("Exiting pointerlock");
@@ -110,18 +122,19 @@ add_task(async function test_fullscreen_pointerlock_conflict() {
await content.document.exitPointerLock();
});
- is(
- fsWarning.getAttribute("onscreen"),
- "true",
- "Should still show full screen warning."
+ checkWarningState(
+ fsWarning,
+ "onscreen",
+ "Should still show full screen warning"
);
- is(
- plWarning.getAttribute("onscreen"),
- null,
- "Should not show pointer lock warning."
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
);
// Cleanup
+ info("Exiting fullscreen");
await document.exitFullscreen();
});
});
=====================================
dom/tests/browser/browser_pointerlock_warning.js
=====================================
@@ -15,6 +15,25 @@ const FRAME_TEST_URL =
encodeURI(BODY_URL) +
'"></iframe></body>';
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
// Make sure the pointerlock warning is shown and exited with the escape key
add_task(async function show_pointerlock_warning_escape() {
let urls = [TEST_URL, FRAME_TEST_URL];
@@ -24,11 +43,7 @@ add_task(async function show_pointerlock_warning_escape() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
let warning = document.getElementById("pointerlock-warning");
- let warningShownPromise = BrowserTestUtils.waitForAttribute(
- "onscreen",
- warning,
- "true"
- );
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
let expectedWarningText;
@@ -49,11 +64,7 @@ add_task(async function show_pointerlock_warning_escape() {
ok(true, "Pointerlock warning shown");
- let warningHiddenPromise = BrowserTestUtils.waitForAttribute(
- "hidden",
- warning,
- ""
- );
+ let warningHiddenPromise = waitForWarningState(warning, "hidden");
await BrowserTestUtils.waitForCondition(
() => warning.innerText == expectedWarningText,
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/93ef5b…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/93ef5b…
You're receiving this email because of your account on gitlab.torproject.org.
1
0