diff --git a/DEPS b/DEPS
index c08024e..0ba78fe 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '5ce33efa7c91a638c0dc94f539c9597a954fd529',
+  'skia_revision': '55b72530fedeb58154635531751a8730982fbf2a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
diff --git a/chrome/VERSION b/chrome/VERSION
index 8129c6cd..dcb0532a 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=58
 MINOR=0
-BUILD=2997
+BUILD=2998
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java
index 2dcd451..74fa03e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java
@@ -76,6 +76,9 @@
         DaydreamApi daydreamApi = DaydreamApi.create(mActivity);
         if (daydreamApi == null) return false;
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        // If this is the first time any app reads the daydream config file, daydream may create its
+        // config directory... crbug.com/686104
+        StrictMode.allowThreadDiskWrites();
         int type = GvrApi.ViewerType.CARDBOARD;
         try {
             type = daydreamApi.getCurrentViewerType();
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc
index bf15ceb6..1cea9df 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc
@@ -45,7 +45,7 @@
   // external protocol handler because we don't want pages to open them, but
   // users still can.
   const ExternalProtocolHandler::BlockState block_state =
-      ExternalProtocolHandler::GetBlockState(scheme);
+      ExternalProtocolHandler::GetBlockState(scheme, profile_);
   switch (block_state) {
     case ExternalProtocolHandler::DONT_BLOCK:
       return metrics::OmniboxInputType::URL;
diff --git a/chrome/browser/chrome_site_per_process_browsertest.cc b/chrome/browser/chrome_site_per_process_browsertest.cc
index 74de684..8e161e6 100644
--- a/chrome/browser/chrome_site_per_process_browsertest.cc
+++ b/chrome/browser/chrome_site_per_process_browsertest.cc
@@ -391,8 +391,8 @@
                                                               protocol);
   }
 
-  ExternalProtocolHandler::BlockState GetBlockState(
-      const std::string& scheme) override {
+  ExternalProtocolHandler::BlockState GetBlockState(const std::string& scheme,
+                                                    Profile* profile) override {
     return ExternalProtocolHandler::DONT_BLOCK;
   }
 
diff --git a/chrome/browser/external_protocol/external_protocol_handler.cc b/chrome/browser/external_protocol/external_protocol_handler.cc
index d629ddd..96206ed 100644
--- a/chrome/browser/external_protocol/external_protocol_handler.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler.cc
@@ -52,11 +52,12 @@
 
 ExternalProtocolHandler::BlockState GetBlockStateWithDelegate(
     const std::string& scheme,
-    ExternalProtocolHandler::Delegate* delegate) {
+    ExternalProtocolHandler::Delegate* delegate,
+    Profile* profile) {
   if (!delegate)
-    return ExternalProtocolHandler::GetBlockState(scheme);
+    return ExternalProtocolHandler::GetBlockState(scheme, profile);
 
-  return delegate->GetBlockState(scheme);
+  return delegate->GetBlockState(scheme, profile);
 }
 
 void RunExternalProtocolDialogWithDelegate(
@@ -135,7 +136,8 @@
 
 // static
 ExternalProtocolHandler::BlockState ExternalProtocolHandler::GetBlockState(
-    const std::string& scheme) {
+    const std::string& scheme,
+    Profile* profile) {
   // If we are being carpet bombed, block the request.
   if (!g_accept_requests)
     return BLOCK;
@@ -147,18 +149,36 @@
     return BLOCK;
   }
 
-  // Check the stored prefs.
-  // TODO(pkasting): This kind of thing should go in the preferences on the
-  // profile, not in the local state. http://crbug.com/457254
-  PrefService* pref = g_browser_process->local_state();
-  if (pref) {  // May be NULL during testing.
-    DictionaryPrefUpdate update_excluded_schemas(pref, prefs::kExcludedSchemes);
+  // Check if there are any prefs in the local state. If there are, wipe them,
+  // and migrate the prefs to the profile.
+  // TODO(ramyasharma) remove the migration in M61.
+  PrefService* local_prefs = g_browser_process->local_state();
+  PrefService* profile_prefs = profile->GetPrefs();
+  if (local_prefs && profile_prefs) {  // May be NULL during testing.
+    DictionaryPrefUpdate local_state_schemas(local_prefs,
+                                             prefs::kExcludedSchemes);
+    DictionaryPrefUpdate update_excluded_schemas_profile(
+        profile_prefs, prefs::kExcludedSchemes);
+    if (update_excluded_schemas_profile->empty()) {
+      // Copy local state to profile state.
+      for (base::DictionaryValue::Iterator it(*local_state_schemas);
+           !it.IsAtEnd(); it.Advance()) {
+        bool is_blocked;
+        // Discard local state if set to blocked, to reset all users
+        // stuck in 'Do Nothing' + 'Do Not Open' state back to the default
+        // prompt state.
+        if (it.value().GetAsBoolean(&is_blocked) && !is_blocked)
+          update_excluded_schemas_profile->SetBoolean(it.key(), is_blocked);
+      }
+      // TODO(ramyasharma): Clear only if required.
+      local_prefs->ClearPref(prefs::kExcludedSchemes);
+    }
 
-    // Warm up the dictionary if needed.
-    PrepopulateDictionary(update_excluded_schemas.Get());
+    // Prepopulate the default states each time.
+    PrepopulateDictionary(update_excluded_schemas_profile.Get());
 
     bool should_block;
-    if (update_excluded_schemas->GetBoolean(scheme, &should_block))
+    if (update_excluded_schemas_profile->GetBoolean(scheme, &should_block))
       return should_block ? BLOCK : DONT_BLOCK;
   }
 
@@ -167,18 +187,18 @@
 
 // static
 void ExternalProtocolHandler::SetBlockState(const std::string& scheme,
-                                            BlockState state) {
+                                            BlockState state,
+                                            Profile* profile) {
   // Set in the stored prefs.
-  // TODO(pkasting): This kind of thing should go in the preferences on the
-  // profile, not in the local state. http://crbug.com/457254
-  PrefService* pref = g_browser_process->local_state();
-  if (pref) {  // May be NULL during testing.
-    DictionaryPrefUpdate update_excluded_schemas(pref, prefs::kExcludedSchemes);
-
-    if (state == UNKNOWN) {
-      update_excluded_schemas->Remove(scheme, NULL);
-    } else {
-      update_excluded_schemas->SetBoolean(scheme, (state == BLOCK));
+  PrefService* profile_prefs = profile->GetPrefs();
+  if (profile_prefs) {  // May be NULL during testing.
+    DictionaryPrefUpdate update_excluded_schemas_profile(
+        profile_prefs, prefs::kExcludedSchemes);
+    if (!update_excluded_schemas_profile->empty()) {
+      if (state == UNKNOWN)
+        update_excluded_schemas_profile->Remove(scheme, nullptr);
+      else
+        update_excluded_schemas_profile->SetBoolean(scheme, (state == BLOCK));
     }
   }
 }
@@ -197,8 +217,14 @@
   // have parameters unexpected by the external program.
   std::string escaped_url_string = net::EscapeExternalHandlerValue(url.spec());
   GURL escaped_url(escaped_url_string);
+
+  content::WebContents* web_contents = tab_util::GetWebContentsByID(
+      render_process_host_id, render_view_routing_id);
+  Profile* profile = nullptr;
+  if (web_contents)  // Maybe NULL during testing.
+    profile = Profile::FromBrowserContext(web_contents->GetBrowserContext());
   BlockState block_state =
-      GetBlockStateWithDelegate(escaped_url.scheme(), delegate);
+      GetBlockStateWithDelegate(escaped_url.scheme(), delegate, profile);
   if (block_state == BLOCK) {
     if (delegate)
       delegate->BlockRequest();
@@ -243,11 +269,6 @@
 // static
 void ExternalProtocolHandler::PrepopulateDictionary(
     base::DictionaryValue* win_pref) {
-  static bool is_warm = false;
-  if (is_warm)
-    return;
-  is_warm = true;
-
   static const char* const denied_schemes[] = {
     "afp",
     "data",
diff --git a/chrome/browser/external_protocol/external_protocol_handler.h b/chrome/browser/external_protocol/external_protocol_handler.h
index 99c0f03..e70e79bb 100644
--- a/chrome/browser/external_protocol/external_protocol_handler.h
+++ b/chrome/browser/external_protocol/external_protocol_handler.h
@@ -14,6 +14,7 @@
 
 class GURL;
 class PrefRegistrySimple;
+class Profile;
 
 namespace base {
 class DictionaryValue;
@@ -34,7 +35,8 @@
     CreateShellWorker(
         const shell_integration::DefaultWebClientWorkerCallback& callback,
         const std::string& protocol) = 0;
-    virtual BlockState GetBlockState(const std::string& scheme) = 0;
+    virtual BlockState GetBlockState(const std::string& scheme,
+                                     Profile* profile) = 0;
     virtual void BlockRequest() = 0;
     virtual void RunExternalProtocolDialog(
         const GURL& url,
@@ -50,10 +52,12 @@
   };
 
   // Returns whether we should block a given scheme.
-  static BlockState GetBlockState(const std::string& scheme);
+  static BlockState GetBlockState(const std::string& scheme, Profile* profile);
 
   // Sets whether we should block a given scheme.
-  static void SetBlockState(const std::string& scheme, BlockState state);
+  static void SetBlockState(const std::string& scheme,
+                            BlockState state,
+                            Profile* profile);
 
   // Checks to see if the protocol is allowed, if it is whitelisted,
   // the application associated with the protocol is launched on the io thread,
diff --git a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
index e0c29f4..62e4c31f 100644
--- a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
@@ -6,6 +6,11 @@
 
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/prefs/testing_pref_service.h"
 #include "content/public/test/test_browser_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -52,8 +57,8 @@
     return new FakeExternalProtocolHandlerWorker(callback, protocol, os_state_);
   }
 
-  ExternalProtocolHandler::BlockState GetBlockState(
-      const std::string& scheme) override {
+  ExternalProtocolHandler::BlockState GetBlockState(const std::string& scheme,
+                                                    Profile* profile) override {
     return block_state_;
   }
 
@@ -111,11 +116,19 @@
       : ui_thread_(BrowserThread::UI, base::MessageLoop::current()),
         file_thread_(BrowserThread::FILE) {}
 
-  void SetUp() override { file_thread_.Start(); }
+  void SetUp() override {
+    file_thread_.Start();
+    local_state_.reset(new TestingPrefServiceSimple);
+    profile_.reset(new TestingProfile());
+    chrome::RegisterLocalState(local_state_->registry());
+    TestingBrowserProcess::GetGlobal()->SetLocalState(local_state_.get());
+  }
 
   void TearDown() override {
     // Ensure that g_accept_requests gets set back to true after test execution.
     ExternalProtocolHandler::PermitLaunchUrl();
+    TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
+    local_state_.reset();
   }
 
   void DoTest(ExternalProtocolHandler::BlockState block_state,
@@ -145,6 +158,9 @@
   content::TestBrowserThread file_thread_;
 
   FakeExternalProtocolHandlerDelegate delegate_;
+
+  std::unique_ptr<TestingPrefServiceSimple> local_state_;
+  std::unique_ptr<TestingProfile> profile_;
 };
 
 TEST_F(ExternalProtocolHandlerTest, TestLaunchSchemeBlockedChromeDefault) {
@@ -191,3 +207,56 @@
   DoTest(ExternalProtocolHandler::UNKNOWN, shell_integration::UNKNOWN_DEFAULT,
          true, false, false);
 }
+
+TEST_F(ExternalProtocolHandlerTest, TestGetBlockStateUnknown) {
+  ExternalProtocolHandler::BlockState block_state =
+      ExternalProtocolHandler::GetBlockState("tel", profile_.get());
+  ASSERT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
+  ASSERT_TRUE(local_state_->GetDictionary(prefs::kExcludedSchemes)->empty());
+  ASSERT_FALSE(
+      profile_->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
+}
+
+TEST_F(ExternalProtocolHandlerTest, TestGetBlockStateDefaultBlock) {
+  ExternalProtocolHandler::BlockState block_state =
+      ExternalProtocolHandler::GetBlockState("afp", profile_.get());
+  ASSERT_EQ(ExternalProtocolHandler::BLOCK, block_state);
+  ASSERT_TRUE(local_state_->GetDictionary(prefs::kExcludedSchemes)->empty());
+  ASSERT_FALSE(
+      profile_->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
+}
+
+TEST_F(ExternalProtocolHandlerTest, TestGetBlockStateDefaultDontBlock) {
+  ExternalProtocolHandler::BlockState block_state =
+      ExternalProtocolHandler::GetBlockState("mailto", profile_.get());
+  ASSERT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);
+  ASSERT_TRUE(local_state_->GetDictionary(prefs::kExcludedSchemes)->empty());
+  ASSERT_FALSE(
+      profile_->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
+}
+
+TEST_F(ExternalProtocolHandlerTest,
+       TestGetBlockStateLocalBlockStateCopiedAndResetOnProfilePref) {
+  base::DictionaryValue prefs_local;
+  prefs_local.SetBoolean("tel", true);
+  local_state_->Set(prefs::kExcludedSchemes, prefs_local);
+  ExternalProtocolHandler::BlockState block_state =
+      ExternalProtocolHandler::GetBlockState("tel", profile_.get());
+  ASSERT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
+  ASSERT_TRUE(local_state_->GetDictionary(prefs::kExcludedSchemes)->empty());
+  ASSERT_FALSE(
+      profile_->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
+}
+
+TEST_F(ExternalProtocolHandlerTest,
+       TestGetBlockStateLocalDontBlockCopiedAsIsToProfilePref) {
+  base::DictionaryValue prefs_local;
+  prefs_local.SetBoolean("tel", false);
+  local_state_->Set(prefs::kExcludedSchemes, prefs_local);
+  ExternalProtocolHandler::BlockState block_state =
+      ExternalProtocolHandler::GetBlockState("tel", profile_.get());
+  ASSERT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);
+  ASSERT_TRUE(local_state_->GetDictionary(prefs::kExcludedSchemes)->empty());
+  ASSERT_FALSE(
+      profile_->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
+}
diff --git a/chrome/browser/prerender/prerender_test_utils.cc b/chrome/browser/prerender/prerender_test_utils.cc
index 0a844c8..73a22726 100644
--- a/chrome/browser/prerender/prerender_test_utils.cc
+++ b/chrome/browser/prerender/prerender_test_utils.cc
@@ -224,8 +224,8 @@
     return nullptr;
   }
 
-  ExternalProtocolHandler::BlockState GetBlockState(
-      const std::string& scheme) override {
+  ExternalProtocolHandler::BlockState GetBlockState(const std::string& scheme,
+                                                    Profile* profile) override {
     // Block everything and fail the test.
     ADD_FAILURE();
     return ExternalProtocolHandler::BLOCK;
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index c8f14b2..d32c584 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -176,6 +176,8 @@
 #if defined(OS_CHROMEOS)
   registry->RegisterBooleanPref(prefs::kAllowScreenLock, true);
 #endif
+
+  registry->RegisterDictionaryPref(prefs::kExcludedSchemes);
 }
 
 std::string Profile::GetDebugName() {
diff --git a/chrome/browser/ui/cocoa/external_protocol_dialog_cocoa.mm b/chrome/browser/ui/cocoa/external_protocol_dialog_cocoa.mm
index a53e065..6951e76b 100644
--- a/chrome/browser/ui/cocoa/external_protocol_dialog_cocoa.mm
+++ b/chrome/browser/ui/cocoa/external_protocol_dialog_cocoa.mm
@@ -7,6 +7,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/shell_integration.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "chrome/grit/chromium_strings.h"
@@ -94,9 +95,15 @@
       NOTREACHED();
   }
 
+  content::WebContents* web_contents =
+      tab_util::GetWebContentsByID(render_process_host_id_, routing_id_);
+
   // Set the "don't warn me again" info.
   if ([[alert_ suppressionButton] state] == NSOnState) {
-    ExternalProtocolHandler::SetBlockState(url_.scheme(), blockState);
+    Profile* profile =
+        Profile::FromBrowserContext(web_contents->GetBrowserContext());
+
+    ExternalProtocolHandler::SetBlockState(url_.scheme(), blockState, profile);
     ExternalProtocolHandler::RecordMetrics(true);
   } else {
     ExternalProtocolHandler::RecordMetrics(false);
@@ -106,9 +113,6 @@
     UMA_HISTOGRAM_LONG_TIMES("clickjacking.launch_url",
                              base::Time::Now() - creation_time_);
 
-    content::WebContents* web_contents =
-        tab_util::GetWebContentsByID(render_process_host_id_, routing_id_);
-
     ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(url_, web_contents);
   }
 
diff --git a/chrome/browser/ui/external_protocol_dialog_delegate.cc b/chrome/browser/ui/external_protocol_dialog_delegate.cc
index 20c662f..925c834 100644
--- a/chrome/browser/ui/external_protocol_dialog_delegate.cc
+++ b/chrome/browser/ui/external_protocol_dialog_delegate.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/external_protocol_dialog_delegate.h"
 
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -61,21 +62,29 @@
 
 void ExternalProtocolDialogDelegate::DoAccept(const GURL& url,
                                               bool dont_block) const {
-  if (dont_block) {
-    ExternalProtocolHandler::SetBlockState(url.scheme(),
-                                           ExternalProtocolHandler::DONT_BLOCK);
-  }
-
   content::WebContents* web_contents = tab_util::GetWebContentsByID(
       render_process_host_id_, render_view_routing_id_);
 
+  if (dont_block) {
+    Profile* profile =
+        Profile::FromBrowserContext(web_contents->GetBrowserContext());
+
+    ExternalProtocolHandler::SetBlockState(
+        url.scheme(), ExternalProtocolHandler::DONT_BLOCK, profile);
+  }
+
   ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(url, web_contents);
 }
 
 void ExternalProtocolDialogDelegate::DoCancel(const GURL& url,
                                               bool dont_block) const {
   if (dont_block) {
-    ExternalProtocolHandler::SetBlockState(url.scheme(),
-                                           ExternalProtocolHandler::BLOCK);
+    content::WebContents* web_contents = tab_util::GetWebContentsByID(
+        render_process_host_id_, render_view_routing_id_);
+    Profile* profile =
+        Profile::FromBrowserContext(web_contents->GetBrowserContext());
+
+    ExternalProtocolHandler::SetBlockState(
+        url.scheme(), ExternalProtocolHandler::BLOCK, profile);
   }
 }