brizental pushed to branch tor-browser-153.4.0esr-16.0-1 at The Tor Project / Applications / Tor Browser

Commits:

4 changed files:

Changes:

  • services/settings/dumps/main/moz.build
    ... ... @@ -28,3 +28,11 @@ if not CONFIG["MOZ_BUILD_APP"].startswith("mobile/"):
    28 28
             "websites-with-shared-credential-backends.json",
    
    29 29
         ]
    
    30 30
     
    
    31
    +# tor-browser#44154: This file is not required by tor-browser.
    
    32
    +# We keep it because it is used by tests and small enough not
    
    33
    +# to be problematic when shipped unconditionally.
    
    34
    +#
    
    35
    +# These collections are referenced in toolkit/ or other core code.
    
    36
    +FINAL_TARGET_FILES.defaults.settings.main += [
    
    37
    +    "password-recipes.json",
    
    38
    +]

  • services/settings/remote-settings.sys.mjs
    ... ... @@ -149,7 +149,6 @@ export async function jexlFilterCreator(environment, collectionName) {
    149 149
     function remoteSettingsFunction() {
    
    150 150
       const _clients = new Map();
    
    151 151
       let _invalidatePolling = false;
    
    152
    -  let _initialized = false;
    
    153 152
     
    
    154 153
       // If not explicitly specified, use the default signer.
    
    155 154
       const defaultOptions = {
    
    ... ... @@ -182,6 +181,8 @@ function remoteSettingsFunction() {
    182 181
         return _clients.get(collectionName);
    
    183 182
       };
    
    184 183
     
    
    184
    +  remoteSettings._initialized = false;
    
    185
    +
    
    185 186
       /**
    
    186 187
        * Internal helper to retrieve existing instances of clients or new instances
    
    187 188
        * with default options if possible, or `null` if bucket/collection are unknown.
    
    ... ... @@ -392,10 +393,10 @@ function remoteSettingsFunction() {
    392 393
       } = {}) => {
    
    393 394
         if (AppConstants.BASE_BROWSER_VERSION) {
    
    394 395
           // Called multiple times on GeckoView due to bug 1730026
    
    395
    -      if (_initialized) {
    
    396
    +      if (remoteSettings._initialized) {
    
    396 397
             return;
    
    397 398
           }
    
    398
    -      _initialized = true;
    
    399
    +      remoteSettings._initialized = true;
    
    399 400
           _maybeImportFromLocalDump(trigger);
    
    400 401
           return;
    
    401 402
         }
    

  • services/settings/test/unit/test_remote_settings_base_browser.js
    1
    +"use strict";
    
    2
    +
    
    3
    +const { Downloader } = ChromeUtils.importESModule(
    
    4
    +  "resource://services-settings/Attachments.sys.mjs"
    
    5
    +);
    
    6
    +const { sinon } = ChromeUtils.importESModule(
    
    7
    +  "resource://testing-common/Sinon.sys.mjs"
    
    8
    +);
    
    9
    +
    
    10
    +// A collection with a dump that's packaged on all test builds.
    
    11
    +const TEST_BUCKET = "main";
    
    12
    +const TEST_COLLECTION = "password-recipes";
    
    13
    +
    
    14
    +let client;
    
    15
    +let DUMP_RECORDS;
    
    16
    +let DUMP_LAST_MODIFIED;
    
    17
    +let SERVER_LAST_MODIFIED;
    
    18
    +let server;
    
    19
    +let serverRequests = [];
    
    20
    +
    
    21
    +function assertNoServerRequests() {
    
    22
    +  Assert.deepEqual(serverRequests, [], "the server received no requests");
    
    23
    +  serverRequests = [];
    
    24
    +}
    
    25
    +
    
    26
    +async function importData(records) {
    
    27
    +  await RemoteSettingsWorker._execute("_test_only_import", [
    
    28
    +    TEST_BUCKET,
    
    29
    +    TEST_COLLECTION,
    
    30
    +    records,
    
    31
    +    records[0]?.last_modified || 0,
    
    32
    +  ]);
    
    33
    +}
    
    34
    +
    
    35
    +async function pollChangesFromOutdatedData() {
    
    36
    +  await importData([{ id: "dummy", last_modified: 1 }]);
    
    37
    +  const seenNotification = TestUtils.topicObserved(
    
    38
    +    "remote-settings:changes-poll-end"
    
    39
    +  );
    
    40
    +  await RemoteSettings.pollChanges({ trigger: "startup" });
    
    41
    +  await seenNotification;
    
    42
    +}
    
    43
    +
    
    44
    +add_setup(async () => {
    
    45
    +  Assert.ok(
    
    46
    +    AppConstants.BASE_BROWSER_VERSION,
    
    47
    +    "This test only makes sense when built with --with-base-browser-version"
    
    48
    +  );
    
    49
    +
    
    50
    +  client = RemoteSettings(TEST_COLLECTION, { bucketName: TEST_BUCKET });
    
    51
    +  // Opening the DB from the main thread creates/upgrades it as needed.
    
    52
    +  // The worker-based import used by importData() below opens it with
    
    53
    +  // allowUpgrades=false, so it requires the DB to already exist.
    
    54
    +  await client.db.getLastModified();
    
    55
    +
    
    56
    +  const dump = await SharedUtils.loadJSONDump(TEST_BUCKET, TEST_COLLECTION);
    
    57
    +  DUMP_RECORDS = dump.data;
    
    58
    +  DUMP_LAST_MODIFIED = dump.timestamp;
    
    59
    +  Assert.greater(
    
    60
    +    DUMP_LAST_MODIFIED,
    
    61
    +    1,
    
    62
    +    "the dump is newer than the outdated data used by the tests"
    
    63
    +  );
    
    64
    +
    
    65
    +  // The server offers data that is newer than the dump, so that processing
    
    66
    +  // anything from it would be observable.
    
    67
    +  SERVER_LAST_MODIFIED = DUMP_LAST_MODIFIED + 1000;
    
    68
    +
    
    69
    +  server = new HttpServer();
    
    70
    +  server.start(-1);
    
    71
    +  registerCleanupFunction(() => server.stop(() => {}));
    
    72
    +  const origin = `http://localhost:${server.identity.primaryPort}`;
    
    73
    +  const attachment = await IOUtils.readUTF8(
    
    74
    +    do_get_file(
    
    75
    +      "test_attachments_downloader/65650a0f-7c22-4c10-9744-2d67e301f5f4.pem"
    
    76
    +    ).path
    
    77
    +  );
    
    78
    +  const responses = {
    
    79
    +    "/v1/": {
    
    80
    +      capabilities: { attachments: { base_url: `${origin}/cdn/` } },
    
    81
    +    },
    
    82
    +    "/v1/buckets/monitor/collections/changes/changeset": {
    
    83
    +      timestamp: SERVER_LAST_MODIFIED,
    
    84
    +      changes: [
    
    85
    +        {
    
    86
    +          bucket: TEST_BUCKET,
    
    87
    +          collection: TEST_COLLECTION,
    
    88
    +          last_modified: SERVER_LAST_MODIFIED,
    
    89
    +        },
    
    90
    +      ],
    
    91
    +    },
    
    92
    +    [`/v1/buckets/${TEST_BUCKET}/collections/${TEST_COLLECTION}/changeset`]: {
    
    93
    +      timestamp: SERVER_LAST_MODIFIED,
    
    94
    +      metadata: {},
    
    95
    +      changes: [{ id: "from-server", last_modified: SERVER_LAST_MODIFIED }],
    
    96
    +    },
    
    97
    +  };
    
    98
    +  server.registerPrefixHandler("/", (request, response) => {
    
    99
    +    serverRequests.push(request.path);
    
    100
    +    if (request.path.startsWith("/cdn/")) {
    
    101
    +      response.write(attachment);
    
    102
    +    } else if (request.path in responses) {
    
    103
    +      response.setHeader("Content-Type", "application/json; charset=UTF-8");
    
    104
    +      response.write(JSON.stringify(responses[request.path]));
    
    105
    +    } else {
    
    106
    +      response.setStatusLine(null, 404, "Not Found");
    
    107
    +    }
    
    108
    +  });
    
    109
    +  Services.prefs.setStringPref("services.settings.server", `${origin}/v1`);
    
    110
    +  // Tor browser routes everything through a SOCKS proxy by default, which
    
    111
    +  // would prevent requests from ever reaching the server.
    
    112
    +  Services.prefs.setIntPref("network.proxy.type", 0);
    
    113
    +});
    
    114
    +
    
    115
    +add_task(function test_shouldSkipRemoteActivity_is_always_true() {
    
    116
    +  Assert.ok(
    
    117
    +    Utils.shouldSkipRemoteActivity,
    
    118
    +    "Remote activity is always skipped for base browser builds"
    
    119
    +  );
    
    120
    +});
    
    121
    +
    
    122
    +add_task(async function test_appconstants_disable_remote_settings_server() {
    
    123
    +  Assert.deepEqual(
    
    124
    +    AppConstants.REMOTE_SETTINGS_SERVER_URLS,
    
    125
    +    [""],
    
    126
    +    "No real Remote Settings server is configured"
    
    127
    +  );
    
    128
    +  Assert.equal(
    
    129
    +    AppConstants.REMOTE_SETTINGS_VERIFY_SIGNATURE,
    
    130
    +    false,
    
    131
    +    "Signature verification is disabled, since we never fetch from a server"
    
    132
    +  );
    
    133
    +  Assert.equal(
    
    134
    +    Services.prefs
    
    135
    +      .getDefaultBranch("")
    
    136
    +      .getStringPref("services.settings.server", ""),
    
    137
    +    "",
    
    138
    +    "The services.settings.server pref has no default value"
    
    139
    +  );
    
    140
    +});
    
    141
    +
    
    142
    +add_task(async function test_sync_never_contacts_server() {
    
    143
    +  await importData([{ id: "dummy", last_modified: 1 }]);
    
    144
    +
    
    145
    +  await client.sync();
    
    146
    +
    
    147
    +  Assert.equal(
    
    148
    +    await client.getLastModified(),
    
    149
    +    1,
    
    150
    +    "sync() did not touch local data"
    
    151
    +  );
    
    152
    +  assertNoServerRequests();
    
    153
    +
    
    154
    +  // Sanity check: the server does offer newer data for this collection.
    
    155
    +  const { remoteTimestamp } = await client._fetchChangeset();
    
    156
    +  Assert.equal(
    
    157
    +    remoteTimestamp,
    
    158
    +    SERVER_LAST_MODIFIED,
    
    159
    +    "the server offers newer data"
    
    160
    +  );
    
    161
    +  serverRequests = [];
    
    162
    +});
    
    163
    +
    
    164
    +add_task(async function test_pollChanges_imports_from_newer_local_dump() {
    
    165
    +  RemoteSettings._initialized = false;
    
    166
    +
    
    167
    +  await pollChangesFromOutdatedData();
    
    168
    +
    
    169
    +  Assert.equal(
    
    170
    +    await client.getLastModified(),
    
    171
    +    DUMP_LAST_MODIFIED,
    
    172
    +    "local data was updated from the packaged dump"
    
    173
    +  );
    
    174
    +  const current = await client.get({ loadDumpIfNewer: false });
    
    175
    +  Assert.equal(
    
    176
    +    current.length,
    
    177
    +    DUMP_RECORDS.length,
    
    178
    +    "all dump records imported"
    
    179
    +  );
    
    180
    +  assertNoServerRequests();
    
    181
    +
    
    182
    +  // Sanity check: the server does advertise newer changes.
    
    183
    +  const { changes } = await Utils.fetchLatestChanges(Utils.SERVER_URL);
    
    184
    +  Assert.deepEqual(
    
    185
    +    changes.map(c => c.last_modified),
    
    186
    +    [SERVER_LAST_MODIFIED],
    
    187
    +    "the server advertises newer changes"
    
    188
    +  );
    
    189
    +  serverRequests = [];
    
    190
    +});
    
    191
    +
    
    192
    +add_task(async function test_pollChanges_is_noop_after_first_call() {
    
    193
    +  RemoteSettings._initialized = false;
    
    194
    +  await pollChangesFromOutdatedData();
    
    195
    +
    
    196
    +  // The local dump import is not awaited by pollChanges(), but it calls
    
    197
    +  // hasLocalDump() synchronously, so this spy tells us deterministically
    
    198
    +  // whether an import was started.
    
    199
    +  const hasLocalDumpSpy = sinon.spy(Utils, "hasLocalDump");
    
    200
    +
    
    201
    +  // This guards against bug 1730026, where GeckoView calls this twice.
    
    202
    +  await RemoteSettings.pollChanges({ trigger: "timer" });
    
    203
    +  Assert.ok(
    
    204
    +    hasLocalDumpSpy.notCalled,
    
    205
    +    "second call to pollChanges() is a no-op"
    
    206
    +  );
    
    207
    +
    
    208
    +  // Sanity check: without the guard, the same call starts an import.
    
    209
    +  RemoteSettings._initialized = false;
    
    210
    +  await pollChangesFromOutdatedData();
    
    211
    +  Assert.ok(
    
    212
    +    hasLocalDumpSpy.called,
    
    213
    +    "pollChanges() starts an import when unguarded"
    
    214
    +  );
    
    215
    +
    
    216
    +  hasLocalDumpSpy.restore();
    
    217
    +  assertNoServerRequests();
    
    218
    +});
    
    219
    +
    
    220
    +add_task(async function test_pollChanges_never_records_sync_history() {
    
    221
    +  RemoteSettings._initialized = false;
    
    222
    +  const syncHistory = new SyncHistory("settings-sync");
    
    223
    +  await syncHistory.clear();
    
    224
    +
    
    225
    +  await pollChangesFromOutdatedData();
    
    226
    +
    
    227
    +  const history = await syncHistory.list();
    
    228
    +  Assert.equal(history.length, 0, "no sync history was recorded");
    
    229
    +  assertNoServerRequests();
    
    230
    +});
    
    231
    +
    
    232
    +add_task(async function test_downloader_never_downloads_from_server() {
    
    233
    +  // Same fixture as test_attachments_downloader.js: a record whose attachment
    
    234
    +  // is not available from any dump or cache.
    
    235
    +  const record = {
    
    236
    +    id: "1f3a0802-648d-11ea-bd79-876a8b69c377",
    
    237
    +    attachment: {
    
    238
    +      hash: "f41ed47d0f43325c9f089d03415c972ce1d3f1ecab6e4d6260665baf3db3ccee",
    
    239
    +      size: 1597,
    
    240
    +      filename: "test_file.pem",
    
    241
    +      location:
    
    242
    +        "main-workspace/some-collection/65650a0f-7c22-4c10-9744-2d67e301f5f4.pem",
    
    243
    +      mimetype: "application/x-pem-file",
    
    244
    +    },
    
    245
    +  };
    
    246
    +  const downloader = new Downloader(TEST_BUCKET, "some-collection");
    
    247
    +
    
    248
    +  // There's no local dump or cache for this attachment, so download must fail
    
    249
    +  // rather than fall back to the network.
    
    250
    +  await Assert.rejects(
    
    251
    +    downloader.downloadAsBytes(record),
    
    252
    +    e => e instanceof Downloader.NotFoundError,
    
    253
    +    "download never falls back to the network"
    
    254
    +  );
    
    255
    +  assertNoServerRequests();
    
    256
    +
    
    257
    +  // Sanity check: the server does serve this attachment.
    
    258
    +  const { size, hash, location } = record.attachment;
    
    259
    +  const buffer = await downloader._fetchAttachment(
    
    260
    +    (await Utils.baseAttachmentsURL()) + location
    
    261
    +  );
    
    262
    +  Assert.ok(
    
    263
    +    await RemoteSettingsWorker.checkContentHash(buffer, size, hash),
    
    264
    +    "the server serves the attachment"
    
    265
    +  );
    
    266
    +  serverRequests = [];
    
    267
    +});

  • services/settings/test/unit/xpcshell.toml
    ... ... @@ -18,11 +18,17 @@ skip-if = [
    18 18
       "os == 'android'", # Bug 2042499
    
    19 19
     ]
    
    20 20
     
    
    21
    +["test_remote_settings_base_browser.js"]
    
    22
    +support-files = ["test_attachments_downloader/**"]
    
    23
    +tags = "base-browser"
    
    24
    +
    
    21 25
     ["test_remote_settings_dump_lastmodified.js"]
    
    26
    +tags = "base-browser"
    
    22 27
     
    
    23 28
     ["test_remote_settings_jexl_filters.js"]
    
    24 29
     
    
    25 30
     ["test_remote_settings_offline.js"]
    
    31
    +tags = "base-browser"
    
    26 32
     
    
    27 33
     ["test_remote_settings_older_than_local.js"]
    
    28 34
     
    
    ... ... @@ -53,8 +59,10 @@ skip-if = [
    53 59
     ]
    
    54 60
     
    
    55 61
     ["test_remote_settings_worker.js"]
    
    62
    +tags = "base-browser"
    
    56 63
     
    
    57 64
     ["test_shutdown_handling.js"]
    
    65
    +tags = "base-browser"
    
    58 66
     
    
    59 67
     ["test_uptake_telemetry.js"]
    
    60 68
     skip-if = [