tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_urlTelemetry.js (11385B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 ChromeUtils.defineESModuleGetters(this, {
      5  BrowserSearchTelemetry:
      6    "moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs",
      7  NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
      8  SearchSERPTelemetry:
      9    "moz-src:///browser/components/search/SearchSERPTelemetry.sys.mjs",
     10  TelemetryTestUtils: "resource://testing-common/TelemetryTestUtils.sys.mjs",
     11  sinon: "resource://testing-common/Sinon.sys.mjs",
     12 });
     13 
     14 const TESTS = [
     15  {
     16    title: "Google search access point",
     17    trackingUrl:
     18      "https://www.google.com/search?q=test&ie=utf-8&oe=utf-8&client=firefox-b-1-ab",
     19    expectedSearchCountEntry: "google:tagged:firefox-b-1-ab",
     20    expectedAdKey: "google:tagged",
     21    adUrls: [
     22      "https://www.googleadservices.com/aclk=foobar",
     23      "https://www.googleadservices.com/pagead/aclk=foobar",
     24      "https://www.google.com/aclk=foobar",
     25      "https://www.google.com/pagead/aclk=foobar",
     26    ],
     27    nonAdUrls: [
     28      "https://www.googleadservices.com/?aclk=foobar",
     29      "https://www.googleadservices.com/bar",
     30      "https://www.google.com/image",
     31    ],
     32  },
     33  {
     34    title: "Google search access point follow-on",
     35    trackingUrl:
     36      "https://www.google.com/search?client=firefox-b-1-ab&ei=EI_VALUE&q=test2&oq=test2&gs_l=GS_L_VALUE",
     37    expectedSearchCountEntry: "google:tagged-follow-on:firefox-b-1-ab",
     38  },
     39  {
     40    title: "Google organic",
     41    trackingUrl:
     42      "https://www.google.com/search?client=firefox-b-d-invalid&source=hp&ei=EI_VALUE&q=test&oq=test&gs_l=GS_L_VALUE",
     43    expectedSearchCountEntry: "google:organic:other",
     44    expectedAdKey: "google:organic",
     45    adUrls: ["https://www.googleadservices.com/aclk=foobar"],
     46    nonAdUrls: ["https://www.googleadservices.com/?aclk=foobar"],
     47  },
     48  {
     49    title: "Google organic no code",
     50    trackingUrl:
     51      "https://www.google.com/search?source=hp&ei=EI_VALUE&q=test&oq=test&gs_l=GS_L_VALUE",
     52    expectedSearchCountEntry: "google:organic:none",
     53    expectedAdKey: "google:organic",
     54    adUrls: ["https://www.googleadservices.com/aclk=foobar"],
     55    nonAdUrls: ["https://www.googleadservices.com/?aclk=foobar"],
     56  },
     57  {
     58    title: "Google organic UK",
     59    trackingUrl:
     60      "https://www.google.co.uk/search?source=hp&ei=EI_VALUE&q=test&oq=test&gs_l=GS_L_VALUE",
     61    expectedSearchCountEntry: "google:organic:none",
     62  },
     63  {
     64    title: "Bing search access point",
     65    trackingUrl: "https://www.bing.com/search?q=test&pc=MOZI&form=MOZLBR",
     66    expectedSearchCountEntry: "bing:tagged:MOZI",
     67    expectedAdKey: "bing:tagged",
     68    adUrls: [
     69      "https://www.bing.com/aclick?ld=foo",
     70      "https://www.bing.com/aclk?ld=foo",
     71    ],
     72    nonAdUrls: [
     73      "https://www.bing.com/fd/ls/ls.gif?IG=foo",
     74      "https://www.bing.com/fd/ls/l?IG=bar",
     75      "https://www.bing.com/aclook?",
     76      "https://www.bing.com/fd/ls/GLinkPingPost.aspx?IG=baz&url=%2Fvideos%2Fsearch%3Fq%3Dfoo",
     77      "https://www.bing.com/fd/ls/GLinkPingPost.aspx?IG=bar&url=https%3A%2F%2Fwww.bing.com%2Faclick",
     78      "https://www.bing.com/fd/ls/GLinkPingPost.aspx?IG=bar&url=https%3A%2F%2Fwww.bing.com%2Faclk",
     79    ],
     80  },
     81  {
     82    setUp() {
     83      Services.cookies.removeAll();
     84      const cv = Services.cookies.add(
     85        "www.bing.com",
     86        "/",
     87        "SRCHS",
     88        "PC=MOZI",
     89        false,
     90        false,
     91        false,
     92        Date.now() + 1000 * 60 * 60,
     93        {},
     94        Ci.nsICookie.SAMESITE_UNSET,
     95        Ci.nsICookie.SCHEME_HTTPS
     96      );
     97      Assert.equal(cv.result, Ci.nsICookieValidation.eOK, "Valid cookie");
     98    },
     99    tearDown() {
    100      Services.cookies.removeAll();
    101    },
    102    title: "Bing search access point follow-on",
    103    trackingUrl:
    104      "https://www.bing.com/search?q=test&qs=n&form=QBRE&sp=-1&pq=&sc=0-0&sk=&cvid=CVID_VALUE",
    105    expectedSearchCountEntry: "bing:tagged-follow-on:MOZI",
    106  },
    107  {
    108    title: "Bing organic",
    109    trackingUrl: "https://www.bing.com/search?q=test&pc=MOZIfoo&form=MOZLBR",
    110    expectedSearchCountEntry: "bing:organic:other",
    111    expectedAdKey: "bing:organic",
    112    adUrls: ["https://www.bing.com/aclick?ld=foo"],
    113    nonAdUrls: ["https://www.bing.com/fd/ls/ls.gif?IG=foo"],
    114  },
    115  {
    116    title: "Bing organic no code",
    117    trackingUrl:
    118      "https://www.bing.com/search?q=test&qs=n&form=QBLH&sp=-1&pq=&sc=0-0&sk=&cvid=CVID_VALUE",
    119    expectedSearchCountEntry: "bing:organic:none",
    120    expectedAdKey: "bing:organic",
    121    adUrls: ["https://www.bing.com/aclick?ld=foo"],
    122    nonAdUrls: ["https://www.bing.com/fd/ls/ls.gif?IG=foo"],
    123  },
    124  {
    125    title: "DuckDuckGo search access point",
    126    trackingUrl: "https://duckduckgo.com/?q=test&t=ffab",
    127    expectedSearchCountEntry: "duckduckgo:tagged:ffab",
    128    expectedAdKey: "duckduckgo:tagged",
    129    adUrls: [
    130      "https://duckduckgo.com/y.js?ad_provider=foo",
    131      "https://duckduckgo.com/y.js?f=bar&ad_provider=foo",
    132      "https://www.amazon.co.uk/foo?tag=duckduckgo-ffab-uk-32-xk",
    133    ],
    134    nonAdUrls: [
    135      "https://duckduckgo.com/?q=foo&t=ffab&ia=images&iax=images",
    136      "https://duckduckgo.com/y.js?ifu=foo",
    137      "https://improving.duckduckgo.com/t/bar",
    138    ],
    139  },
    140  {
    141    title: "DuckDuckGo organic",
    142    trackingUrl: "https://duckduckgo.com/?q=test&t=other&ia=shopping",
    143    expectedSearchCountEntry: "duckduckgo:organic:other",
    144    expectedAdKey: "duckduckgo:organic",
    145    adUrls: ["https://duckduckgo.com/y.js?ad_provider=foo"],
    146    nonAdUrls: ["https://duckduckgo.com/?q=foo&t=ffab&ia=images&iax=images"],
    147  },
    148  {
    149    title: "DuckDuckGo expected organic code",
    150    trackingUrl: "https://duckduckgo.com/?q=test&t=h_&ia=shopping",
    151    expectedSearchCountEntry: "duckduckgo:organic:none",
    152    expectedAdKey: "duckduckgo:organic",
    153    adUrls: ["https://duckduckgo.com/y.js?ad_provider=foo"],
    154    nonAdUrls: ["https://duckduckgo.com/?q=foo&t=ffab&ia=images&iax=images"],
    155  },
    156  {
    157    title: "DuckDuckGo expected organic code 2",
    158    trackingUrl: "https://duckduckgo.com/?q=test&t=hz&ia=shopping",
    159    expectedSearchCountEntry: "duckduckgo:organic:none",
    160    expectedAdKey: "duckduckgo:organic",
    161    adUrls: ["https://duckduckgo.com/y.js?ad_provider=foo"],
    162    nonAdUrls: ["https://duckduckgo.com/?q=foo&t=ffab&ia=images&iax=images"],
    163  },
    164  {
    165    title: "DuckDuckGo organic no code",
    166    trackingUrl: "https://duckduckgo.com/?q=test&ia=shopping",
    167    expectedSearchCountEntry: "duckduckgo:organic:none",
    168    expectedAdKey: "duckduckgo:organic",
    169    adUrls: ["https://duckduckgo.com/y.js?ad_provider=foo"],
    170    nonAdUrls: ["https://duckduckgo.com/?q=foo&t=ffab&ia=images&iax=images"],
    171  },
    172  {
    173    title: "Baidu search access point",
    174    trackingUrl: "https://www.baidu.com/baidu?wd=test&tn=monline_7_dg&ie=utf-8",
    175    expectedSearchCountEntry: "baidu:tagged:monline_7_dg",
    176    expectedAdKey: "baidu:tagged",
    177    adUrls: ["https://www.baidu.com/baidu.php?url=encoded"],
    178    nonAdUrls: ["https://www.baidu.com/link?url=encoded"],
    179  },
    180  {
    181    title: "Baidu search access point follow-on",
    182    trackingUrl:
    183      "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=monline_7_dg&wd=test2&oq=test&rsv_pq=RSV_PQ_VALUE&rsv_t=RSV_T_VALUE&rqlang=cn&rsv_enter=1&rsv_sug3=2&rsv_sug2=0&inputT=227&rsv_sug4=397",
    184    expectedSearchCountEntry: "baidu:tagged-follow-on:monline_7_dg",
    185  },
    186  {
    187    title: "Baidu organic",
    188    trackingUrl:
    189      "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&ch=&tn=baidu&bar=&wd=test&rn=&oq&rsv_pq=RSV_PQ_VALUE&rsv_t=RSV_T_VALUE&rqlang=cn",
    190    expectedSearchCountEntry: "baidu:organic:other",
    191  },
    192  {
    193    title: "Baidu organic no code",
    194    trackingUrl:
    195      "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&ch=&bar=&wd=test&rn=&oq&rsv_pq=RSV_PQ_VALUE&rsv_t=RSV_T_VALUE&rqlang=cn",
    196    expectedSearchCountEntry: "baidu:organic:none",
    197  },
    198  {
    199    title: "Ecosia search access point",
    200    trackingUrl: "https://www.ecosia.org/search?tt=mzl&q=foo",
    201    expectedSearchCountEntry: "ecosia:tagged:mzl",
    202    expectedAdKey: "ecosia:tagged",
    203    adUrls: ["https://www.bing.com/aclick?ld=foo"],
    204    nonAdUrls: [],
    205  },
    206  {
    207    title: "Ecosia organic",
    208    trackingUrl: "https://www.ecosia.org/search?method=index&q=foo",
    209    expectedSearchCountEntry: "ecosia:organic:none",
    210    expectedAdKey: "ecosia:organic",
    211    adUrls: ["https://www.bing.com/aclick?ld=foo"],
    212    nonAdUrls: [],
    213  },
    214 ];
    215 
    216 /**
    217 * This function is primarily for testing the Ad URL regexps that are triggered
    218 * when a URL is clicked on. These regexps are also used for the `with_ads`
    219 * probe. However, we test the ad_clicks route as that is easier to hit.
    220 *
    221 * @param {string} serpUrl
    222 *   The url to simulate where the page the click came from.
    223 * @param {string} adUrl
    224 *   The ad url to simulate being clicked.
    225 * @param {string} [expectedAdKey]
    226 *   The expected key to be logged for the scalar. Omit if no scalar should be
    227 *   logged.
    228 */
    229 async function testAdUrlClicked(serpUrl, adUrl, expectedAdKey) {
    230  info(`Testing Ad URL: ${adUrl}`);
    231  let channel = NetUtil.newChannel({
    232    uri: NetUtil.newURI(adUrl),
    233    triggeringPrincipal: Services.scriptSecurityManager.createContentPrincipal(
    234      NetUtil.newURI(serpUrl),
    235      {}
    236    ),
    237    loadUsingSystemPrincipal: true,
    238  });
    239  SearchSERPTelemetry._contentHandler.observeActivity(
    240    channel,
    241    Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION,
    242    Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE
    243  );
    244  // Since the content handler takes a moment to allow the channel information
    245  // to settle down, wait the same amount of time here.
    246  await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
    247 
    248  const scalars = TelemetryTestUtils.getProcessScalars("parent", true, true);
    249  if (!expectedAdKey) {
    250    Assert.ok(
    251      !("browser.search.adclicks.unknown" in scalars),
    252      "Should not have recorded an ad click"
    253    );
    254  } else {
    255    TelemetryTestUtils.assertKeyedScalar(
    256      scalars,
    257      "browser.search.adclicks.unknown",
    258      expectedAdKey,
    259      1
    260    );
    261  }
    262 }
    263 
    264 do_get_profile();
    265 
    266 add_setup(async function () {
    267  await SearchSERPTelemetry.init();
    268  sinon.stub(BrowserSearchTelemetry, "shouldRecordSearchCount").returns(true);
    269 
    270  registerCleanupFunction(async () => {
    271    sinon.restore();
    272  });
    273 });
    274 
    275 add_task(async function test_parsing_search_urls() {
    276  for (const test of TESTS) {
    277    info(`Running ${test.title}`);
    278    if (test.setUp) {
    279      test.setUp();
    280    }
    281    SearchSERPTelemetry.updateTrackingStatus(
    282      {
    283        getTabBrowser: () => {},
    284        // There is no concept of browsing in unit tests, so assume in tests that we
    285        // are not in private browsing mode. We have browser tests that check when
    286        // private browsing is used.
    287        contentPrincipal: {
    288          originAttributes: {
    289            privateBrowsingId: 0,
    290          },
    291        },
    292      },
    293      test.trackingUrl
    294    );
    295    let scalars = TelemetryTestUtils.getProcessScalars("parent", true, true);
    296    TelemetryTestUtils.assertKeyedScalar(
    297      scalars,
    298      "browser.search.content.unknown",
    299      test.expectedSearchCountEntry,
    300      1
    301    );
    302 
    303    if ("adUrls" in test) {
    304      for (const adUrl of test.adUrls) {
    305        await testAdUrlClicked(test.trackingUrl, adUrl, test.expectedAdKey);
    306      }
    307      for (const nonAdUrls of test.nonAdUrls) {
    308        await testAdUrlClicked(test.trackingUrl, nonAdUrls);
    309      }
    310    }
    311 
    312    if (test.tearDown) {
    313      test.tearDown();
    314    }
    315  }
    316 });