Add multi-state DNS interception policy - functionality piece.

For enterprises: enterprises may enable the feature, or enable
did-you-mean without checks, as they know their machines' path to
DNS servers.

For non-enterprise users:
Adds a feature to roll out a new default-off behavior.
A heuristic-based approach for non-enterprise users will follow, though
likely in a future milestone.

Bug: 1090985, 1090985

Change-Id: I536129341c3a3eced640dd7af9d3cdf025ee28c0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2380212
Commit-Queue: Travis Skare <skare@chromium.org>
Reviewed-by: Rohit Rao <rohitrao@chromium.org>
Reviewed-by: Owen Min <zmin@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Tommy Li <tommycli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#823972}
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index fff6990..6f917085 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -200,6 +200,10 @@
   return profile_->GetPrefs();
 }
 
+PrefService* ChromeAutocompleteProviderClient::GetLocalState() {
+  return g_browser_process->local_state();
+}
+
 const AutocompleteSchemeClassifier&
 ChromeAutocompleteProviderClient::GetSchemeClassifier() const {
   return scheme_classifier_;
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
index fcb6fae..e431b9d 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
@@ -37,6 +37,7 @@
   // AutocompleteProviderClient:
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   PrefService* GetPrefs() override;
+  PrefService* GetLocalState() override;
   const AutocompleteSchemeClassifier& GetSchemeClassifier() const override;
   AutocompleteClassifier* GetAutocompleteClassifier() override;
   history::HistoryService* GetHistoryService() override;
diff --git a/chrome/browser/intranet_redirect_detector.cc b/chrome/browser/intranet_redirect_detector.cc
index 56eb9cf..2e6014b 100644
--- a/chrome/browser/intranet_redirect_detector.cc
+++ b/chrome/browser/intranet_redirect_detector.cc
@@ -10,6 +10,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/location.h"
 #include "base/rand_util.h"
 #include "base/single_thread_task_runner.h"
@@ -18,8 +19,13 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "components/omnibox/browser/intranet_redirector_state.h"
+#include "components/omnibox/browser/omnibox_prefs.h"
+#include "components/omnibox/common/omnibox_features.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
@@ -73,7 +79,7 @@
   registry->RegisterStringPref(prefs::kLastKnownIntranetRedirectOrigin,
                                std::string());
   registry->RegisterBooleanPref(prefs::kDNSInterceptionChecksEnabled, true);
-  registry->RegisterIntegerPref(prefs::kIntranetRedirectBehavior, 0);
+  registry->RegisterIntegerPref(omnibox::kIntranetRedirectBehavior, 0);
 }
 
 void IntranetRedirectDetector::Restart() {
@@ -260,6 +266,24 @@
 }
 
 bool IntranetRedirectDetector::IsEnabledByPolicy() {
-  return g_browser_process->local_state()->GetBoolean(
-      prefs::kDNSInterceptionChecksEnabled);
+  // The InterceptionChecksBehavior pref and the older
+  // DNSInterceptionChecksEnabled policy should each be able to disable
+  // interception checks. Therefore, we enable the redirect detector iff allowed
+  // by both policies.
+
+  // Check IntranetRedirectorBehavior pref and experiment.
+  auto behavior =
+      omnibox::GetInterceptionChecksBehavior(g_browser_process->local_state());
+  if (behavior == omnibox::IntranetRedirectorBehavior::DISABLE_FEATURE ||
+      behavior == omnibox::IntranetRedirectorBehavior::
+                      DISABLE_INTERCEPTION_CHECKS_ENABLE_INFOBARS) {
+    return false;
+  }
+
+  // Consult previous DNSInterceptionChecksEnabled policy.
+  if (!g_browser_process->local_state()->GetBoolean(
+          prefs::kDNSInterceptionChecksEnabled))
+    return false;
+
+  return true;
 }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 6c059a0..b4bd7c6 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -598,7 +598,7 @@
     prefs::kDNSInterceptionChecksEnabled,
     base::Value::Type::BOOLEAN },
   { key::kIntranetRedirectBehavior,
-    prefs::kIntranetRedirectBehavior,
+    omnibox::kIntranetRedirectBehavior,
     base::Value::Type::INTEGER },
   { key::kAdvancedProtectionAllowed,
     prefs::kAdvancedProtectionAllowed,
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index eab4cf7..1949597 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1764,19 +1764,11 @@
 
 // Boolean specifying that the intranet redirect detector should be enabled.
 // Defaults to true.
-// Will be replaced by DNSInterceptionChecksBehavior, below; see notes for that
-// function.
+// See also kIntranetRedirectBehavior in the omnibox component's prefs, which
+// also impacts the redirect detector.
 const char kDNSInterceptionChecksEnabled[] =
     "browser.dns_interception_checks_enabled";
 
-// Enum specifying the active behavior for the intranet redirect detector.
-// This will replace kDNSInterceptionChecksEnabled over time, though
-// currently both policies are in use. When the Enabled policy is set to false,
-// it effectively short-circuits the behavior, and it cannot be enabled by this
-// Behavior policy. Alternatively the policies can be considered to AND
-// together. Values are defined in IntranetRedirectDetector::RedirectorBehavior.
-const char kIntranetRedirectBehavior[] = "browser.intranet_redirect_behavior";
-
 // An enum value of how the browser was shut down (see browser_shutdown.h).
 const char kShutdownType[] = "shutdown.type";
 // Number of processes that were open when the user shut down.
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 701a48d..40ee7e5 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -127,6 +127,8 @@
     "in_memory_url_index_types.h",
     "inline_autocompletion_util.cc",
     "inline_autocompletion_util.h",
+    "intranet_redirector_state.cc",
+    "intranet_redirector_state.h",
     "keyword_extensions_delegate.cc",
     "keyword_extensions_delegate.h",
     "keyword_provider.cc",
@@ -438,6 +440,7 @@
     "//components/bookmarks/test",
     "//components/history/core/browser",
     "//components/history/core/test",
+    "//components/prefs:test_support",
     "//components/query_tiles/test:test_support",
     "//components/resources",
     "//components/search_engines",
diff --git a/components/omnibox/browser/autocomplete_classifier.cc b/components/omnibox/browser/autocomplete_classifier.cc
index 8bcba27..f4e962f 100644
--- a/components/omnibox/browser/autocomplete_classifier.cc
+++ b/components/omnibox/browser/autocomplete_classifier.cc
@@ -99,7 +99,7 @@
 
   *match = *default_match;
   if (alternate_nav_url) {
-    *alternate_nav_url =
-        AutocompleteResult::ComputeAlternateNavUrl(input, *match);
+    *alternate_nav_url = AutocompleteResult::ComputeAlternateNavUrl(
+        input, *match, controller_->autocomplete_provider_client());
   }
 }
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index 7aada34..09b72db 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -174,6 +174,11 @@
   // Sets the provider timeout duration for future calls to |Start()|.
   void SetStartStopTimerDurationForTesting(base::TimeDelta duration);
 
+  // Returns the AutocompleteProviderClient owned by the controller.
+  AutocompleteProviderClient* autocomplete_provider_client() const {
+    return provider_client_.get();
+  }
+
  private:
   friend class AutocompleteProviderTest;
   friend class OmniboxSuggestionButtonRowBrowserTest;
diff --git a/components/omnibox/browser/autocomplete_provider_client.h b/components/omnibox/browser/autocomplete_provider_client.h
index bee7622..d368628 100644
--- a/components/omnibox/browser/autocomplete_provider_client.h
+++ b/components/omnibox/browser/autocomplete_provider_client.h
@@ -61,6 +61,7 @@
   virtual scoped_refptr<network::SharedURLLoaderFactory>
   GetURLLoaderFactory() = 0;
   virtual PrefService* GetPrefs() = 0;
+  virtual PrefService* GetLocalState() = 0;
   virtual const AutocompleteSchemeClassifier& GetSchemeClassifier() const = 0;
   virtual AutocompleteClassifier* GetAutocompleteClassifier() = 0;
   virtual history::HistoryService* GetHistoryService() = 0;
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 5657776..102aa3c 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -24,6 +24,7 @@
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/base_search_provider.h"
+#include "components/omnibox/browser/intranet_redirector_state.h"
 #include "components/omnibox/browser/match_compare.h"
 #include "components/omnibox/browser/omnibox_pedal.h"
 #include "components/omnibox/browser/omnibox_pedal_provider.h"
@@ -714,7 +715,19 @@
 // static
 GURL AutocompleteResult::ComputeAlternateNavUrl(
     const AutocompleteInput& input,
-    const AutocompleteMatch& match) {
+    const AutocompleteMatch& match,
+    AutocompleteProviderClient* provider_client) {
+  auto redirector_policy =
+      omnibox::GetInterceptionChecksBehavior(provider_client->GetLocalState());
+
+  bool policy_allows_alternate_navs =
+      (redirector_policy == omnibox::IntranetRedirectorBehavior::
+                                DISABLE_INTERCEPTION_CHECKS_ENABLE_INFOBARS ||
+       redirector_policy == omnibox::IntranetRedirectorBehavior::
+                                ENABLE_INTERCEPTION_CHECKS_AND_INFOBARS);
+  if (!policy_allows_alternate_navs)
+    return GURL();
+
   return ((input.type() == metrics::OmniboxInputType::UNKNOWN) &&
           (AutocompleteMatch::IsSearchType(match.type)) &&
           !ui::PageTransitionCoreTypeIs(match.transition,
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index 1ff7298..f23b5e2 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -172,8 +172,10 @@
 
   // Returns a URL to offer the user as an alternative navigation when they
   // open |match| after typing in |input|.
-  static GURL ComputeAlternateNavUrl(const AutocompleteInput& input,
-                                     const AutocompleteMatch& match);
+  static GURL ComputeAlternateNavUrl(
+      const AutocompleteInput& input,
+      const AutocompleteMatch& match,
+      AutocompleteProviderClient* provider_client);
 
   // Prepend missing tail suggestion prefixes in results, if present.
   void InlineTailPrefixes();
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index fc5296f..c60dd1f 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -26,9 +26,13 @@
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/fake_autocomplete_provider_client.h"
+#include "components/omnibox/browser/intranet_redirector_state.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
+#include "components/omnibox/browser/omnibox_prefs.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/variations/variations_associated_data.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -313,13 +317,18 @@
                           metrics::OmniboxEventProto::OTHER,
                           TestSchemeClassifier());
 
+  FakeAutocompleteProviderClient client;
+  reinterpret_cast<TestingPrefServiceSimple*>(client.GetLocalState())
+      ->registry()
+      ->RegisterIntegerPref(omnibox::kIntranetRedirectBehavior, 0);
+
   // Against search matches, we should generate an alternate nav URL.
   {
     AutocompleteMatch match;
     match.type = AutocompleteMatchType::SEARCH_SUGGEST;
     match.destination_url = GURL("http://www.foo.com/s?q=foo");
     GURL alternate_nav_url =
-        AutocompleteResult::ComputeAlternateNavUrl(input, match);
+        AutocompleteResult::ComputeAlternateNavUrl(input, match, &client);
     EXPECT_EQ("http://a/", alternate_nav_url.spec());
   }
 
@@ -329,7 +338,47 @@
     match.type = AutocompleteMatchType::SEARCH_SUGGEST;
     match.destination_url = GURL("http://a/");
     GURL alternate_nav_url =
-        AutocompleteResult::ComputeAlternateNavUrl(input, match);
+        AutocompleteResult::ComputeAlternateNavUrl(input, match, &client);
+    EXPECT_FALSE(alternate_nav_url.is_valid());
+  }
+}
+
+TEST_F(AutocompleteResultTest, AlternateNavUrl_IntranetRedirectPolicy) {
+  AutocompleteInput input(base::ASCIIToUTF16("a"),
+                          metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+
+  FakeAutocompleteProviderClient client;
+  reinterpret_cast<TestingPrefServiceSimple*>(client.GetLocalState())
+      ->registry()
+      ->RegisterIntegerPref(omnibox::kIntranetRedirectBehavior, 0);
+
+  // Allow alternate nav URLs when policy allows.
+  {
+    client.GetLocalState()->SetInteger(
+        omnibox::kIntranetRedirectBehavior,
+        static_cast<int>(omnibox::IntranetRedirectorBehavior::
+                             ENABLE_INTERCEPTION_CHECKS_AND_INFOBARS));
+
+    AutocompleteMatch match;
+    match.type = AutocompleteMatchType::SEARCH_SUGGEST;
+    match.destination_url = GURL("http://www.foo.com/s?q=foo");
+    GURL alternate_nav_url =
+        AutocompleteResult::ComputeAlternateNavUrl(input, match, &client);
+    EXPECT_EQ("http://a/", alternate_nav_url.spec());
+  }
+
+  // Disallow alternate nav URLs when policy disallows.
+  {
+    client.GetLocalState()->SetInteger(
+        omnibox::kIntranetRedirectBehavior,
+        static_cast<int>(omnibox::IntranetRedirectorBehavior::DISABLE_FEATURE));
+
+    AutocompleteMatch match;
+    match.type = AutocompleteMatchType::SEARCH_SUGGEST;
+    match.destination_url = GURL("http://www.foo.com/s?q=foo");
+    GURL alternate_nav_url =
+        AutocompleteResult::ComputeAlternateNavUrl(input, match, &client);
     EXPECT_FALSE(alternate_nav_url.is_valid());
   }
 }
diff --git a/components/omnibox/browser/fake_autocomplete_provider_client.cc b/components/omnibox/browser/fake_autocomplete_provider_client.cc
index dca40ce..efbb228 100644
--- a/components/omnibox/browser/fake_autocomplete_provider_client.cc
+++ b/components/omnibox/browser/fake_autocomplete_provider_client.cc
@@ -16,6 +16,7 @@
 #include "components/omnibox/browser/in_memory_url_index.h"
 #include "components/omnibox/browser/in_memory_url_index_test_util.h"
 #include "components/omnibox/browser/shortcuts_backend.h"
+#include "components/prefs/testing_pref_service.h"
 #include "components/query_tiles/test/fake_tile_service.h"
 #include "components/search_engines/search_terms_data.h"
 #include "components/search_engines/template_url_service.h"
@@ -35,6 +36,9 @@
                            nullptr, history_dir_.GetPath(), SchemeSet()));
   in_memory_url_index_->Init();
 
+  pref_service_ = std::make_unique<TestingPrefServiceSimple>();
+  local_state_ = std::make_unique<TestingPrefServiceSimple>();
+
   shortcuts_backend_ = base::MakeRefCounted<ShortcutsBackend>(
       GetTemplateURLService(), std::make_unique<SearchTermsData>(),
       GetHistoryService(), base::FilePath(), true);
@@ -60,6 +64,14 @@
   run_loop.Run();
 }
 
+PrefService* FakeAutocompleteProviderClient::GetPrefs() {
+  return pref_service_.get();
+}
+
+PrefService* FakeAutocompleteProviderClient::GetLocalState() {
+  return local_state_.get();
+}
+
 const AutocompleteSchemeClassifier&
 FakeAutocompleteProviderClient::GetSchemeClassifier() const {
   return scheme_classifier_;
diff --git a/components/omnibox/browser/fake_autocomplete_provider_client.h b/components/omnibox/browser/fake_autocomplete_provider_client.h
index 5275b16..17acef0 100644
--- a/components/omnibox/browser/fake_autocomplete_provider_client.h
+++ b/components/omnibox/browser/fake_autocomplete_provider_client.h
@@ -22,7 +22,9 @@
 }  // namespace history
 
 class InMemoryURLIndex;
+class PrefService;
 class ShortcutsBackend;
+class TestingPrefServiceSimple;
 
 // Fully operational AutocompleteProviderClient for usage in tests.
 // Note: The history index rebuild task is created from main thread, usually
@@ -40,6 +42,12 @@
   FakeAutocompleteProviderClient& operator=(
       const FakeAutocompleteProviderClient&) = delete;
 
+  PrefService* GetPrefs() override;
+  // Note: this will not be shared with other test fakes that may create their
+  // own local_state testing PrefService.
+  // In this case, AutocompleteProviderClient could be modified to accept the
+  // local pref store in its constructor.
+  PrefService* GetLocalState() override;
   const AutocompleteSchemeClassifier& GetSchemeClassifier() const override;
   history::HistoryService* GetHistoryService() override;
   bookmarks::BookmarkModel* GetBookmarkModel() override;
@@ -68,6 +76,8 @@
   TestSchemeClassifier scheme_classifier_;
   std::unique_ptr<InMemoryURLIndex> in_memory_url_index_;
   std::unique_ptr<history::HistoryService> history_service_;
+  std::unique_ptr<TestingPrefServiceSimple> local_state_;
+  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
   scoped_refptr<ShortcutsBackend> shortcuts_backend_;
   std::unique_ptr<query_tiles::TileService> tile_service_;
 
diff --git a/components/omnibox/browser/intranet_redirector_state.cc b/components/omnibox/browser/intranet_redirector_state.cc
new file mode 100644
index 0000000..9096c8a
--- /dev/null
+++ b/components/omnibox/browser/intranet_redirector_state.cc
@@ -0,0 +1,56 @@
+// Copyright 2020 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 "components/omnibox/browser/intranet_redirector_state.h"
+
+#include "base/feature_list.h"
+#include "components/omnibox/browser/omnibox_prefs.h"
+#include "components/omnibox/common/omnibox_features.h"
+#include "components/prefs/pref_service.h"
+
+namespace omnibox {
+
+IntranetRedirectorBehavior GetInterceptionChecksBehavior(
+    const PrefService* pref_service) {
+  // Here, setting the policy will always take precedence.
+  // If policy is not set, users are eligible for an experiment.
+  // The experiment check happens after the policy check to register the users
+  // in an A/B Finch group at that point.
+  const PrefService::Preference* behavior_pref = nullptr;
+  // Expected to exist unless unregistered in tests.
+  if (pref_service) {
+    behavior_pref =
+        pref_service->FindPreference(omnibox::kIntranetRedirectBehavior);
+  }
+  if (behavior_pref && !behavior_pref->IsDefaultValue()) {
+    // If policy has set the value, use it and ignore the experiment.
+    // We filter the integer pref value to known policy/setting options.
+    int pref_value = behavior_pref->GetValue()->GetInt();
+    if (pref_value ==
+        static_cast<int>(IntranetRedirectorBehavior::DISABLE_FEATURE))
+      return IntranetRedirectorBehavior::DISABLE_FEATURE;
+    if (pref_value ==
+        static_cast<int>(IntranetRedirectorBehavior::
+                             DISABLE_INTERCEPTION_CHECKS_ENABLE_INFOBARS))
+      return IntranetRedirectorBehavior::
+          DISABLE_INTERCEPTION_CHECKS_ENABLE_INFOBARS;
+    if (pref_value ==
+        static_cast<int>(IntranetRedirectorBehavior::
+                             ENABLE_INTERCEPTION_CHECKS_AND_INFOBARS))
+      return IntranetRedirectorBehavior::
+          ENABLE_INTERCEPTION_CHECKS_AND_INFOBARS;
+  }
+
+  // When the relevant policy is not set, the experiment defaults the feature
+  // off.
+  if (base::FeatureList::IsEnabled(
+          omnibox::kIntranetRedirectBehaviorPolicyRollout)) {
+    return IntranetRedirectorBehavior::DISABLE_FEATURE;
+  }
+
+  // Current behavior for users not in the experiment rollout.
+  return IntranetRedirectorBehavior::ENABLE_INTERCEPTION_CHECKS_AND_INFOBARS;
+}
+
+}  // namespace omnibox
diff --git a/components/omnibox/browser/intranet_redirector_state.h b/components/omnibox/browser/intranet_redirector_state.h
new file mode 100644
index 0000000..7a84f653
--- /dev/null
+++ b/components/omnibox/browser/intranet_redirector_state.h
@@ -0,0 +1,32 @@
+// Copyright 2020 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.
+
+#ifndef COMPONENTS_OMNIBOX_BROWSER_INTRANET_REDIRECTOR_STATE_H_
+#define COMPONENTS_OMNIBOX_BROWSER_INTRANET_REDIRECTOR_STATE_H_
+
+#include "components/prefs/pref_service.h"
+
+namespace omnibox {
+
+// Settings values for intranet redirection and did-you-mean infobars.
+enum class IntranetRedirectorBehavior {
+  // Both redirect checks and the did-you-mean infobars are disabled.
+  DISABLE_FEATURE = 1,
+  // Checks are disabled for all domains but did-you-mean infobars will be
+  // shown for single-word queries. This is useful for cases where enterprises
+  // are certain DNS requests will not be hijacked.
+  DISABLE_INTERCEPTION_CHECKS_ENABLE_INFOBARS = 2,
+  // Enable both interception checks and the infobar.
+  // Default value prior to M88.
+  ENABLE_INTERCEPTION_CHECKS_AND_INFOBARS = 3,
+};
+
+// Returns the current behavior of the redirect detector feature.
+// Defined by policy for enterprises; currently disabled for non-enterprises.
+IntranetRedirectorBehavior GetInterceptionChecksBehavior(
+    const PrefService* prefs);
+
+}  // namespace omnibox
+
+#endif  // COMPONENTS_OMNIBOX_BROWSER_INTRANET_REDIRECTOR_STATE_H_
diff --git a/components/omnibox/browser/mock_autocomplete_provider_client.h b/components/omnibox/browser/mock_autocomplete_provider_client.h
index ab25d94..f2c6881 100644
--- a/components/omnibox/browser/mock_autocomplete_provider_client.h
+++ b/components/omnibox/browser/mock_autocomplete_provider_client.h
@@ -36,6 +36,7 @@
 
   // AutocompleteProviderClient:
   MOCK_METHOD0(GetPrefs, PrefService*());
+  MOCK_METHOD0(GetLocalState, PrefService*());
   MOCK_CONST_METHOD0(GetSchemeClassifier,
                      const AutocompleteSchemeClassifier&());
   MOCK_METHOD0(GetAutocompleteClassifier, AutocompleteClassifier*());
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 16a7ad1..1a0e670f 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -249,8 +249,10 @@
   if (!match.destination_url.is_valid()) {
     GetInfoForCurrentText(&match, alternate_nav_url);
   } else if (alternate_nav_url) {
+    std::unique_ptr<AutocompleteProviderClient> provider_client =
+        client_->CreateAutocompleteProviderClient();
     *alternate_nav_url = AutocompleteResult::ComputeAlternateNavUrl(
-        input_, match);
+        input_, match, provider_client.get());
   }
   return match;
 }
@@ -1628,8 +1630,10 @@
     }
     if (found_match_for_text && alternate_nav_url &&
         (!popup_model() || popup_model()->SelectionOnInitialLine())) {
-      *alternate_nav_url =
-          AutocompleteResult::ComputeAlternateNavUrl(input_, *match);
+      std::unique_ptr<AutocompleteProviderClient> provider_client =
+          client_->CreateAutocompleteProviderClient();
+      *alternate_nav_url = AutocompleteResult::ComputeAlternateNavUrl(
+          input_, *match, provider_client.get());
     }
   }
 
diff --git a/components/omnibox/browser/omnibox_prefs.cc b/components/omnibox/browser/omnibox_prefs.cc
index ed03e73..4e3a866 100644
--- a/components/omnibox/browser/omnibox_prefs.cc
+++ b/components/omnibox/browser/omnibox_prefs.cc
@@ -25,6 +25,11 @@
 // Also gated by a feature and server-side Admin Panel controls.
 const char kDocumentSuggestEnabled[] = "documentsuggest.enabled";
 
+// Enum specifying the active behavior for the intranet redirect detector.
+// The browser pref kDNSInterceptionChecksEnabled also impacts the redirector.
+// Values are defined in omnibox::IntranetRedirectorBehavior.
+const char kIntranetRedirectBehavior[] = "browser.intranet_redirect_behavior";
+
 // A dictionary of visibility preferences for suggestion groups. The key is the
 // suggestion group ID serialized as a string, and the value is
 // SuggestionGroupVisibility serialized as an integer.
diff --git a/components/omnibox/browser/omnibox_prefs.h b/components/omnibox/browser/omnibox_prefs.h
index 0c3eb746..76ba1f3 100644
--- a/components/omnibox/browser/omnibox_prefs.h
+++ b/components/omnibox/browser/omnibox_prefs.h
@@ -34,6 +34,7 @@
 // Alphabetical list of preference names specific to the omnibox component.
 // Keep alphabetized, and document each in the .cc file.
 extern const char kDocumentSuggestEnabled[];
+extern const char kIntranetRedirectBehavior[];
 extern const char kSuggestionGroupVisibility[];
 extern const char kPreventUrlElisionsInOmnibox[];
 extern const char kZeroSuggestCachedResults[];
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 3aae5ea..0e75701 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -329,6 +329,16 @@
 const base::Feature kWebUIOmniboxPopup{"WebUIOmniboxPopup",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables rollout of changing the default behavior for DNS interception checks
+// and did-you-mean infobar.
+// Users who are in the enabled group for this feature will have interception
+// checks and did-you-mean turned off. Enterprise Policy takes precedence over
+// this setting, and policy is checked before the feature group is checked and
+// marked.
+const base::Feature kIntranetRedirectBehaviorPolicyRollout{
+    "OmniboxDNSInterceptionChecksPolicyRollout",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // When enabled, use Assistant for omnibox voice query recognition instead of
 // Android's built-in voice recognition service. Only works on Android.
 const base::Feature kOmniboxAssistantVoiceSearch{
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 6453f5c1..1c314ff 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -93,6 +93,7 @@
 
 // Omnibox UI - these affect the UI or function of the location bar (not the
 // popup).
+extern const base::Feature kIntranetRedirectBehaviorPolicyRollout;
 extern const base::Feature kOmniboxAssistantVoiceSearch;
 
 // Path-hiding experiments - these hide the path and other URL components in
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 5ef2137..f799b10 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -1615,7 +1615,7 @@
           'caption': '''Allow DNS interception checks and did-you-mean "http://intranetsite/" infobars.''',
         }
       ],
-      'future_on': ['chrome.*', 'chrome_os'],
+      'supported_on': ['chrome.*:88-', 'chrome_os:88-'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
diff --git a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.h b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.h
index a9f78f2..99558a0 100644
--- a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.h
+++ b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.h
@@ -29,6 +29,7 @@
   // AutocompleteProviderClient implementation.
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   PrefService* GetPrefs() override;
+  PrefService* GetLocalState() override;
   const AutocompleteSchemeClassifier& GetSchemeClassifier() const override;
   AutocompleteClassifier* GetAutocompleteClassifier() override;
   history::HistoryService* GetHistoryService() override;
diff --git a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
index 079f728..303eeff 100644
--- a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
+++ b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
@@ -60,6 +60,10 @@
   return browser_state_->GetPrefs();
 }
 
+PrefService* AutocompleteProviderClientImpl::GetLocalState() {
+  return GetApplicationContext()->GetLocalState();
+}
+
 const AutocompleteSchemeClassifier&
 AutocompleteProviderClientImpl::GetSchemeClassifier() const {
   return scheme_classifier_;