Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(953)

Side by Side Diff: net/nqe/network_quality_estimator.cc

Issue 2453653002: Separate out observation sources as either HTTP layer or transport layer (Closed)
Patch Set: Addressed bengr comments Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | net/nqe/network_quality_estimator_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/nqe/network_quality_estimator.h" 5 #include "net/nqe/network_quality_estimator.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 #include <limits> 9 #include <limits>
10 #include <utility> 10 #include <utility>
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after
320 } 320 }
321 321
322 void NetworkQualityEstimator::AddDefaultEstimates() { 322 void NetworkQualityEstimator::AddDefaultEstimates() {
323 DCHECK(thread_checker_.CalledOnValidThread()); 323 DCHECK(thread_checker_.CalledOnValidThread());
324 324
325 if (default_observations_[current_network_id_.type].http_rtt() != 325 if (default_observations_[current_network_id_.type].http_rtt() !=
326 nqe::internal::InvalidRTT()) { 326 nqe::internal::InvalidRTT()) {
327 RttObservation rtt_observation( 327 RttObservation rtt_observation(
328 default_observations_[current_network_id_.type].http_rtt(), 328 default_observations_[current_network_id_.type].http_rtt(),
329 tick_clock_->NowTicks(), 329 tick_clock_->NowTicks(),
330 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_FROM_PLATFORM); 330 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM);
331 rtt_observations_.AddObservation(rtt_observation); 331 rtt_observations_.AddObservation(rtt_observation);
332 NotifyObserversOfRTT(rtt_observation); 332 NotifyObserversOfRTT(rtt_observation);
333 } 333 }
334 334
335 if (default_observations_[current_network_id_.type] 335 if (default_observations_[current_network_id_.type]
336 .downstream_throughput_kbps() != nqe::internal::kInvalidThroughput) { 336 .downstream_throughput_kbps() != nqe::internal::kInvalidThroughput) {
337 ThroughputObservation throughput_observation( 337 ThroughputObservation throughput_observation(
338 default_observations_[current_network_id_.type] 338 default_observations_[current_network_id_.type]
339 .downstream_throughput_kbps(), 339 .downstream_throughput_kbps(),
340 tick_clock_->NowTicks(), 340 tick_clock_->NowTicks(),
341 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_FROM_PLATFORM); 341 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM);
342 downstream_throughput_kbps_observations_.AddObservation( 342 downstream_throughput_kbps_observations_.AddObservation(
343 throughput_observation); 343 throughput_observation);
344 NotifyObserversOfThroughput(throughput_observation); 344 NotifyObserversOfThroughput(throughput_observation);
345 } 345 }
346 } 346 }
347 347
348 NetworkQualityEstimator::~NetworkQualityEstimator() { 348 NetworkQualityEstimator::~NetworkQualityEstimator() {
349 DCHECK(thread_checker_.CalledOnValidThread()); 349 DCHECK(thread_checker_.CalledOnValidThread());
350 NetworkChangeNotifier::RemoveConnectionTypeObserver(this); 350 NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
351 } 351 }
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
437 base::TimeDelta observed_http_rtt = 437 base::TimeDelta observed_http_rtt =
438 load_timing_info.receive_headers_end - load_timing_info.send_start; 438 load_timing_info.receive_headers_end - load_timing_info.send_start;
439 DCHECK_GE(observed_http_rtt, base::TimeDelta()); 439 DCHECK_GE(observed_http_rtt, base::TimeDelta());
440 if (observed_http_rtt < peak_network_quality_.http_rtt() || 440 if (observed_http_rtt < peak_network_quality_.http_rtt() ||
441 peak_network_quality_.http_rtt() == nqe::internal::InvalidRTT()) { 441 peak_network_quality_.http_rtt() == nqe::internal::InvalidRTT()) {
442 peak_network_quality_ = nqe::internal::NetworkQuality( 442 peak_network_quality_ = nqe::internal::NetworkQuality(
443 observed_http_rtt, peak_network_quality_.transport_rtt(), 443 observed_http_rtt, peak_network_quality_.transport_rtt(),
444 peak_network_quality_.downstream_throughput_kbps()); 444 peak_network_quality_.downstream_throughput_kbps());
445 } 445 }
446 446
447 RttObservation http_rtt_observation( 447 RttObservation http_rtt_observation(observed_http_rtt, now,
448 observed_http_rtt, now, NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST); 448 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP);
449 rtt_observations_.AddObservation(http_rtt_observation); 449 rtt_observations_.AddObservation(http_rtt_observation);
450 NotifyObserversOfRTT(http_rtt_observation); 450 NotifyObserversOfRTT(http_rtt_observation);
451 } 451 }
452 452
453 void NetworkQualityEstimator::RecordAccuracyAfterMainFrame( 453 void NetworkQualityEstimator::RecordAccuracyAfterMainFrame(
454 base::TimeDelta measuring_duration) const { 454 base::TimeDelta measuring_duration) const {
455 DCHECK(thread_checker_.CalledOnValidThread()); 455 DCHECK(thread_checker_.CalledOnValidThread());
456 DCHECK_EQ(0, measuring_duration.InMilliseconds() % 1000); 456 DCHECK_EQ(0, measuring_duration.InMilliseconds() % 1000);
457 DCHECK(ContainsValue(GetAccuracyRecordingIntervals(), measuring_duration)); 457 DCHECK(ContainsValue(GetAccuracyRecordingIntervals(), measuring_duration));
458 458
(...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after
860 if (GetTransportRTT(&rtt)) { 860 if (GetTransportRTT(&rtt)) {
861 // Add the 50th percentile value. 861 // Add the 50th percentile value.
862 base::HistogramBase* transport_rtt_percentile = GetHistogram( 862 base::HistogramBase* transport_rtt_percentile = GetHistogram(
863 "TransportRTT.Percentile50.", current_network_id_.type, 10 * 1000); 863 "TransportRTT.Percentile50.", current_network_id_.type, 10 * 1000);
864 transport_rtt_percentile->Add(rtt.InMilliseconds()); 864 transport_rtt_percentile->Add(rtt.InMilliseconds());
865 865
866 // Add the remaining percentile values. 866 // Add the remaining percentile values.
867 static const int kPercentiles[] = {0, 10, 90, 100}; 867 static const int kPercentiles[] = {0, 10, 90, 100};
868 std::vector<NetworkQualityObservationSource> disallowed_observation_sources; 868 std::vector<NetworkQualityObservationSource> disallowed_observation_sources;
869 disallowed_observation_sources.push_back( 869 disallowed_observation_sources.push_back(
870 NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST); 870 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP);
871 // Disallow external estimate provider since it provides RTT at HTTP layer. 871 // Disallow external estimate provider since it provides RTT at HTTP layer.
872 disallowed_observation_sources.push_back( 872 disallowed_observation_sources.push_back(
873 NETWORK_QUALITY_OBSERVATION_SOURCE_EXTERNAL_ESTIMATE); 873 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_EXTERNAL_ESTIMATE);
874 disallowed_observation_sources.push_back( 874 disallowed_observation_sources.push_back(
875 NETWORK_QUALITY_OBSERVATION_SOURCE_CACHED_ESTIMATE); 875 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE);
876 disallowed_observation_sources.push_back( 876 disallowed_observation_sources.push_back(
877 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_FROM_PLATFORM); 877 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM);
878 for (size_t i = 0; i < arraysize(kPercentiles); ++i) { 878 for (size_t i = 0; i < arraysize(kPercentiles); ++i) {
879 rtt = GetRTTEstimateInternal(disallowed_observation_sources, 879 rtt = GetRTTEstimateInternal(disallowed_observation_sources,
880 base::TimeTicks(), kPercentiles[i]); 880 base::TimeTicks(), kPercentiles[i]);
881 881
882 transport_rtt_percentile = GetHistogram( 882 transport_rtt_percentile = GetHistogram(
883 "TransportRTT.Percentile" + base::IntToString(kPercentiles[i]) + ".", 883 "TransportRTT.Percentile" + base::IntToString(kPercentiles[i]) + ".",
884 current_network_id_.type, 10 * 1000); // 10 seconds 884 current_network_id_.type, 10 * 1000); // 10 seconds
885 transport_rtt_percentile->Add(rtt.InMilliseconds()); 885 transport_rtt_percentile->Add(rtt.InMilliseconds());
886 } 886 }
887 } 887 }
(...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after
1188 *rtt = GetRTTEstimateInternal(disallowed_observation_sources, start_time, 50); 1188 *rtt = GetRTTEstimateInternal(disallowed_observation_sources, start_time, 50);
1189 return (*rtt != nqe::internal::InvalidRTT()); 1189 return (*rtt != nqe::internal::InvalidRTT());
1190 } 1190 }
1191 1191
1192 bool NetworkQualityEstimator::GetRecentTransportRTT( 1192 bool NetworkQualityEstimator::GetRecentTransportRTT(
1193 const base::TimeTicks& start_time, 1193 const base::TimeTicks& start_time,
1194 base::TimeDelta* rtt) const { 1194 base::TimeDelta* rtt) const {
1195 DCHECK(thread_checker_.CalledOnValidThread()); 1195 DCHECK(thread_checker_.CalledOnValidThread());
1196 std::vector<NetworkQualityObservationSource> disallowed_observation_sources; 1196 std::vector<NetworkQualityObservationSource> disallowed_observation_sources;
1197 disallowed_observation_sources.push_back( 1197 disallowed_observation_sources.push_back(
1198 NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST); 1198 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP);
1199 // Disallow external estimate provider since it provides RTT at HTTP layer. 1199 // Disallow external estimate provider since it provides RTT at HTTP layer.
1200 disallowed_observation_sources.push_back( 1200 disallowed_observation_sources.push_back(
1201 NETWORK_QUALITY_OBSERVATION_SOURCE_EXTERNAL_ESTIMATE); 1201 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_EXTERNAL_ESTIMATE);
1202 disallowed_observation_sources.push_back( 1202 disallowed_observation_sources.push_back(
1203 NETWORK_QUALITY_OBSERVATION_SOURCE_CACHED_ESTIMATE); 1203 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE);
1204 disallowed_observation_sources.push_back( 1204 disallowed_observation_sources.push_back(
1205 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_FROM_PLATFORM); 1205 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM);
1206 1206
1207 *rtt = GetRTTEstimateInternal(disallowed_observation_sources, start_time, 50); 1207 *rtt = GetRTTEstimateInternal(disallowed_observation_sources, start_time, 50);
1208 return (*rtt != nqe::internal::InvalidRTT()); 1208 return (*rtt != nqe::internal::InvalidRTT());
1209 } 1209 }
1210 1210
1211 bool NetworkQualityEstimator::GetRecentDownlinkThroughputKbps( 1211 bool NetworkQualityEstimator::GetRecentDownlinkThroughputKbps(
1212 const base::TimeTicks& start_time, 1212 const base::TimeTicks& start_time,
1213 int32_t* kbps) const { 1213 int32_t* kbps) const {
1214 DCHECK(thread_checker_.CalledOnValidThread()); 1214 DCHECK(thread_checker_.CalledOnValidThread());
1215 *kbps = GetDownlinkThroughputKbpsEstimateInternal(start_time, 50); 1215 *kbps = GetDownlinkThroughputKbpsEstimateInternal(start_time, 50);
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
1321 cached_network_quality.effective_connection_type(); 1321 cached_network_quality.effective_connection_type();
1322 1322
1323 if (effective_connection_type_ != EFFECTIVE_CONNECTION_TYPE_UNKNOWN) 1323 if (effective_connection_type_ != EFFECTIVE_CONNECTION_TYPE_UNKNOWN)
1324 NotifyObserversOfEffectiveConnectionTypeChanged(); 1324 NotifyObserversOfEffectiveConnectionTypeChanged();
1325 } 1325 }
1326 1326
1327 if (cached_network_quality.network_quality().downstream_throughput_kbps() != 1327 if (cached_network_quality.network_quality().downstream_throughput_kbps() !=
1328 nqe::internal::kInvalidThroughput) { 1328 nqe::internal::kInvalidThroughput) {
1329 ThroughputObservation througphput_observation( 1329 ThroughputObservation througphput_observation(
1330 cached_network_quality.network_quality().downstream_throughput_kbps(), 1330 cached_network_quality.network_quality().downstream_throughput_kbps(),
1331 now, NETWORK_QUALITY_OBSERVATION_SOURCE_CACHED_ESTIMATE); 1331 now, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE);
1332 downstream_throughput_kbps_observations_.AddObservation( 1332 downstream_throughput_kbps_observations_.AddObservation(
1333 througphput_observation); 1333 througphput_observation);
1334 NotifyObserversOfThroughput(througphput_observation); 1334 NotifyObserversOfThroughput(througphput_observation);
1335 } 1335 }
1336 1336
1337 if (cached_network_quality.network_quality().http_rtt() != 1337 if (cached_network_quality.network_quality().http_rtt() !=
1338 nqe::internal::InvalidRTT()) { 1338 nqe::internal::InvalidRTT()) {
1339 RttObservation rtt_observation( 1339 RttObservation rtt_observation(
1340 cached_network_quality.network_quality().http_rtt(), now, 1340 cached_network_quality.network_quality().http_rtt(), now,
1341 NETWORK_QUALITY_OBSERVATION_SOURCE_CACHED_ESTIMATE); 1341 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE);
1342 rtt_observations_.AddObservation(rtt_observation); 1342 rtt_observations_.AddObservation(rtt_observation);
1343 NotifyObserversOfRTT(rtt_observation); 1343 NotifyObserversOfRTT(rtt_observation);
1344 } 1344 }
1345 return true; 1345 return true;
1346 } 1346 }
1347 1347
1348 void NetworkQualityEstimator::OnUpdatedEstimateAvailable( 1348 void NetworkQualityEstimator::OnUpdatedEstimateAvailable(
1349 const base::TimeDelta& rtt, 1349 const base::TimeDelta& rtt,
1350 int32_t downstream_throughput_kbps, 1350 int32_t downstream_throughput_kbps,
1351 int32_t upstream_throughput_kbps) { 1351 int32_t upstream_throughput_kbps) {
1352 DCHECK(thread_checker_.CalledOnValidThread()); 1352 DCHECK(thread_checker_.CalledOnValidThread());
1353 DCHECK(external_estimate_provider_); 1353 DCHECK(external_estimate_provider_);
1354 1354
1355 RecordExternalEstimateProviderMetrics( 1355 RecordExternalEstimateProviderMetrics(
1356 EXTERNAL_ESTIMATE_PROVIDER_STATUS_CALLBACK); 1356 EXTERNAL_ESTIMATE_PROVIDER_STATUS_CALLBACK);
1357 1357
1358 external_estimate_provider_quality_ = nqe::internal::NetworkQuality(); 1358 external_estimate_provider_quality_ = nqe::internal::NetworkQuality();
1359 1359
1360 if (rtt > base::TimeDelta()) { 1360 if (rtt > base::TimeDelta()) {
1361 RecordExternalEstimateProviderMetrics( 1361 RecordExternalEstimateProviderMetrics(
1362 EXTERNAL_ESTIMATE_PROVIDER_STATUS_RTT_AVAILABLE); 1362 EXTERNAL_ESTIMATE_PROVIDER_STATUS_RTT_AVAILABLE);
1363 UMA_HISTOGRAM_TIMES("NQE.ExternalEstimateProvider.RTT", rtt); 1363 UMA_HISTOGRAM_TIMES("NQE.ExternalEstimateProvider.RTT", rtt);
1364 rtt_observations_.AddObservation( 1364 rtt_observations_.AddObservation(RttObservation(
1365 RttObservation(rtt, tick_clock_->NowTicks(), 1365 rtt, tick_clock_->NowTicks(),
1366 NETWORK_QUALITY_OBSERVATION_SOURCE_EXTERNAL_ESTIMATE)); 1366 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_EXTERNAL_ESTIMATE));
1367 external_estimate_provider_quality_.set_http_rtt(rtt); 1367 external_estimate_provider_quality_.set_http_rtt(rtt);
1368 } 1368 }
1369 1369
1370 if (downstream_throughput_kbps > 0) { 1370 if (downstream_throughput_kbps > 0) {
1371 RecordExternalEstimateProviderMetrics( 1371 RecordExternalEstimateProviderMetrics(
1372 EXTERNAL_ESTIMATE_PROVIDER_STATUS_DOWNLINK_BANDWIDTH_AVAILABLE); 1372 EXTERNAL_ESTIMATE_PROVIDER_STATUS_DOWNLINK_BANDWIDTH_AVAILABLE);
1373 UMA_HISTOGRAM_COUNTS("NQE.ExternalEstimateProvider.DownlinkBandwidth", 1373 UMA_HISTOGRAM_COUNTS("NQE.ExternalEstimateProvider.DownlinkBandwidth",
1374 downstream_throughput_kbps); 1374 downstream_throughput_kbps);
1375 downstream_throughput_kbps_observations_.AddObservation( 1375 downstream_throughput_kbps_observations_.AddObservation(
1376 ThroughputObservation( 1376 ThroughputObservation(
1377 downstream_throughput_kbps, tick_clock_->NowTicks(), 1377 downstream_throughput_kbps, tick_clock_->NowTicks(),
1378 NETWORK_QUALITY_OBSERVATION_SOURCE_EXTERNAL_ESTIMATE)); 1378 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_EXTERNAL_ESTIMATE));
1379 external_estimate_provider_quality_.set_downstream_throughput_kbps( 1379 external_estimate_provider_quality_.set_downstream_throughput_kbps(
1380 downstream_throughput_kbps); 1380 downstream_throughput_kbps);
1381 } 1381 }
1382 } 1382 }
1383 1383
1384 void NetworkQualityEstimator::SetTickClockForTesting( 1384 void NetworkQualityEstimator::SetTickClockForTesting(
1385 std::unique_ptr<base::TickClock> tick_clock) { 1385 std::unique_ptr<base::TickClock> tick_clock) {
1386 DCHECK(thread_checker_.CalledOnValidThread()); 1386 DCHECK(thread_checker_.CalledOnValidThread());
1387 tick_clock_ = std::move(tick_clock); 1387 tick_clock_ = std::move(tick_clock);
1388 } 1388 }
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
1442 1442
1443 if (downstream_kbps > peak_network_quality_.downstream_throughput_kbps() || 1443 if (downstream_kbps > peak_network_quality_.downstream_throughput_kbps() ||
1444 peak_network_quality_.downstream_throughput_kbps() == 1444 peak_network_quality_.downstream_throughput_kbps() ==
1445 nqe::internal::kInvalidThroughput) { 1445 nqe::internal::kInvalidThroughput) {
1446 peak_network_quality_ = nqe::internal::NetworkQuality( 1446 peak_network_quality_ = nqe::internal::NetworkQuality(
1447 peak_network_quality_.http_rtt(), peak_network_quality_.transport_rtt(), 1447 peak_network_quality_.http_rtt(), peak_network_quality_.transport_rtt(),
1448 downstream_kbps); 1448 downstream_kbps);
1449 } 1449 }
1450 ThroughputObservation throughput_observation( 1450 ThroughputObservation throughput_observation(
1451 downstream_kbps, tick_clock_->NowTicks(), 1451 downstream_kbps, tick_clock_->NowTicks(),
1452 NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST); 1452 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP);
1453 downstream_throughput_kbps_observations_.AddObservation( 1453 downstream_throughput_kbps_observations_.AddObservation(
1454 throughput_observation); 1454 throughput_observation);
1455 NotifyObserversOfThroughput(throughput_observation); 1455 NotifyObserversOfThroughput(throughput_observation);
1456 } 1456 }
1457 1457
1458 void NetworkQualityEstimator::MaybeComputeEffectiveConnectionType() { 1458 void NetworkQualityEstimator::MaybeComputeEffectiveConnectionType() {
1459 DCHECK(thread_checker_.CalledOnValidThread()); 1459 DCHECK(thread_checker_.CalledOnValidThread());
1460 1460
1461 const base::TimeTicks now = tick_clock_->NowTicks(); 1461 const base::TimeTicks now = tick_clock_->NowTicks();
1462 // Recompute effective connection type only if 1462 // Recompute effective connection type only if
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
1529 void NetworkQualityEstimator::OnPrefsRead( 1529 void NetworkQualityEstimator::OnPrefsRead(
1530 const std::map<nqe::internal::NetworkID, 1530 const std::map<nqe::internal::NetworkID,
1531 nqe::internal::CachedNetworkQuality> read_prefs) { 1531 nqe::internal::CachedNetworkQuality> read_prefs) {
1532 DCHECK(thread_checker_.CalledOnValidThread()); 1532 DCHECK(thread_checker_.CalledOnValidThread());
1533 UMA_HISTOGRAM_COUNTS("NQE.Prefs.ReadSize", read_prefs.size()); 1533 UMA_HISTOGRAM_COUNTS("NQE.Prefs.ReadSize", read_prefs.size());
1534 // TODO(tbansal): crbug.com/490870. Incorporate the network quality into the 1534 // TODO(tbansal): crbug.com/490870. Incorporate the network quality into the
1535 // current estimates. 1535 // current estimates.
1536 } 1536 }
1537 1537
1538 } // namespace net 1538 } // namespace net
OLDNEW
« no previous file with comments | « no previous file | net/nqe/network_quality_estimator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698