Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_network_interceptor.cc
blob0e0a6b7ce35aac42beb7c46898efe94874e9c2d3
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/devtools/devtools_network_interceptor.h"
7 #include <limits>
9 #include "base/time/time.h"
10 #include "chrome/browser/devtools/devtools_network_conditions.h"
11 #include "chrome/browser/devtools/devtools_network_transaction.h"
12 #include "net/base/load_timing_info.h"
14 namespace {
16 int64_t kPacketSize = 1500;
18 } // namespace
20 DevToolsNetworkInterceptor::DevToolsNetworkInterceptor()
21 : conditions_(new DevToolsNetworkConditions()),
22 weak_ptr_factory_(this) {
25 DevToolsNetworkInterceptor::~DevToolsNetworkInterceptor() {
28 base::WeakPtr<DevToolsNetworkInterceptor>
29 DevToolsNetworkInterceptor::GetWeakPtr() {
30 return weak_ptr_factory_.GetWeakPtr();
33 void DevToolsNetworkInterceptor::AddTransaction(
34 DevToolsNetworkTransaction* transaction) {
35 DCHECK(transactions_.find(transaction) == transactions_.end());
36 transactions_.insert(transaction);
39 void DevToolsNetworkInterceptor::RemoveTransaction(
40 DevToolsNetworkTransaction* transaction) {
41 DCHECK(transactions_.find(transaction) != transactions_.end());
42 transactions_.erase(transaction);
44 if (!conditions_->IsThrottling())
45 return;
47 base::TimeTicks now = base::TimeTicks::Now();
48 UpdateThrottledTransactions(now);
49 throttled_transactions_.erase(std::remove(throttled_transactions_.begin(),
50 throttled_transactions_.end(), transaction),
51 throttled_transactions_.end());
53 SuspendedTransactions::iterator it = suspended_transactions_.begin();
54 for (; it != suspended_transactions_.end(); ++it) {
55 if (it->first == transaction) {
56 suspended_transactions_.erase(it);
57 break;
61 ArmTimer(now);
64 void DevToolsNetworkInterceptor::UpdateConditions(
65 scoped_ptr<DevToolsNetworkConditions> conditions) {
66 DCHECK(conditions);
67 base::TimeTicks now = base::TimeTicks::Now();
68 if (conditions_->IsThrottling())
69 UpdateThrottledTransactions(now);
71 conditions_ = conditions.Pass();
73 if (conditions_->offline()) {
74 timer_.Stop();
75 throttled_transactions_.clear();
76 suspended_transactions_.clear();
77 Transactions old_transactions(transactions_);
78 Transactions::iterator it = old_transactions.begin();
79 for (;it != old_transactions.end(); ++it) {
80 if (transactions_.find(*it) == transactions_.end())
81 continue;
82 if (!(*it)->request() || (*it)->failed())
83 continue;
84 if (ShouldFail(*it))
85 (*it)->Fail();
87 return;
90 if (conditions_->IsThrottling()) {
91 DCHECK(conditions_->download_throughput() != 0);
92 offset_ = now;
93 last_tick_ = 0;
94 int64_t us_tick_length =
95 (1000000L * kPacketSize) / conditions_->download_throughput();
96 DCHECK(us_tick_length != 0);
97 if (us_tick_length == 0)
98 us_tick_length = 1;
99 tick_length_ = base::TimeDelta::FromMicroseconds(us_tick_length);
100 latency_length_ = base::TimeDelta();
101 double latency = conditions_->latency();
102 if (latency > 0)
103 latency_length_ = base::TimeDelta::FromMillisecondsD(latency);
104 ArmTimer(now);
105 } else {
106 timer_.Stop();
108 std::vector<DevToolsNetworkTransaction*> throttled_transactions;
109 throttled_transactions.swap(throttled_transactions_);
110 size_t throttle_count = throttled_transactions.size();
111 for (size_t i = 0; i < throttle_count; ++i)
112 FireThrottledCallback(throttled_transactions[i]);
114 SuspendedTransactions suspended_transactions;
115 suspended_transactions_.swap(suspended_transactions_);
116 size_t suspend_count = suspended_transactions.size();
117 for (size_t i = 0; i < suspend_count; ++i)
118 FireThrottledCallback(suspended_transactions[i].first);
122 void DevToolsNetworkInterceptor::FireThrottledCallback(
123 DevToolsNetworkTransaction* transaction) {
124 if (transactions_.find(transaction) != transactions_.end())
125 transaction->FireThrottledCallback();
128 void DevToolsNetworkInterceptor::UpdateThrottledTransactions(
129 base::TimeTicks now) {
130 int64_t last_tick = (now - offset_) / tick_length_;
131 int64_t ticks = last_tick - last_tick_;
132 last_tick_ = last_tick;
134 int64_t length = throttled_transactions_.size();
135 if (!length) {
136 UpdateSuspendedTransactions(now);
137 return;
140 int64_t shift = ticks % length;
141 for (int64_t i = 0; i < length; ++i) {
142 throttled_transactions_[i]->DecreaseThrottledByteCount(
143 (ticks / length) * kPacketSize + (i < shift ? kPacketSize : 0));
145 std::rotate(throttled_transactions_.begin(),
146 throttled_transactions_.begin() + shift, throttled_transactions_.end());
148 UpdateSuspendedTransactions(now);
151 void DevToolsNetworkInterceptor::UpdateSuspendedTransactions(
152 base::TimeTicks now) {
153 int64_t activation_baseline =
154 (now - latency_length_ - base::TimeTicks()).InMicroseconds();
155 SuspendedTransactions suspended_transactions;
156 SuspendedTransactions::iterator it = suspended_transactions_.begin();
157 for (; it != suspended_transactions_.end(); ++it) {
158 if (it->second <= activation_baseline)
159 throttled_transactions_.push_back(it->first);
160 else
161 suspended_transactions.push_back(*it);
163 suspended_transactions_.swap(suspended_transactions);
166 void DevToolsNetworkInterceptor::OnTimer() {
167 base::TimeTicks now = base::TimeTicks::Now();
168 UpdateThrottledTransactions(now);
170 std::vector<DevToolsNetworkTransaction*> active_transactions;
171 std::vector<DevToolsNetworkTransaction*> finished_transactions;
172 size_t length = throttled_transactions_.size();
173 for (size_t i = 0; i < length; ++i) {
174 if (throttled_transactions_[i]->throttled_byte_count() < 0)
175 finished_transactions.push_back(throttled_transactions_[i]);
176 else
177 active_transactions.push_back(throttled_transactions_[i]);
179 throttled_transactions_.swap(active_transactions);
181 length = finished_transactions.size();
182 for (size_t i = 0; i < length; ++i)
183 FireThrottledCallback(finished_transactions[i]);
185 ArmTimer(now);
188 void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) {
189 size_t throttle_count = throttled_transactions_.size();
190 size_t suspend_count = suspended_transactions_.size();
191 if (!throttle_count && !suspend_count)
192 return;
193 int64_t min_ticks_left = 0x10000L;
194 for (size_t i = 0; i < throttle_count; ++i) {
195 int64_t packets_left = (throttled_transactions_[i]->throttled_byte_count() +
196 kPacketSize - 1) / kPacketSize;
197 int64_t ticks_left = (i + 1) + throttle_count * (packets_left - 1);
198 if (i == 0 || ticks_left < min_ticks_left)
199 min_ticks_left = ticks_left;
201 base::TimeTicks desired_time =
202 offset_ + tick_length_ * (last_tick_ + min_ticks_left);
204 int64_t min_baseline = std::numeric_limits<int64>::max();
205 for (size_t i = 0; i < suspend_count; ++i) {
206 if (suspended_transactions_[i].second < min_baseline)
207 min_baseline = suspended_transactions_[i].second;
209 if (suspend_count) {
210 base::TimeTicks activation_time = base::TimeTicks() +
211 base::TimeDelta::FromMicroseconds(min_baseline) + latency_length_;
212 if (activation_time < desired_time)
213 desired_time = activation_time;
216 timer_.Start(
217 FROM_HERE,
218 desired_time - now,
219 base::Bind(
220 &DevToolsNetworkInterceptor::OnTimer,
221 base::Unretained(this)));
224 void DevToolsNetworkInterceptor::ThrottleTransaction(
225 DevToolsNetworkTransaction* transaction, bool start) {
226 base::TimeTicks now = base::TimeTicks::Now();
227 UpdateThrottledTransactions(now);
228 if (start && latency_length_ != base::TimeDelta()) {
229 net::LoadTimingInfo load_timing_info;
230 base::TimeTicks send_end;
231 if (transaction->GetLoadTimingInfo(&load_timing_info))
232 send_end = load_timing_info.send_end;
233 if (send_end.is_null())
234 send_end = now;
235 int64_t us_send_end = (send_end - base::TimeTicks()).InMicroseconds();
236 suspended_transactions_.push_back(
237 SuspendedTransaction(transaction, us_send_end));
238 UpdateSuspendedTransactions(now);
239 } else {
240 throttled_transactions_.push_back(transaction);
242 ArmTimer(now);
245 bool DevToolsNetworkInterceptor::ShouldFail(
246 const DevToolsNetworkTransaction* transaction) {
247 if (!conditions_->offline())
248 return false;
250 if (!transaction->request_initiator().empty())
251 return false;
253 return true;
256 bool DevToolsNetworkInterceptor::ShouldThrottle(
257 const DevToolsNetworkTransaction* transaction) {
258 if (!conditions_->IsThrottling())
259 return false;
261 if (!transaction->request_initiator().empty())
262 return false;
264 return true;