summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chrome_metrics_helper.cc
blob: 578f29952fd2a89a8b31e28b4bf0ae42c9051987 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chrome_metrics_helper.h"

#include "base/base64.h"
#include "base/memory/singleton.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/common/metrics/proto/chrome_experiments.pb.h"
#include "chrome/common/metrics/variations/variations_util.h"
#include "content/public/browser/browser_thread.h"
#include "net/http/http_request_headers.h"

using content::BrowserThread;

ChromeMetricsHelper* ChromeMetricsHelper::GetInstance() {
  return Singleton<ChromeMetricsHelper>::get();
}

void ChromeMetricsHelper::AppendHeaders(const GURL& url,
                                        bool incognito,
                                        bool uma_enabled,
                                        net::HttpRequestHeaders* headers) {
  // Note the criteria for attaching Chrome experiment headers:
  // 1. We only transmit to *.google.<TLD> domains. NOTE that this use of
  //    google_util helpers to check this does not guarantee that the URL is
  //    Google-owned, only that it is of the form *.google.<TLD>. In the future
  //    we may choose to reinforce this check.
  // 2. Only transmit for non-Incognito profiles.
  // 3. For the X-Chrome-UMA-Enabled bit, only set it if UMA is in fact enabled
  //    for this install of Chrome.
  // 4. For the X-Chrome-Variations, only include non-empty variation IDs.
  if (incognito ||
      !google_util::IsGoogleDomainUrl(url.spec(),
                                      google_util::ALLOW_SUBDOMAIN,
                                      google_util::ALLOW_NON_STANDARD_PORTS)) {
    return;
  }

  if (uma_enabled)
    headers->SetHeaderIfMissing("X-Chrome-UMA-Enabled", "1");

  // Lazily initialize the header, if not already done, before attempting to
  // transmit it.
  InitVariationIDsCacheIfNeeded();
  base::AutoLock scoped_lock(lock_);
  if (!variation_ids_header_.empty())
    headers->SetHeaderIfMissing("X-Chrome-Variations", variation_ids_header_);
}

ChromeMetricsHelper::ChromeMetricsHelper()
    : variation_ids_cache_initialized_(false) {
}

ChromeMetricsHelper::~ChromeMetricsHelper() {
}

void ChromeMetricsHelper::OnFieldTrialGroupFinalized(
    const std::string& trial_name,
    const std::string& group_name) {
  chrome_variations::VariationID new_id =
      chrome_variations::GetGoogleVariationID(
          chrome_variations::GOOGLE_WEB_PROPERTIES, trial_name, group_name);
  if (new_id == chrome_variations::kEmptyID)
    return;
  base::AutoLock scoped_lock(lock_);
  variation_ids_set_.insert(new_id);
  UpdateVariationIDsHeaderValue();
}

void ChromeMetricsHelper::InitVariationIDsCacheIfNeeded() {
  base::AutoLock scoped_lock(lock_);
  if (variation_ids_cache_initialized_)
    return;

  // Register for additional cache updates. This is done first to avoid a race
  // that could cause registered FieldTrials to be missed.
  DCHECK(MessageLoop::current());
  base::FieldTrialList::AddObserver(this);

  base::FieldTrial::ActiveGroups initial_groups;
  base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
  for (base::FieldTrial::ActiveGroups::const_iterator it =
       initial_groups.begin(); it != initial_groups.end(); ++it) {
    const chrome_variations::VariationID id =
        chrome_variations::GetGoogleVariationID(
            chrome_variations::GOOGLE_WEB_PROPERTIES, it->trial_name,
            it->group_name);
    if (id != chrome_variations::kEmptyID)
      variation_ids_set_.insert(id);
  }
  UpdateVariationIDsHeaderValue();

  variation_ids_cache_initialized_ = true;
}

void ChromeMetricsHelper::UpdateVariationIDsHeaderValue() {
  // The header value is a serialized protobuffer of Variation IDs which is
  // base64 encoded before transmitting as a string.
  if (variation_ids_set_.empty())
    return;

  // This is the bottleneck for the creation of the header, so validate the size
  // here. Force a hard maximum on the ID count in case the Variations server
  // returns too many IDs and DOSs receiving servers with large requests.
  DCHECK_LE(variation_ids_set_.size(), 10U);
  if (variation_ids_set_.size() > 20) {
    variation_ids_header_.clear();
    return;
  }

  metrics::ChromeVariations proto;
  for (std::set<chrome_variations::VariationID>::const_iterator it =
      variation_ids_set_.begin(); it != variation_ids_set_.end(); ++it)
    proto.add_variation_id(*it);

  std::string serialized;
  proto.SerializeToString(&serialized);

  std::string hashed;
  if (base::Base64Encode(serialized, &hashed)) {
    // If successful, swap the header value with the new one.
    // Note that the list of IDs and the header could be temporarily out of sync
    // if IDs are added as the header is recreated. The receiving servers are OK
    // with such descrepancies.
    variation_ids_header_ = hashed;
  } else {
    NOTREACHED() << "Failed to base64 encode Variation IDs value: "
                 << serialized;
  }
}