Send DPR client hint on main frame requests

Compute DPR in the browser process and include it as a
client hint (if the origin requested it) on main
frame requests.

Bug: 821954
Change-Id: I73a2017d426cb6d9403d41bc89e3107da44034b2
Reviewed-on: https://chromium-review.googlesource.com/963315
Reviewed-by: Ryan Sturm <[email protected]>
Commit-Queue: Tarun Bansal <[email protected]>
Cr-Commit-Position: refs/heads/master@{#543209}
diff --git a/chrome/browser/client_hints/client_hints.cc b/chrome/browser/client_hints/client_hints.cc
index afc02919..eed74382 100644
--- a/chrome/browser/client_hints/client_hints.cc
+++ b/chrome/browser/client_hints/client_hints.cc
@@ -6,6 +6,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/client_hints/client_hints.h"
@@ -21,8 +22,15 @@
 #include "third_party/WebKit/public/common/client_hints/client_hints.h"
 #include "third_party/WebKit/public/common/device_memory/approximated_device_memory.h"
 #include "third_party/WebKit/public/platform/WebClientHintsType.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "url/gurl.h"
 
+#if !defined(OS_ANDROID)
+#include "content/public/browser/host_zoom_map.h"
+#include "content/public/common/page_zoom.h"
+#endif  // !OS_ANDROID
+
 namespace {
 
 bool IsJavaScriptAllowed(Profile* profile, const GURL& url) {
@@ -92,6 +100,42 @@
         base::NumberToString(device_memory));
   }
 
+  if (web_client_hints.IsEnabled(blink::mojom::WebClientHintsType::kDpr)) {
+    double device_scale_factor = 1.0;
+    if (display::Screen::GetScreen()) {
+      device_scale_factor = display::Screen::GetScreen()
+                                ->GetPrimaryDisplay()
+                                .device_scale_factor();
+    }
+
+    double zoom_factor = 1.0;
+
+// Android does not have the concept of zooming in like desktop.
+#if !defined(OS_ANDROID)
+    double zoom_level =
+        content::HostZoomMap::GetDefaultForBrowserContext(context)
+            ->GetZoomLevelForHostAndScheme(url.scheme(),
+                                           net::GetHostOrSpecFromURL(url));
+
+    double default_zoom_level =
+        content::HostZoomMap::GetDefaultForBrowserContext(context)
+            ->GetDefaultZoomLevel();
+
+    if (zoom_level <= 0.0)
+      zoom_level = default_zoom_level;
+
+    if (zoom_level > 0.0)
+      zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
+#endif  // !OS_ANDROID
+
+    DCHECK_LT(0.0, device_scale_factor);
+
+    additional_headers->SetHeader(
+        blink::kClientHintsHeaderMapping[static_cast<int>(
+            blink::mojom::WebClientHintsType::kDpr)],
+        base::NumberToString(device_scale_factor * zoom_factor));
+  }
+
   // Static assert that triggers if a new client hint header is added. If a new
   // client hint header is added, the following assertion should be updated.
   // If possible, logic should be added above so that the request headers for
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index 6d87646..b96c11b 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -242,8 +242,8 @@
       // device-memory header is attached to the main frame request.
       EXPECT_EQ(expect_client_hints_on_main_frame_,
                 base::ContainsKey(request.headers, "device-memory"));
-      // Currently, dpr header is never attached on the main frame request.
-      EXPECT_FALSE(base::ContainsKey(request.headers, "dpr"));
+      EXPECT_EQ(expect_client_hints_on_main_frame_,
+                base::ContainsKey(request.headers, "dpr"));
     }
 
     if (!is_main_frame_navigation) {
@@ -350,8 +350,8 @@
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
   // Two client hints are attached to the image request, and the device-memory
-  // header is attached to the main frame request.
-  EXPECT_EQ(3u, count_client_hints_headers_seen());
+  // and dpr headers are attached to the main frame request.
+  EXPECT_EQ(4u, count_client_hints_headers_seen());
 
   // Navigating to without_accept_ch_without_lifetime_img_foo_com() should not
   // attach client hints to the image subresouce contained in that page since
@@ -362,8 +362,8 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  // The device-memory header is attached to the main frame request.
-  EXPECT_EQ(4u, count_client_hints_headers_seen());
+  // The device-memory and dprheader is attached to the main frame request.
+  EXPECT_EQ(6u, count_client_hints_headers_seen());
   // Requests to third party servers should not have client hints attached.
   EXPECT_EQ(1u, third_party_request_count_seen());
   EXPECT_EQ(0u, third_party_client_hints_count_seen());
@@ -442,7 +442,7 @@
 
   // Two client hints are attached to the image request, and the device-memory
   // header is attached to the main frame request.
-  EXPECT_EQ(3u, count_client_hints_headers_seen());
+  EXPECT_EQ(4u, count_client_hints_headers_seen());
 }
 
 // Loads a webpage that does not request persisting of client hints.
@@ -500,8 +500,8 @@
                                without_accept_ch_without_lifetime_url());
 
   // Two client hints are attached to the image request, and the device-memory
-  // header is attached to the main frame request.
-  EXPECT_EQ(3u, count_client_hints_headers_seen());
+  // and dpr headers are attached to the main frame request.
+  EXPECT_EQ(4u, count_client_hints_headers_seen());
 }
 
 // Ensure that when cookies are blocked, client hint preferences are not
@@ -594,7 +594,7 @@
                                without_accept_ch_without_lifetime_url());
   // Two client hints are attached to the image request, and the device-memory
   // header is attached to the main frame request.
-  EXPECT_EQ(3u, count_client_hints_headers_seen());
+  EXPECT_EQ(4u, count_client_hints_headers_seen());
 
   // Clear settings.
   HostContentSettingsMapFactory::GetForProfile(browser()->profile())
@@ -688,8 +688,8 @@
   ui_test_utils::NavigateToURL(browser(),
                                without_accept_ch_without_lifetime_url());
   // Two client hints are attached to the image request, and the device-memory
-  // header is attached to the main frame request.
-  EXPECT_EQ(3u, count_client_hints_headers_seen());
+  // and dpr headers are attached to the main frame request.
+  EXPECT_EQ(4u, count_client_hints_headers_seen());
 
   // Clear settings.
   HostContentSettingsMapFactory::GetForProfile(browser()->profile())