1 // Copyright 2013 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.
4 //
5 // Generating a fingerprint consists of two major steps:
6 // (1) Gather all the necessary data.
7 // (2) Write it into a protocol buffer.
8 //
9 // Step (2) is as simple as it sounds -- it's really just a matter of copying
10 // data. Step (1) requires waiting on several asynchronous callbacks, which are
11 // managed by the FingerprintDataLoader class.
12
13 #include "components/autofill/content/browser/risk/fingerprint.h"
14
15 #include "base/bind.h"
16 #include "base/callback.h"
17 #include "base/cpu.h"
18 #include "base/logging.h"
19 #include "base/memory/weak_ptr.h"
20 #include "base/scoped_observer.h"
21 #include "base/strings/string_split.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/sys_info.h"
24 #include "base/time/time.h"
25 #include "base/timer/timer.h"
26 #include "base/values.h"
27 #include "components/autofill/content/browser/risk/proto/fingerprint.pb.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/font_list_async.h"
30 #include "content/public/browser/geolocation_provider.h"
31 #include "content/public/browser/gpu_data_manager.h"
32 #include "content/public/browser/gpu_data_manager_observer.h"
33 #include "content/public/browser/plugin_service.h"
34 #include "content/public/browser/render_widget_host.h"
35 #include "content/public/browser/render_widget_host_view.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/browser/web_contents_view.h"
38 #include "content/public/common/content_client.h"
39 #include "content/public/common/geoposition.h"
40 #include "content/public/common/webplugininfo.h"
41 #include "gpu/config/gpu_info.h"
42 #include "third_party/WebKit/public/platform/WebRect.h"
43 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
44 #include "ui/gfx/rect.h"
45 #include "ui/gfx/screen.h"
46
47 using blink::WebScreenInfo;
48
49 namespace autofill {
50 namespace risk {
51
52 namespace {
53
54 const int32 kFingerprinterVersion = 1;
55
56 // Maximum amount of time, in seconds, to wait for loading asynchronous
57 // fingerprint data.
58 const int kTimeoutSeconds = 4;
59
60 // Returns the delta between the local timezone and UTC.
GetTimezoneOffset()61 base::TimeDelta GetTimezoneOffset() {
62 const base::Time utc = base::Time::Now();
63
64 base::Time::Exploded local;
65 utc.LocalExplode(&local);
66
67 return base::Time::FromUTCExploded(local) - utc;
68 }
69
70 // Returns the concatenation of the operating system name and version, e.g.
71 // "Mac OS X 10.6.8".
GetOperatingSystemVersion()72 std::string GetOperatingSystemVersion() {
73 return base::SysInfo::OperatingSystemName() + " " +
74 base::SysInfo::OperatingSystemVersion();
75 }
76
77 // Adds the list of |fonts| to the |machine|.
AddFontsToFingerprint(const base::ListValue & fonts,Fingerprint::MachineCharacteristics * machine)78 void AddFontsToFingerprint(const base::ListValue& fonts,
79 Fingerprint::MachineCharacteristics* machine) {
80 for (base::ListValue::const_iterator it = fonts.begin();
81 it != fonts.end(); ++it) {
82 // Each item in the list is a two-element list such that the first element
83 // is the font family and the second is the font name.
84 const base::ListValue* font_description = NULL;
85 bool success = (*it)->GetAsList(&font_description);
86 DCHECK(success);
87
88 std::string font_name;
89 success = font_description->GetString(1, &font_name);
90 DCHECK(success);
91
92 machine->add_font(font_name);
93 }
94 }
95
96 // Adds the list of |plugins| to the |machine|.
AddPluginsToFingerprint(const std::vector<content::WebPluginInfo> & plugins,Fingerprint::MachineCharacteristics * machine)97 void AddPluginsToFingerprint(const std::vector<content::WebPluginInfo>& plugins,
98 Fingerprint::MachineCharacteristics* machine) {
99 for (std::vector<content::WebPluginInfo>::const_iterator it = plugins.begin();
100 it != plugins.end(); ++it) {
101 Fingerprint::MachineCharacteristics::Plugin* plugin =
102 machine->add_plugin();
103 plugin->set_name(UTF16ToUTF8(it->name));
104 plugin->set_description(UTF16ToUTF8(it->desc));
105 for (std::vector<content::WebPluginMimeType>::const_iterator mime_type =
106 it->mime_types.begin();
107 mime_type != it->mime_types.end(); ++mime_type) {
108 plugin->add_mime_type(mime_type->mime_type);
109 }
110 plugin->set_version(UTF16ToUTF8(it->version));
111 }
112 }
113
114 // Adds the list of HTTP accept languages to the |machine|.
AddAcceptLanguagesToFingerprint(const std::string & accept_languages_str,Fingerprint::MachineCharacteristics * machine)115 void AddAcceptLanguagesToFingerprint(
116 const std::string& accept_languages_str,
117 Fingerprint::MachineCharacteristics* machine) {
118 std::vector<std::string> accept_languages;
119 base::SplitString(accept_languages_str, ',', &accept_languages);
120 for (std::vector<std::string>::const_iterator it = accept_languages.begin();
121 it != accept_languages.end(); ++it) {
122 machine->add_requested_language(*it);
123 }
124 }
125
126 // This function writes
127 // (a) the number of screens,
128 // (b) the primary display's screen size,
129 // (c) the screen's color depth, and
130 // (d) the size of the screen unavailable to web page content,
131 // i.e. the Taskbar size on Windows
132 // into the |machine|.
AddScreenInfoToFingerprint(const WebScreenInfo & screen_info,Fingerprint::MachineCharacteristics * machine)133 void AddScreenInfoToFingerprint(const WebScreenInfo& screen_info,
134 Fingerprint::MachineCharacteristics* machine) {
135 // TODO(scottmg): NativeScreen maybe wrong. http://crbug.com/133312
136 machine->set_screen_count(
137 gfx::Screen::GetNativeScreen()->GetNumDisplays());
138
139 const gfx::Size screen_size =
140 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().GetSizeInPixel();
141 machine->mutable_screen_size()->set_width(screen_size.width());
142 machine->mutable_screen_size()->set_height(screen_size.height());
143
144 machine->set_screen_color_depth(screen_info.depth);
145
146 const gfx::Rect screen_rect(screen_info.rect);
147 const gfx::Rect available_rect(screen_info.availableRect);
148 const gfx::Rect unavailable_rect =
149 gfx::SubtractRects(screen_rect, available_rect);
150 machine->mutable_unavailable_screen_size()->set_width(
151 unavailable_rect.width());
152 machine->mutable_unavailable_screen_size()->set_height(
153 unavailable_rect.height());
154 }
155
156 // Writes info about the machine's CPU into the |machine|.
AddCpuInfoToFingerprint(Fingerprint::MachineCharacteristics * machine)157 void AddCpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine) {
158 base::CPU cpu;
159 machine->mutable_cpu()->set_vendor_name(cpu.vendor_name());
160 machine->mutable_cpu()->set_brand(cpu.cpu_brand());
161 }
162
163 // Writes info about the machine's GPU into the |machine|.
AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics * machine)164 void AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine) {
165 const gpu::GPUInfo& gpu_info =
166 content::GpuDataManager::GetInstance()->GetGPUInfo();
167 if (!gpu_info.finalized)
168 return;
169
170 Fingerprint::MachineCharacteristics::Graphics* graphics =
171 machine->mutable_graphics_card();
172 graphics->set_vendor_id(gpu_info.gpu.vendor_id);
173 graphics->set_device_id(gpu_info.gpu.device_id);
174 graphics->set_driver_version(gpu_info.driver_version);
175 graphics->set_driver_date(gpu_info.driver_date);
176
177 Fingerprint::MachineCharacteristics::Graphics::PerformanceStatistics*
178 gpu_performance = graphics->mutable_performance_statistics();
179 gpu_performance->set_graphics_score(gpu_info.performance_stats.graphics);
180 gpu_performance->set_gaming_score(gpu_info.performance_stats.gaming);
181 gpu_performance->set_overall_score(gpu_info.performance_stats.overall);
182 }
183
184 // Waits for geoposition data to be loaded. Lives on the IO thread.
185 class GeopositionLoader {
186 public:
187 // |callback_| will be called on the UI thread with the loaded geoposition,
188 // once it is available.
189 GeopositionLoader(
190 const base::TimeDelta& timeout,
191 const base::Callback<void(const content::Geoposition&)>& callback);
~GeopositionLoader()192 ~GeopositionLoader() {}
193
194 private:
195 // Methods to communicate with the GeolocationProvider.
196 void OnGotGeoposition(const content::Geoposition& geoposition);
197
198 // The callback that will be called once the geoposition is available.
199 // Will be called on the UI thread.
200 const base::Callback<void(const content::Geoposition&)> callback_;
201
202 // The callback used as an "observer" of the GeolocationProvider.
203 content::GeolocationProvider::LocationUpdateCallback geolocation_callback_;
204
205 // Timer to enforce a maximum timeout before the |callback_| is called, even
206 // if the geoposition has not been loaded.
207 base::OneShotTimer<GeopositionLoader> timeout_timer_;
208 };
209
GeopositionLoader(const base::TimeDelta & timeout,const base::Callback<void (const content::Geoposition &)> & callback)210 GeopositionLoader::GeopositionLoader(
211 const base::TimeDelta& timeout,
212 const base::Callback<void(const content::Geoposition&)>& callback)
213 : callback_(callback) {
214 timeout_timer_.Start(FROM_HERE, timeout,
215 base::Bind(&GeopositionLoader::OnGotGeoposition,
216 base::Unretained(this),
217 content::Geoposition()));
218
219 geolocation_callback_ =
220 base::Bind(&GeopositionLoader::OnGotGeoposition, base::Unretained(this));
221 content::GeolocationProvider::GetInstance()->AddLocationUpdateCallback(
222 geolocation_callback_, false);
223 }
224
OnGotGeoposition(const content::Geoposition & geoposition)225 void GeopositionLoader::OnGotGeoposition(
226 const content::Geoposition& geoposition) {
227 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
228 base::Bind(callback_, geoposition));
229
230 // Unregister as an observer, since this class instance might be destroyed
231 // after this callback. Note: It's important to unregister *after* posting
232 // the task above. Unregistering as an observer can have the side-effect of
233 // modifying the value of |geoposition|.
234 bool removed =
235 content::GeolocationProvider::GetInstance()->RemoveLocationUpdateCallback(
236 geolocation_callback_);
237 DCHECK(removed);
238
239 delete this;
240 }
241
242 // Asynchronously loads the user's current geoposition and calls |callback_| on
243 // the UI thread with the loaded geoposition, once it is available. Expected to
244 // be called on the IO thread.
LoadGeoposition(const base::TimeDelta & timeout,const base::Callback<void (const content::Geoposition &)> & callback)245 void LoadGeoposition(
246 const base::TimeDelta& timeout,
247 const base::Callback<void(const content::Geoposition&)>& callback) {
248 // The loader is responsible for freeing its own memory.
249 new GeopositionLoader(timeout, callback);
250 }
251
252 // Waits for all asynchronous data required for the fingerprint to be loaded,
253 // then fills out the fingerprint.
254 class FingerprintDataLoader : public content::GpuDataManagerObserver {
255 public:
256 FingerprintDataLoader(
257 uint64 obfuscated_gaia_id,
258 const gfx::Rect& window_bounds,
259 const gfx::Rect& content_bounds,
260 const WebScreenInfo& screen_info,
261 const std::string& version,
262 const std::string& charset,
263 const std::string& accept_languages,
264 const base::Time& install_time,
265 const std::string& app_locale,
266 const base::TimeDelta& timeout,
267 const base::Callback<void(scoped_ptr<Fingerprint>)>& callback);
268
269 private:
~FingerprintDataLoader()270 virtual ~FingerprintDataLoader() {}
271
272 // content::GpuDataManagerObserver:
273 virtual void OnGpuInfoUpdate() OVERRIDE;
274
275 // Callbacks for asynchronously loaded data.
276 void OnGotFonts(scoped_ptr<base::ListValue> fonts);
277 void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins);
278 void OnGotGeoposition(const content::Geoposition& geoposition);
279
280 // If all of the asynchronous data has been loaded, calls |callback_| with
281 // the fingerprint data.
282 void MaybeFillFingerprint();
283
284 // Calls |callback_| with the fingerprint data.
285 void FillFingerprint();
286
287 // The GPU data provider.
288 // Weak reference because the GpuDataManager class is a singleton.
289 content::GpuDataManager* const gpu_data_manager_;
290
291 // Ensures that any observer registrations for the GPU data are cleaned up by
292 // the time this object is destroyed.
293 ScopedObserver<content::GpuDataManager, FingerprintDataLoader> gpu_observer_;
294
295 // Data that will be passed on to the next loading phase. See the comment for
296 // GetFingerprint() for a description of these variables.
297 const uint64 obfuscated_gaia_id_;
298 const gfx::Rect window_bounds_;
299 const gfx::Rect content_bounds_;
300 const WebScreenInfo screen_info_;
301 const std::string version_;
302 const std::string charset_;
303 const std::string accept_languages_;
304 const base::Time install_time_;
305
306 // Data that will be loaded asynchronously.
307 scoped_ptr<base::ListValue> fonts_;
308 std::vector<content::WebPluginInfo> plugins_;
309 bool waiting_on_plugins_;
310 content::Geoposition geoposition_;
311
312 // Timer to enforce a maximum timeout before the |callback_| is called, even
313 // if not all asynchronous data has been loaded.
314 base::OneShotTimer<FingerprintDataLoader> timeout_timer_;
315
316 // For invalidating asynchronous callbacks that might arrive after |this|
317 // instance is destroyed.
318 base::WeakPtrFactory<FingerprintDataLoader> weak_ptr_factory_;
319
320 // The current application locale.
321 std::string app_locale_;
322
323 // The callback that will be called once all the data is available.
324 base::Callback<void(scoped_ptr<Fingerprint>)> callback_;
325
326 DISALLOW_COPY_AND_ASSIGN(FingerprintDataLoader);
327 };
328
FingerprintDataLoader(uint64 obfuscated_gaia_id,const gfx::Rect & window_bounds,const gfx::Rect & content_bounds,const WebScreenInfo & screen_info,const std::string & version,const std::string & charset,const std::string & accept_languages,const base::Time & install_time,const std::string & app_locale,const base::TimeDelta & timeout,const base::Callback<void (scoped_ptr<Fingerprint>)> & callback)329 FingerprintDataLoader::FingerprintDataLoader(
330 uint64 obfuscated_gaia_id,
331 const gfx::Rect& window_bounds,
332 const gfx::Rect& content_bounds,
333 const WebScreenInfo& screen_info,
334 const std::string& version,
335 const std::string& charset,
336 const std::string& accept_languages,
337 const base::Time& install_time,
338 const std::string& app_locale,
339 const base::TimeDelta& timeout,
340 const base::Callback<void(scoped_ptr<Fingerprint>)>& callback)
341 : gpu_data_manager_(content::GpuDataManager::GetInstance()),
342 gpu_observer_(this),
343 obfuscated_gaia_id_(obfuscated_gaia_id),
344 window_bounds_(window_bounds),
345 content_bounds_(content_bounds),
346 screen_info_(screen_info),
347 version_(version),
348 charset_(charset),
349 accept_languages_(accept_languages),
350 install_time_(install_time),
351 waiting_on_plugins_(true),
352 weak_ptr_factory_(this),
353 callback_(callback) {
354 DCHECK(!install_time_.is_null());
355
356 timeout_timer_.Start(FROM_HERE, timeout,
357 base::Bind(&FingerprintDataLoader::MaybeFillFingerprint,
358 weak_ptr_factory_.GetWeakPtr()));
359
360 // Load GPU data if needed.
361 if (!gpu_data_manager_->IsCompleteGpuInfoAvailable()) {
362 gpu_observer_.Add(gpu_data_manager_);
363 gpu_data_manager_->RequestCompleteGpuInfoIfNeeded();
364 }
365
366 #if defined(ENABLE_PLUGINS)
367 // Load plugin data.
368 content::PluginService::GetInstance()->GetPlugins(
369 base::Bind(&FingerprintDataLoader::OnGotPlugins,
370 weak_ptr_factory_.GetWeakPtr()));
371 #else
372 waiting_on_plugins_ = false;
373 #endif
374
375 // Load font data.
376 content::GetFontListAsync(
377 base::Bind(&FingerprintDataLoader::OnGotFonts,
378 weak_ptr_factory_.GetWeakPtr()));
379
380 // Load geolocation data.
381 content::BrowserThread::PostTask(
382 content::BrowserThread::IO, FROM_HERE,
383 base::Bind(&LoadGeoposition,
384 timeout,
385 base::Bind(&FingerprintDataLoader::OnGotGeoposition,
386 weak_ptr_factory_.GetWeakPtr())));
387 }
388
OnGpuInfoUpdate()389 void FingerprintDataLoader::OnGpuInfoUpdate() {
390 if (!gpu_data_manager_->IsCompleteGpuInfoAvailable())
391 return;
392
393 gpu_observer_.Remove(gpu_data_manager_);
394 MaybeFillFingerprint();
395 }
396
OnGotFonts(scoped_ptr<base::ListValue> fonts)397 void FingerprintDataLoader::OnGotFonts(scoped_ptr<base::ListValue> fonts) {
398 DCHECK(!fonts_);
399 fonts_.reset(fonts.release());
400 MaybeFillFingerprint();
401 }
402
OnGotPlugins(const std::vector<content::WebPluginInfo> & plugins)403 void FingerprintDataLoader::OnGotPlugins(
404 const std::vector<content::WebPluginInfo>& plugins) {
405 DCHECK(waiting_on_plugins_);
406 waiting_on_plugins_ = false;
407 plugins_ = plugins;
408 MaybeFillFingerprint();
409 }
410
OnGotGeoposition(const content::Geoposition & geoposition)411 void FingerprintDataLoader::OnGotGeoposition(
412 const content::Geoposition& geoposition) {
413 DCHECK(!geoposition_.Validate());
414
415 geoposition_ = geoposition;
416 DCHECK(geoposition_.Validate() ||
417 geoposition_.error_code != content::Geoposition::ERROR_CODE_NONE);
418
419 MaybeFillFingerprint();
420 }
421
MaybeFillFingerprint()422 void FingerprintDataLoader::MaybeFillFingerprint() {
423 // If all of the data has been loaded, or if the |timeout_timer_| has expired,
424 // fill the fingerprint and clean up.
425 if (!timeout_timer_.IsRunning() ||
426 (gpu_data_manager_->IsCompleteGpuInfoAvailable() &&
427 fonts_ &&
428 !waiting_on_plugins_ &&
429 (geoposition_.Validate() ||
430 geoposition_.error_code != content::Geoposition::ERROR_CODE_NONE))) {
431 FillFingerprint();
432 delete this;
433 }
434 }
435
FillFingerprint()436 void FingerprintDataLoader::FillFingerprint() {
437 scoped_ptr<Fingerprint> fingerprint(new Fingerprint);
438 Fingerprint::MachineCharacteristics* machine =
439 fingerprint->mutable_machine_characteristics();
440
441 machine->set_operating_system_build(GetOperatingSystemVersion());
442 // We use the delta between the install time and the Unix epoch, in hours.
443 machine->set_browser_install_time_hours(
444 (install_time_ - base::Time::UnixEpoch()).InHours());
445 machine->set_utc_offset_ms(GetTimezoneOffset().InMilliseconds());
446 machine->set_browser_language(app_locale_);
447 machine->set_charset(charset_);
448 machine->set_user_agent(content::GetUserAgent(GURL()));
449 machine->set_ram(base::SysInfo::AmountOfPhysicalMemory());
450 machine->set_browser_build(version_);
451 machine->set_browser_feature(
452 Fingerprint::MachineCharacteristics::FEATURE_REQUEST_AUTOCOMPLETE);
453 if (fonts_)
454 AddFontsToFingerprint(*fonts_, machine);
455 AddPluginsToFingerprint(plugins_, machine);
456 AddAcceptLanguagesToFingerprint(accept_languages_, machine);
457 AddScreenInfoToFingerprint(screen_info_, machine);
458 AddCpuInfoToFingerprint(machine);
459 AddGpuInfoToFingerprint(machine);
460
461 // TODO(isherman): Record the user_and_device_name_hash.
462 // TODO(isherman): Record the partition size of the hard drives?
463
464 Fingerprint::TransientState* transient_state =
465 fingerprint->mutable_transient_state();
466 Fingerprint::Dimension* inner_window_size =
467 transient_state->mutable_inner_window_size();
468 inner_window_size->set_width(content_bounds_.width());
469 inner_window_size->set_height(content_bounds_.height());
470 Fingerprint::Dimension* outer_window_size =
471 transient_state->mutable_outer_window_size();
472 outer_window_size->set_width(window_bounds_.width());
473 outer_window_size->set_height(window_bounds_.height());
474
475 // TODO(isherman): Record network performance data, which is theoretically
476 // available to JS.
477
478 // TODO(isherman): Record more user behavior data.
479 if (geoposition_.Validate() &&
480 geoposition_.error_code == content::Geoposition::ERROR_CODE_NONE) {
481 Fingerprint::UserCharacteristics::Location* location =
482 fingerprint->mutable_user_characteristics()->mutable_location();
483 location->set_altitude(geoposition_.altitude);
484 location->set_latitude(geoposition_.latitude);
485 location->set_longitude(geoposition_.longitude);
486 location->set_accuracy(geoposition_.accuracy);
487 location->set_time_in_ms(
488 (geoposition_.timestamp - base::Time::UnixEpoch()).InMilliseconds());
489 }
490
491 Fingerprint::Metadata* metadata = fingerprint->mutable_metadata();
492 metadata->set_timestamp_ms(
493 (base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds());
494 metadata->set_obfuscated_gaia_id(obfuscated_gaia_id_);
495 metadata->set_fingerprinter_version(kFingerprinterVersion);
496
497 callback_.Run(fingerprint.Pass());
498 }
499
500 } // namespace
501
502 namespace internal {
503
GetFingerprintInternal(uint64 obfuscated_gaia_id,const gfx::Rect & window_bounds,const gfx::Rect & content_bounds,const blink::WebScreenInfo & screen_info,const std::string & version,const std::string & charset,const std::string & accept_languages,const base::Time & install_time,const std::string & app_locale,const base::TimeDelta & timeout,const base::Callback<void (scoped_ptr<Fingerprint>)> & callback)504 void GetFingerprintInternal(
505 uint64 obfuscated_gaia_id,
506 const gfx::Rect& window_bounds,
507 const gfx::Rect& content_bounds,
508 const blink::WebScreenInfo& screen_info,
509 const std::string& version,
510 const std::string& charset,
511 const std::string& accept_languages,
512 const base::Time& install_time,
513 const std::string& app_locale,
514 const base::TimeDelta& timeout,
515 const base::Callback<void(scoped_ptr<Fingerprint>)>& callback) {
516 // Begin loading all of the data that we need to load asynchronously.
517 // This class is responsible for freeing its own memory.
518 new FingerprintDataLoader(obfuscated_gaia_id, window_bounds, content_bounds,
519 screen_info, version, charset, accept_languages,
520 install_time, app_locale, timeout, callback);
521 }
522
523 } // namespace internal
524
GetFingerprint(uint64 obfuscated_gaia_id,const gfx::Rect & window_bounds,const content::WebContents & web_contents,const std::string & version,const std::string & charset,const std::string & accept_languages,const base::Time & install_time,const std::string & app_locale,const base::Callback<void (scoped_ptr<Fingerprint>)> & callback)525 void GetFingerprint(
526 uint64 obfuscated_gaia_id,
527 const gfx::Rect& window_bounds,
528 const content::WebContents& web_contents,
529 const std::string& version,
530 const std::string& charset,
531 const std::string& accept_languages,
532 const base::Time& install_time,
533 const std::string& app_locale,
534 const base::Callback<void(scoped_ptr<Fingerprint>)>& callback) {
535 gfx::Rect content_bounds;
536 web_contents.GetView()->GetContainerBounds(&content_bounds);
537
538 blink::WebScreenInfo screen_info;
539 content::RenderWidgetHostView* host_view =
540 web_contents.GetRenderWidgetHostView();
541 if (host_view)
542 host_view->GetRenderWidgetHost()->GetWebScreenInfo(&screen_info);
543
544 internal::GetFingerprintInternal(
545 obfuscated_gaia_id, window_bounds, content_bounds, screen_info, version,
546 charset, accept_languages, install_time, app_locale,
547 base::TimeDelta::FromSeconds(kTimeoutSeconds), callback);
548 }
549
550 } // namespace risk
551 } // namespace autofill
552