diff --git a/DEPS b/DEPS
index 0182c5b..7199c16d 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': 'a5fdc974a996dca79be8388e61db68043001760b',
+  'skia_revision': 'db0e4f952a71a7a5009ec8276a234227e0ec18f6',
   # 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/browser/browsing_data/browsing_data_remover.h b/chrome/browser/browsing_data/browsing_data_remover.h
index b291f46..335844d 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.h
+++ b/chrome/browser/browsing_data/browsing_data_remover.h
@@ -80,6 +80,7 @@
     REMOVE_WEBAPP_DATA = 1 << 18,
 #endif
     REMOVE_DURABLE_PERMISSION = 1 << 19,
+    REMOVE_EXTERNAL_PROTOCOL_DATA = 1 << 20,
 
     // The following flag is used only in tests. In normal usage, hosted app
     // data is controlled by the REMOVE_COOKIES flag, applied to the
@@ -101,7 +102,8 @@
                        REMOVE_WEBAPP_DATA |
 #endif
                        REMOVE_SITE_USAGE_DATA |
-                       REMOVE_DURABLE_PERMISSION,
+                       REMOVE_DURABLE_PERMISSION |
+                       REMOVE_EXTERNAL_PROTOCOL_DATA,
 
     // Datatypes protected by Important Sites.
     IMPORTANT_SITES_DATATYPES = REMOVE_SITE_DATA | REMOVE_CACHE,
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index bb4b12a1..69a1b303 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/browsing_data/browsing_data_remover_test_util.h"
 #include "chrome/browser/browsing_data/cache_counter.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -274,6 +275,20 @@
   EXPECT_EQ(0, GetCacheSize());
 }
 
+IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
+                       ExternalProtocolHandlerPrefs) {
+  Profile* profile = browser()->profile();
+  base::DictionaryValue prefs;
+  prefs.SetBoolean("tel", true);
+  profile->GetPrefs()->Set(prefs::kExcludedSchemes, prefs);
+  ExternalProtocolHandler::BlockState block_state =
+      ExternalProtocolHandler::GetBlockState("tel", profile);
+  ASSERT_EQ(ExternalProtocolHandler::BLOCK, block_state);
+  RemoveAndWait(BrowsingDataRemover::REMOVE_SITE_DATA);
+  block_state = ExternalProtocolHandler::GetBlockState("tel", profile);
+  ASSERT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
+}
+
 // Verify that TransportSecurityState data is cleared for REMOVE_CACHE.
 IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverTransportSecurityStateBrowserTest,
                        ClearTransportSecurityState) {
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 2e75283..ceb6cd3 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/domain_reliability/service_factory.h"
 #include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/web_history_service_factory.h"
 #include "chrome/browser/io_thread.h"
@@ -925,6 +926,11 @@
     webapp_registry_->UnregisterWebappsForUrls(filter);
 #endif
 
+  //////////////////////////////////////////////////////////////////////////////
+  // Remove external protocol data.
+  if (remove_mask & BrowsingDataRemover::REMOVE_EXTERNAL_PROTOCOL_DATA)
+    ExternalProtocolHandler::ClearData(profile_);
+
   synchronous_clear_operations_.GetCompletionCallback().Run();
 }
 
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index cc3445c..26ddb051 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -1056,6 +1056,23 @@
 }
 #endif
 
+TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveExternalProtocolData) {
+  TestingProfile* profile = GetProfile();
+  // Add external protocol data on profile.
+  base::DictionaryValue prefs;
+  prefs.SetBoolean("tel", true);
+  profile->GetPrefs()->Set(prefs::kExcludedSchemes, prefs);
+
+  EXPECT_FALSE(
+      profile->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
+
+  BlockUntilBrowsingDataRemoved(
+      AnHourAgo(), base::Time::Max(),
+      BrowsingDataRemover::REMOVE_EXTERNAL_PROTOCOL_DATA, false);
+  EXPECT_TRUE(
+      profile->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
+}
+
 // Test that clearing history deletes favicons not associated with bookmarks.
 TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveFaviconsForever) {
   GURL page_url("http://a");
diff --git a/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc b/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc
index 7cbdbbb..10efc18 100644
--- a/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc
+++ b/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc
@@ -222,6 +222,7 @@
                   BrowsingDataRemover::REMOVE_WEBSQL) |
         GetAsMask(data_to_remove, "serverBoundCertificates",
                   BrowsingDataRemover::REMOVE_CHANNEL_IDS);
+
     EXPECT_EQ(expected_removal_mask, removal_mask);
   }
 
@@ -288,21 +289,25 @@
                                                    browser()));
 
   EXPECT_EQ(base::Time::FromDoubleT(1.0), GetBeginTime());
-  EXPECT_EQ((BrowsingDataRemover::REMOVE_SITE_DATA |
-             BrowsingDataRemover::REMOVE_CACHE |
-             BrowsingDataRemover::REMOVE_DOWNLOADS |
-             BrowsingDataRemover::REMOVE_FORM_DATA |
-             BrowsingDataRemover::REMOVE_HISTORY |
-             BrowsingDataRemover::REMOVE_PASSWORDS) &
-             // TODO(benwells): implement clearing of site usage data via the
-             // browsing data API. https://crbug.com/500801.
-             ~BrowsingDataRemover::REMOVE_SITE_USAGE_DATA &
-             // TODO(dmurph): implement clearing of durable storage permission
-             // via the browsing data API. https://crbug.com/500801.
-             ~BrowsingDataRemover::REMOVE_DURABLE_PERMISSION &
-             // We can't remove plugin data inside a test profile.
-             ~BrowsingDataRemover::REMOVE_PLUGIN_DATA,
-            GetRemovalMask());
+  EXPECT_EQ(
+      (BrowsingDataRemover::REMOVE_SITE_DATA |
+       BrowsingDataRemover::REMOVE_CACHE |
+       BrowsingDataRemover::REMOVE_DOWNLOADS |
+       BrowsingDataRemover::REMOVE_FORM_DATA |
+       BrowsingDataRemover::REMOVE_HISTORY |
+       BrowsingDataRemover::REMOVE_PASSWORDS) &
+          // TODO(benwells): implement clearing of site usage data via the
+          // browsing data API. https://crbug.com/500801.
+          ~BrowsingDataRemover::REMOVE_SITE_USAGE_DATA &
+          // TODO(dmurph): implement clearing of durable storage permission
+          // via the browsing data API. https://crbug.com/500801.
+          ~BrowsingDataRemover::REMOVE_DURABLE_PERMISSION &
+          // We can't remove plugin data inside a test profile.
+          ~BrowsingDataRemover::REMOVE_PLUGIN_DATA &
+          // TODO(ramyasharma): implement clearing of external protocol data
+          // via the browsing data API. https://crbug.com/692850.
+          ~BrowsingDataRemover::REMOVE_EXTERNAL_PROTOCOL_DATA,
+      GetRemovalMask());
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, BrowsingDataOriginTypeMask) {
@@ -478,46 +483,46 @@
 
 // Test cookie and app data settings.
 IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, SettingsFunctionSiteData) {
-  int site_data_no_durable_or_usage =
+  int site_data_no_durable_or_usage_or_external =
       BrowsingDataRemover::REMOVE_SITE_DATA &
       ~BrowsingDataRemover::REMOVE_SITE_USAGE_DATA &
-      ~BrowsingDataRemover::REMOVE_DURABLE_PERMISSION;
-  int site_data_no_plugins_durable_usage =
-      site_data_no_durable_or_usage & ~BrowsingDataRemover::REMOVE_PLUGIN_DATA;
+      ~BrowsingDataRemover::REMOVE_DURABLE_PERMISSION &
+      ~BrowsingDataRemover::REMOVE_EXTERNAL_PROTOCOL_DATA;
+  int site_data_no_plugins_durable_usage_external =
+      site_data_no_durable_or_usage_or_external &
+      ~BrowsingDataRemover::REMOVE_PLUGIN_DATA;
 
   SetPrefsAndVerifySettings(BrowsingDataRemover::REMOVE_COOKIES,
                             UNPROTECTED_WEB,
-                            site_data_no_plugins_durable_usage);
+                            site_data_no_plugins_durable_usage_external);
   SetPrefsAndVerifySettings(
-      BrowsingDataRemover::REMOVE_HOSTED_APP_DATA_TESTONLY,
-      PROTECTED_WEB,
-      site_data_no_plugins_durable_usage);
+      BrowsingDataRemover::REMOVE_HOSTED_APP_DATA_TESTONLY, PROTECTED_WEB,
+      site_data_no_plugins_durable_usage_external);
   SetPrefsAndVerifySettings(
       BrowsingDataRemover::REMOVE_COOKIES |
           BrowsingDataRemover::REMOVE_HOSTED_APP_DATA_TESTONLY,
       PROTECTED_WEB | UNPROTECTED_WEB,
-      site_data_no_plugins_durable_usage);
-  SetPrefsAndVerifySettings(
-      BrowsingDataRemover::REMOVE_COOKIES |
-          BrowsingDataRemover::REMOVE_PLUGIN_DATA,
-      UNPROTECTED_WEB,
-      site_data_no_durable_or_usage);
+      site_data_no_plugins_durable_usage_external);
+  SetPrefsAndVerifySettings(BrowsingDataRemover::REMOVE_COOKIES |
+                                BrowsingDataRemover::REMOVE_PLUGIN_DATA,
+                            UNPROTECTED_WEB,
+                            site_data_no_durable_or_usage_or_external);
 }
 
 // Test an arbitrary assortment of settings.
 IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, SettingsFunctionAssorted) {
-  int site_data_no_plugins_durable_usage =
+  int site_data_no_plugins_durable_usage_external =
       BrowsingDataRemover::REMOVE_SITE_DATA &
       ~BrowsingDataRemover::REMOVE_DURABLE_PERMISSION &
       ~BrowsingDataRemover::REMOVE_SITE_USAGE_DATA &
-      ~BrowsingDataRemover::REMOVE_PLUGIN_DATA;
+      ~BrowsingDataRemover::REMOVE_PLUGIN_DATA &
+      ~BrowsingDataRemover::REMOVE_EXTERNAL_PROTOCOL_DATA;
 
-  SetPrefsAndVerifySettings(
-      BrowsingDataRemover::REMOVE_COOKIES |
-          BrowsingDataRemover::REMOVE_HISTORY |
-          BrowsingDataRemover::REMOVE_DOWNLOADS,
-    UNPROTECTED_WEB,
-    site_data_no_plugins_durable_usage |
-        BrowsingDataRemover::REMOVE_HISTORY |
-        BrowsingDataRemover::REMOVE_DOWNLOADS);
+  SetPrefsAndVerifySettings(BrowsingDataRemover::REMOVE_COOKIES |
+                                BrowsingDataRemover::REMOVE_HISTORY |
+                                BrowsingDataRemover::REMOVE_DOWNLOADS,
+                            UNPROTECTED_WEB,
+                            site_data_no_plugins_durable_usage_external |
+                                BrowsingDataRemover::REMOVE_HISTORY |
+                                BrowsingDataRemover::REMOVE_DOWNLOADS);
 }
diff --git a/chrome/browser/external_protocol/external_protocol_handler.cc b/chrome/browser/external_protocol/external_protocol_handler.cc
index 29f98ed..36b9fdc5 100644
--- a/chrome/browser/external_protocol/external_protocol_handler.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler.cc
@@ -345,3 +345,9 @@
 void ExternalProtocolHandler::RegisterPrefs(PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(prefs::kExcludedSchemes);
 }
+
+// static
+void ExternalProtocolHandler::ClearData(Profile* profile) {
+  PrefService* prefs = profile->GetPrefs();
+  prefs->ClearPref(prefs::kExcludedSchemes);
+}
diff --git a/chrome/browser/external_protocol/external_protocol_handler.h b/chrome/browser/external_protocol/external_protocol_handler.h
index 30a1600..671626f 100644
--- a/chrome/browser/external_protocol/external_protocol_handler.h
+++ b/chrome/browser/external_protocol/external_protocol_handler.h
@@ -140,6 +140,9 @@
                                         ui::PageTransition page_transition,
                                         bool has_user_gesture);
 
+  // Clears the external protocol handling data.
+  static void ClearData(Profile* profile);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ExternalProtocolHandler);
 };
diff --git a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
index 62e4c31f..150d509 100644
--- a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
@@ -260,3 +260,14 @@
   ASSERT_FALSE(
       profile_->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
 }
+
+TEST_F(ExternalProtocolHandlerTest, TestClearProfileState) {
+  base::DictionaryValue prefs;
+  prefs.SetBoolean("tel", true);
+  profile_->GetPrefs()->Set(prefs::kExcludedSchemes, prefs);
+  ASSERT_FALSE(
+      profile_->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
+  ExternalProtocolHandler::ClearData(profile_.get());
+  ASSERT_TRUE(
+      profile_->GetPrefs()->GetDictionary(prefs::kExcludedSchemes)->empty());
+}
diff --git a/chrome/browser/signin/easy_unlock_service.cc b/chrome/browser/signin/easy_unlock_service.cc
index 1679d36..b76ae430 100644
--- a/chrome/browser/signin/easy_unlock_service.cc
+++ b/chrome/browser/signin/easy_unlock_service.cc
@@ -64,21 +64,10 @@
 #include "components/signin/core/account_id/account_id.h"
 #endif
 
-#if defined(OS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
 using proximity_auth::ScreenlockState;
 
 namespace {
 
-enum BluetoothType {
-  BT_NO_ADAPTER,
-  BT_NORMAL,
-  BT_LOW_ENERGY_CAPABLE,
-  BT_MAX_TYPE
-};
-
 PrefService* GetLocalState() {
   return g_browser_process ? g_browser_process->local_state() : NULL;
 }
@@ -145,32 +134,17 @@
     service_->OnBluetoothAdapterPresentChanged();
   }
 
-  device::BluetoothAdapter* getAdapter() {
-    return adapter_.get();
-  }
-
  private:
   void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter) {
     adapter_ = adapter;
     adapter_->AddObserver(this);
     service_->OnBluetoothAdapterPresentChanged();
 
-#if !defined(OS_CHROMEOS)
-    // Bluetooth detection causes serious performance degradations on Mac
-    // and possibly other platforms as well: http://crbug.com/467316
-    // Since this feature is currently only offered for ChromeOS we just
-    // turn it off on other platforms once the inforamtion about the
-    // adapter has been gathered and reported.
-    // TODO(bcwhite,xiyuan): Revisit when non-chromeos platforms are supported.
-    adapter_->RemoveObserver(this);
-    adapter_ = NULL;
-#else
     // TODO(tengs): At the moment, there is no way for Bluetooth discoverability
     // to be turned on except through the Easy Unlock setup. If we step on any
     // toes in the future then we need to revisit this guard.
     if (adapter_->IsDiscoverable())
       TurnOffBluetoothDiscoverability();
-#endif  // !defined(OS_CHROMEOS)
   }
 
   // apps::AppLifetimeMonitor::Observer:
@@ -744,31 +718,20 @@
   CHECK(app_manager_.get());
 
   InitializeInternal();
+
+#if defined(OS_CHROMEOS)
+  // Only start Bluetooth detection for ChromeOS since the feature is
+  // only offered on ChromeOS. Enabling this on non-ChromeOS platforms
+  // previously introduced a performance regression: http://crbug.com/404482
+  // Make sure not to reintroduce a performance regression if re-enabling on
+  // additional platforms.
+  // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
   bluetooth_detector_->Initialize();
+#endif  // defined(OS_CHROMEOS)
 }
 
 void EasyUnlockService::OnBluetoothAdapterPresentChanged() {
   UpdateAppState();
-
-  // Whether we've already passed Bluetooth availability information to UMA.
-  // This is static because there may be multiple instances and we want to
-  // report this system-level stat only once per run of Chrome.
-  static bool bluetooth_adapter_has_been_reported = false;
-
-  if (!bluetooth_adapter_has_been_reported) {
-    bluetooth_adapter_has_been_reported = true;
-    int bttype = BT_NO_ADAPTER;
-    if (bluetooth_detector_->IsPresent()) {
-      bttype = BT_LOW_ENERGY_CAPABLE;
-#if defined(OS_WIN)
-      if (base::win::GetVersion() < base::win::VERSION_WIN8) {
-        bttype = BT_NORMAL;
-      }
-#endif
-    }
-    UMA_HISTOGRAM_ENUMERATION(
-      "EasyUnlock.BluetoothAvailability", bttype, BT_MAX_TYPE);
-  }
 }
 
 void EasyUnlockService::SetHardlockStateForUser(
diff --git a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
index e329fe3..5d37829 100644
--- a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
+++ b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
@@ -890,6 +890,7 @@
   unsigned prefixLength = hyphenation.lastHyphenLocation(
       StringView(text.text(), start, len),
       std::min(maxPrefixLength, len - Hyphenation::minimumSuffixLength) + 1);
+  DCHECK_LE(prefixLength, maxPrefixLength);
   if (!prefixLength || prefixLength < Hyphenation::minimumPrefixLength)
     return false;
 
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 5b469fe..a7bae8a 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1509,6 +1509,7 @@
   if (use_minikin_hyphenation) {
     sources += [
       "text/hyphenation/HyphenationMinikin.cpp",
+      "text/hyphenation/HyphenationMinikin.h",
       "text/hyphenation/HyphenatorAOSP.cpp",
       "text/hyphenation/HyphenatorAOSP.h",
     ]
diff --git a/third_party/WebKit/Source/platform/text/HyphenationTest.cpp b/third_party/WebKit/Source/platform/text/HyphenationTest.cpp
index f4acfb6..aca243d13 100644
--- a/third_party/WebKit/Source/platform/text/HyphenationTest.cpp
+++ b/third_party/WebKit/Source/platform/text/HyphenationTest.cpp
@@ -7,6 +7,11 @@
 #include "platform/LayoutLocale.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if OS(ANDROID)
+#include "base/files/file_path.h"
+#include "platform/text/hyphenation/HyphenationMinikin.h"
+#endif
+
 namespace blink {
 
 class NoHyphenation : public Hyphenation {
@@ -28,4 +33,52 @@
   LayoutLocale::clearForTesting();
 }
 
+#if OS(ANDROID) || OS(MACOSX)
+TEST(HyphenationTest, LastHyphenLocation) {
+#if OS(ANDROID)
+  // Because the mojo service to open hyphenation dictionaries is not accessible
+  // from the unit test, open the dictionary file directly for testing.
+  base::FilePath path("/system/usr/hyphen-data/hyph-en-us.hyb");
+  base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!file.IsValid()) {
+    // Ignore this test on platforms without hyphenation dictionaries.
+    return;
+  }
+  RefPtr<Hyphenation> hyphenation =
+      HyphenationMinikin::fromFileForTesting(std::move(file));
+#else
+  const LayoutLocale* locale = LayoutLocale::get("en-us");
+  ASSERT_TRUE(locale);
+  Hyphenation* hyphenation = locale->getHyphenation();
+#endif
+  ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation engine";
+
+  // Get all hyphenation points by |hyphenLocations|.
+  const String word("hyphenation");
+  Vector<size_t, 8> locations = hyphenation->hyphenLocations(word);
+  for (unsigned i = 1; i < locations.size(); i++) {
+    ASSERT_GT(locations[i - 1], locations[i])
+        << "hyphenLocations must return locations in the descending order";
+  }
+
+  // Test |lastHyphenLocation| returns all hyphenation points.
+  locations.push_back(0);
+  size_t locationIndex = locations.size() - 1;
+  for (size_t beforeIndex = 0; beforeIndex < word.length(); beforeIndex++) {
+    size_t location = hyphenation->lastHyphenLocation(word, beforeIndex);
+
+    if (location)
+      EXPECT_LT(location, beforeIndex);
+
+    if (locationIndex > 0 && location == locations[locationIndex - 1])
+      locationIndex--;
+    EXPECT_EQ(locations[locationIndex], location) << String::format(
+        "lastHyphenLocation(%s, %zd)", word.utf8().data(), beforeIndex);
+  }
+
+  EXPECT_EQ(locationIndex, 0u)
+      << "Not all locations are found by lastHyphenLocation";
+}
+#endif
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.cpp b/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.cpp
index a930cc7..d6749b9 100644
--- a/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.cpp
+++ b/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.cpp
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "platform/text/Hyphenation.h"
+#include "platform/text/hyphenation/HyphenationMinikin.h"
 
 #include "base/files/file.h"
 #include "base/files/memory_mapped_file.h"
@@ -18,23 +18,6 @@
 
 using Hyphenator = android::Hyphenator;
 
-class HyphenationMinikin : public Hyphenation {
- public:
-  bool openDictionary(const AtomicString& locale);
-
-  size_t lastHyphenLocation(const StringView& text,
-                            size_t beforeIndex) const override;
-  Vector<size_t, 8> hyphenLocations(const StringView&) const override;
-
- private:
-  static base::File openDictionaryFile(const AtomicString& locale);
-
-  std::vector<uint8_t> hyphenate(const StringView&) const;
-
-  base::MemoryMappedFile m_file;
-  std::unique_ptr<Hyphenator> m_hyphenator;
-};
-
 static mojom::blink::HyphenationPtr connectToRemoteService() {
   mojom::blink::HyphenationPtr service;
   Platform::current()->interfaceProvider()->getInterface(
@@ -48,17 +31,17 @@
   return service;
 }
 
-base::File HyphenationMinikin::openDictionaryFile(const AtomicString& locale) {
+bool HyphenationMinikin::openDictionary(const AtomicString& locale) {
   const mojom::blink::HyphenationPtr& service = getService();
   base::File file;
   base::ElapsedTimer timer;
   service->OpenDictionary(locale, &file);
   UMA_HISTOGRAM_TIMES("Hyphenation.Open", timer.Elapsed());
-  return file;
+
+  return openDictionary(std::move(file));
 }
 
-bool HyphenationMinikin::openDictionary(const AtomicString& locale) {
-  base::File file = openDictionaryFile(locale);
+bool HyphenationMinikin::openDictionary(base::File file) {
   if (!file.IsValid())
     return false;
   if (!m_file.Initialize(std::move(file))) {
@@ -87,14 +70,17 @@
 
 size_t HyphenationMinikin::lastHyphenLocation(const StringView& text,
                                               size_t beforeIndex) const {
-  if (text.length() < minimumPrefixLength + minimumSuffixLength)
+  if (text.length() < minimumPrefixLength + minimumSuffixLength ||
+      beforeIndex <= minimumPrefixLength)
     return 0;
 
   std::vector<uint8_t> result = hyphenate(text);
-  static_assert(minimumPrefixLength >= 1,
-                "Change the 'if' above if this fails");
-  for (size_t i = text.length() - minimumSuffixLength - 1;
-       i >= minimumPrefixLength; i--) {
+  beforeIndex =
+      std::min<size_t>(beforeIndex, text.length() - minimumSuffixLength);
+  CHECK_LE(beforeIndex, result.size());
+  CHECK_GE(beforeIndex, 1u);
+  static_assert(minimumPrefixLength >= 1, "|beforeIndex - 1| can underflow");
+  for (size_t i = beforeIndex - 1; i >= minimumPrefixLength; i--) {
     if (result[i])
       return i;
   }
@@ -170,4 +156,12 @@
   return nullptr;
 }
 
+PassRefPtr<HyphenationMinikin> HyphenationMinikin::fromFileForTesting(
+    base::File file) {
+  RefPtr<HyphenationMinikin> hyphenation(adoptRef(new HyphenationMinikin));
+  if (hyphenation->openDictionary(std::move(file)))
+    return hyphenation.release();
+  return nullptr;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.h b/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.h
new file mode 100644
index 0000000..1f61b43
--- /dev/null
+++ b/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.h
@@ -0,0 +1,39 @@
+// Copyright 2017 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 "platform/text/Hyphenation.h"
+
+#include "base/files/memory_mapped_file.h"
+#include "platform/PlatformExport.h"
+
+namespace base {
+class File;
+}  // namespace base
+
+namespace android {
+class Hyphenator;
+}  // namespace andorid
+
+namespace blink {
+
+class PLATFORM_EXPORT HyphenationMinikin : public Hyphenation {
+ public:
+  bool openDictionary(const AtomicString& locale);
+
+  size_t lastHyphenLocation(const StringView& text,
+                            size_t beforeIndex) const override;
+  Vector<size_t, 8> hyphenLocations(const StringView&) const override;
+
+  static PassRefPtr<HyphenationMinikin> fromFileForTesting(base::File);
+
+ private:
+  bool openDictionary(base::File);
+
+  std::vector<uint8_t> hyphenate(const StringView&) const;
+
+  base::MemoryMappedFile m_file;
+  std::unique_ptr<android::Hyphenator> m_hyphenator;
+};
+
+}  // namespace blink
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index cbba338..f7fd8937 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -13578,6 +13578,9 @@
 
 <histogram name="EasyUnlock.BluetoothAvailability"
     enum="EasyUnlockBluetoothType">
+  <obsolete>
+    Deprecated as of 02/2017.
+  </obsolete>
   <owner>bcwhite@chromium.org</owner>
   <summary>
     Reports the type of Bluetooth adapter present in the device.