diff --git a/.gn b/.gn
index c91f9114..bee1495 100644
--- a/.gn
+++ b/.gn
@@ -43,6 +43,10 @@
   v8_experimental_extra_library_files = []
   v8_enable_gdbjit = false
   v8_imminent_deprecation_warnings = false
+
+  # TODO(jochen): Remove this. http://crbug.com/v8/5830,
+  # http://crbug.com/728583.
+  v8_check_microtasks_scopes_consistency = false
 }
 
 # These are the targets to check headers for by default. The files in targets
diff --git a/chrome/common/extensions/api/PRESUBMIT.py b/chrome/common/extensions/api/PRESUBMIT.py
index 39cc6be..bdd7779 100644
--- a/chrome/common/extensions/api/PRESUBMIT.py
+++ b/chrome/common/extensions/api/PRESUBMIT.py
@@ -27,6 +27,7 @@
 
   api_pair_names = {
     'autofill_private.idl': 'autofill_private.js',
+    'automation.idl': 'automation.js',
     'developer_private.idl': 'developer_private.js',
     'bookmark_manager_private.json': 'bookmark_manager_private.js',
     'command_line_private.json': 'command_line_private.js',
diff --git a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
index ed7de74..5c35cc36 100644
--- a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
+++ b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
@@ -491,7 +491,6 @@
 }
 
 void BluetoothDeviceChooserController::PopulateConnectedDevices() {
-  // TODO(crbug.com/728897): Use RetrieveGattConnectedDevices once implemented.
   for (const device::BluetoothDevice* device : adapter_->GetDevices()) {
     if (device->IsGattConnected()) {
       AddFilteredDevice(*device);
diff --git a/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom b/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
index 98da605..a9d71fa 100644
--- a/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
+++ b/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
@@ -30,10 +30,6 @@
   SimulateCentral(CentralState state) => (FakeCentral fake_central);
 };
 
-// HCI Error Codes from BT 4.2 Vol 2 Part D 1.3 List Of Error Codes.
-const uint16 kHCISuccess = 0x0000;
-const uint16 kHCIConnectionTimeout = 0x0008;
-
 // FakeCentral allows clients to simulate events that a device in the
 // Central/Observer role would receive as well as monitor the operations
 // performed by the device in the Central/Observer role.
@@ -52,20 +48,7 @@
   // connected to the system or weren't connected through the UA e.g. a user
   // connected a peripheral through the system's settings. This method is
   // intended to simulate peripherals that those methods would return.
-  // Even though these devices are already connected to the OS, clients
-  // need to call the respective connect functions to signal they intend to keep
-  // the connection alive.
   SimulatePreconnectedPeripheral(string address,
                                  string name,
                                  array<UUID> known_service_uuids) => ();
-
-  // Sets the next GATT Connection request response for peripheral with
-  // |address| to |code|. |code| could be an HCI Error Code from
-  // BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a number outside that range
-  // returned by specific platforms e.g. Android returns 0x101 to signal a GATT
-  // failure
-  // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
-  // Calls callback with false if there was any error when simulating the next
-  // response.
-  SetNextGATTConnectionResponse(string address, uint16 code) => (bool success);
 };
diff --git a/device/bluetooth/test/fake_central.cc b/device/bluetooth/test/fake_central.cc
index 2b11378..8bc994f 100644
--- a/device/bluetooth/test/fake_central.cc
+++ b/device/bluetooth/test/fake_central.cc
@@ -37,29 +37,13 @@
   FakePeripheral* fake_peripheral =
       static_cast<FakePeripheral*>(device_iter->second.get());
   fake_peripheral->SetName(name);
-  fake_peripheral->SetSystemConnected(true);
+  fake_peripheral->SetGattConnected(true);
   fake_peripheral->SetServiceUUIDs(device::BluetoothDevice::UUIDSet(
       known_service_uuids.begin(), known_service_uuids.end()));
 
   std::move(callback).Run();
 }
 
-void FakeCentral::SetNextGATTConnectionResponse(
-    const std::string& address,
-    uint16_t code,
-    SetNextGATTConnectionResponseCallback callback) {
-  auto device_iter = devices_.find(address);
-  if (device_iter == devices_.end()) {
-    std::move(callback).Run(false);
-    return;
-  }
-
-  FakePeripheral* fake_peripheral =
-      static_cast<FakePeripheral*>(device_iter->second.get());
-  fake_peripheral->SetNextGATTConnectionResponse(code);
-  std::move(callback).Run(true);
-}
-
 std::string FakeCentral::GetAddress() const {
   NOTREACHED();
   return std::string();
diff --git a/device/bluetooth/test/fake_central.h b/device/bluetooth/test/fake_central.h
index 4b096eca..93fbd93 100644
--- a/device/bluetooth/test/fake_central.h
+++ b/device/bluetooth/test/fake_central.h
@@ -29,10 +29,6 @@
       const std::string& name,
       const std::vector<device::BluetoothUUID>& known_service_uuids,
       SimulatePreconnectedPeripheralCallback callback) override;
-  void SetNextGATTConnectionResponse(
-      const std::string& address,
-      uint16_t code,
-      SetNextGATTConnectionResponseCallback) override;
 
   // BluetoothAdapter overrides:
   std::string GetAddress() const override;
diff --git a/device/bluetooth/test/fake_peripheral.cc b/device/bluetooth/test/fake_peripheral.cc
index a8524e2..acf93c73 100644
--- a/device/bluetooth/test/fake_peripheral.cc
+++ b/device/bluetooth/test/fake_peripheral.cc
@@ -4,17 +4,13 @@
 
 #include "device/bluetooth/test/fake_peripheral.h"
 
-#include "base/memory/weak_ptr.h"
-
 namespace bluetooth {
 
 FakePeripheral::FakePeripheral(FakeCentral* fake_central,
                                const std::string& address)
     : device::BluetoothDevice(fake_central),
       address_(address),
-      system_connected_(false),
-      gatt_connected_(false),
-      weak_ptr_factory_(this) {}
+      gatt_connected_(false) {}
 
 FakePeripheral::~FakePeripheral() {}
 
@@ -22,20 +18,14 @@
   name_ = std::move(name);
 }
 
-void FakePeripheral::SetSystemConnected(bool connected) {
-  system_connected_ = connected;
+void FakePeripheral::SetGattConnected(bool connected) {
+  gatt_connected_ = connected;
 }
 
 void FakePeripheral::SetServiceUUIDs(UUIDSet service_uuids) {
   service_uuids_ = std::move(service_uuids);
 }
 
-void FakePeripheral::SetNextGATTConnectionResponse(uint16_t code) {
-  DCHECK(!next_connection_response_);
-  DCHECK(create_gatt_connection_error_callbacks_.empty());
-  next_connection_response_ = code;
-}
-
 uint32_t FakePeripheral::GetBluetoothClass() const {
   NOTREACHED();
   return 0;
@@ -102,10 +92,7 @@
 }
 
 bool FakePeripheral::IsGattConnected() const {
-  // TODO(crbug.com/728870): Return gatt_connected_ only once system connected
-  // peripherals are supported and Web Bluetooth uses them. See issue for more
-  // details.
-  return system_connected_ || gatt_connected_;
+  return gatt_connected_;
 }
 
 bool FakePeripheral::IsConnectable() const {
@@ -197,43 +184,11 @@
   NOTREACHED();
 }
 
-void FakePeripheral::CreateGattConnection(
-    const GattConnectionCallback& callback,
-    const ConnectErrorCallback& error_callback) {
-  create_gatt_connection_success_callbacks_.push_back(callback);
-  create_gatt_connection_error_callbacks_.push_back(error_callback);
-
-  // TODO(crbug.com/728870): Stop overriding CreateGattConnection once
-  // IsGattConnected() is fixed. See issue for more details.
-  if (gatt_connected_)
-    return DidConnectGatt();
-
-  CreateGattConnectionImpl();
-}
-
 void FakePeripheral::CreateGattConnectionImpl() {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&FakePeripheral::DispatchConnectionResponse,
-                            weak_ptr_factory_.GetWeakPtr()));
-}
-
-void FakePeripheral::DispatchConnectionResponse() {
-  DCHECK(next_connection_response_);
-
-  uint16_t code = next_connection_response_.value();
-  next_connection_response_.reset();
-
-  if (code == mojom::kHCISuccess) {
-    gatt_connected_ = true;
-    DidConnectGatt();
-  } else if (code == mojom::kHCIConnectionTimeout) {
-    DidFailToConnectGatt(ERROR_FAILED);
-  } else {
-    DidFailToConnectGatt(ERROR_UNKNOWN);
-  }
+  NOTREACHED();
 }
 
 void FakePeripheral::DisconnectGatt() {
+  NOTREACHED();
 }
-
 }  // namespace bluetooth
diff --git a/device/bluetooth/test/fake_peripheral.h b/device/bluetooth/test/fake_peripheral.h
index b105c4f1..8e6222234 100644
--- a/device/bluetooth/test/fake_peripheral.h
+++ b/device/bluetooth/test/fake_peripheral.h
@@ -8,7 +8,6 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/test/fake_central.h"
@@ -25,20 +24,13 @@
   // Changes the name of the device.
   void SetName(base::Optional<std::string> name);
 
-  // Set it to indicate if the system has connected to the Peripheral outside of
-  // the Bluetooth interface e.g. the user connected to the device through
-  // system settings.
-  void SetSystemConnected(bool gatt_connected);
+  // Set it to indicate if the Peripheral is connected or not.
+  void SetGattConnected(bool gatt_connected);
 
   // Updates the peripheral's UUIDs that are returned by
   // BluetoothDevice::GetUUIDs().
   void SetServiceUUIDs(UUIDSet service_uuids);
 
-  // If |code| is kHCISuccess calls a pending success callback for
-  // CreateGattConnection. Otherwise calls a pending error callback
-  // with the ConnectErrorCode corresponding to |code|.
-  void SetNextGATTConnectionResponse(uint16_t code);
-
   // BluetoothDevice overrides:
   uint32_t GetBluetoothClass() const override;
 #if defined(OS_CHROMEOS) || defined(OS_LINUX)
@@ -86,31 +78,16 @@
       const device::BluetoothUUID& uuid,
       const ConnectToServiceCallback& callback,
       const ConnectToServiceErrorCallback& error_callback) override;
-  void CreateGattConnection(
-      const GattConnectionCallback& callback,
-      const ConnectErrorCallback& error_callback) override;
 
  protected:
   void CreateGattConnectionImpl() override;
   void DisconnectGatt() override;
 
  private:
-  void DispatchConnectionResponse();
-
   const std::string address_;
   base::Optional<std::string> name_;
-  UUIDSet service_uuids_;
-  // True when the system has connected to the device outside of the Bluetooth
-  // interface e.g. the user connected to the device through system settings.
-  bool system_connected_;
-  // True when this Bluetooth interface is connected to the device.
   bool gatt_connected_;
-
-  // Used to decide which callback should be called when
-  // CreateGattConnection is called.
-  base::Optional<uint16_t> next_connection_response_;
-
-  base::WeakPtrFactory<FakePeripheral> weak_ptr_factory_;
+  UUIDSet service_uuids_;
 
   DISALLOW_COPY_AND_ASSIGN(FakePeripheral);
 };
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js
index 32544f0..fec4374 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js
@@ -1,9 +1,12 @@
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice()
-    .then(([device]) => {
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gatt => {
       return assert_promise_rejects_with_message(
-        device.gatt.CALLS([
+        gatt.CALLS([
           getPrimaryService('wrong_name')|
           getPrimaryServices('wrong_name')
         ]),
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
index e2e95c2e..83580da7 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
@@ -1,8 +1,11 @@
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({acceptAllDevices: true})
-    .then(([device]) => assert_promise_rejects_with_message(
-        device.gatt.CALLS([
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{name: 'Heart Rate Device'}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => assert_promise_rejects_with_message(
+        gattServer.CALLS([
           getPrimaryService('heart_rate')|
           getPrimaryServices()|
           getPrimaryServices('heart_rate')[UUID]]),
@@ -12,3 +15,4 @@
                          'SecurityError')));
 }, 'Request for present service without permission to access any service. ' +
    'Reject with SecurityError.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
index a5fb002..f0d82e1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
@@ -2,16 +2,13 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return getDiscoveredHealthThermometerDevice()
-    .then(([device, fake_peripheral]) => {
-      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
-        .then(() => device.gatt.connect())
-        .then(gatt => assert_true(gatt.connected));
-    });
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => assert_true(gattServer.connected));
 }, 'Device will connect');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
index 60156c1..00b5c0a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
@@ -2,21 +2,16 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return getDiscoveredHealthThermometerDevice()
-    .then(([device, fake_peripheral]) => {
-      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
-        .then(() => {
-          // Don't return the promise and let |device| go out of scope
-          // so that it gets garbage collected.
-          device.gatt.connect();
-        });
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['health_thermometer']}]}))
+    .then(device => {
+      device.gatt.connect();
     })
-    .then(runGarbageCollection)
+    .then(runGarbageCollection);
 }, 'Garbage Collection ran during a connect call that succeeds. ' +
    'Should not crash.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
index d6d1a42..1c7774f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
@@ -2,24 +2,16 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return getDiscoveredHealthThermometerDevice()
-    .then(([device, fake_peripheral]) => {
-      return fake_peripheral
-        .setNextGATTConnectionResponse({code: HCI_SUCCESS})
-        .then(() => device.gatt.connect())
-        .then(gatt1 => {
-          // No second response is necessary because an ATT Bearer
-          // already exists from the first connection.
-          // See https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
-          // step 5.1.
-          return device.gatt.connect().then(gatt2 => [gatt1, gatt2]);
-        });
-    })
-    .then(([gatt1, gatt2]) => assert_equals(gatt1, gatt2));
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => {
+      return Promise.all([device.gatt.connect(), device.gatt.connect()])
+    }).then(gattServers => {
+      assert_equals(gattServers[0], gattServers[1]);
+    });
 }, 'Multiple connects should return the same gatt object.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html
index c6f391c..bbba6ddb 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html
@@ -2,18 +2,15 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return getDiscoveredHealthThermometerDevice()
-    .then(([device, fake_peripheral]) => {
-      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
-        .then(() => device.gatt.connect());
-    })
-    .then(gatt => {
-      assert_equals(gatt.device, gatt.device);
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => {
+      assert_equals(gattServer.device, gattServer.device);
     });
 }, "[SameObject] test for BluetoothRemoteGATTServer's device.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html
index 591757d1..aaa69c8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html
@@ -8,10 +8,13 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice()
-    .then(([device]) => {
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gatt => {
       return assert_promise_rejects_with_message(
-        device.gatt.getPrimaryService('wrong_name'),
+        gatt.getPrimaryService('wrong_name'),
         new DOMException(
           'Failed to execute \'getPrimaryService\' on ' +
           '\'BluetoothRemoteGATTServer\': Invalid Service name: ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
index d02d25d..c04a98f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
@@ -8,14 +8,18 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({acceptAllDevices: true})
-    .then(([device]) => assert_promise_rejects_with_message(
-        device.gatt.getPrimaryService('heart_rate'),
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{name: 'Heart Rate Device'}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => assert_promise_rejects_with_message(
+        gattServer.getPrimaryService('heart_rate'),
         new DOMException('Origin is not allowed to access any service. Tip: ' +
                          'Add the service UUID to \'optionalServices\' in ' +
                          'requestDevice() options. https://goo.gl/HxfxSQ',
                          'SecurityError')));
 }, 'Request for present service without permission to access any service. ' +
    'Reject with SecurityError.');
+</script>
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html
index f8d010b..66c9a37 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html
@@ -8,10 +8,13 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice()
-    .then(([device]) => {
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gatt => {
       return assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices('wrong_name'),
+        gatt.getPrimaryServices('wrong_name'),
         new DOMException(
           'Failed to execute \'getPrimaryServices\' on ' +
           '\'BluetoothRemoteGATTServer\': Invalid Service name: ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
index c80c983..12b0ef9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
@@ -8,14 +8,18 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({acceptAllDevices: true})
-    .then(([device]) => assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices('heart_rate'),
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{name: 'Heart Rate Device'}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => assert_promise_rejects_with_message(
+        gattServer.getPrimaryServices('heart_rate'),
         new DOMException('Origin is not allowed to access any service. Tip: ' +
                          'Add the service UUID to \'optionalServices\' in ' +
                          'requestDevice() options. https://goo.gl/HxfxSQ',
                          'SecurityError')));
 }, 'Request for present service without permission to access any service. ' +
    'Reject with SecurityError.');
+</script>
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
index c771e477..0b19a37 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
@@ -8,14 +8,18 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({acceptAllDevices: true})
-    .then(([device]) => assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices(),
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{name: 'Heart Rate Device'}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => assert_promise_rejects_with_message(
+        gattServer.getPrimaryServices(),
         new DOMException('Origin is not allowed to access any service. Tip: ' +
                          'Add the service UUID to \'optionalServices\' in ' +
                          'requestDevice() options. https://goo.gl/HxfxSQ',
                          'SecurityError')));
 }, 'Request for present service without permission to access any service. ' +
    'Reject with SecurityError.');
+</script>
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
index aae8a9b..862950d 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
@@ -1,11 +1,5 @@
 'use strict';
 
-// HCI Error Codes. Used for simulateGATT[Dis]ConnectionResponse.
-// For a complete list of possible error codes see
-// BT 4.2 Vol 2 Part D 1.3 List Of Error Codes.
-const HCI_SUCCESS = 0x0000;
-const HCI_CONNECTION_TIMEOUT = 0x0008;
-
 // Bluetooth UUID constants:
 // Services:
 var blocklist_test_service_uuid = "611c954a-263b-4f4a-aab6-01ddb953f985";
@@ -422,8 +416,6 @@
   }];
 }
 
-// Simulates a pre-connected device with |address|, |name| and
-// |knownServiceUUIDs|.
 function setUpPreconnectedDevice({
   address = '00:00:00:00:00:00', name = 'LE Device', knownServiceUUIDs = []}) {
   return navigator.bluetooth.test.simulateCentral({state: 'powered-on'})
@@ -434,8 +426,6 @@
     }));
 }
 
-// Returns an array containing two FakePeripherals corresponding
-// to the simulated devices.
 function setUpHealthThermometerAndHeartRateDevices() {
   return navigator.bluetooth.test.simulateCentral({state: 'powered-on'})
    .then(fake_central => Promise.all([
@@ -450,35 +440,3 @@
        knownServiceUUIDs: ['generic_access', 'heart_rate'],
      })]));
 }
-
-// Returns a BluetoothDevice discovered using |options| and its
-// corresponding FakePeripheral.
-// The simulated device is called 'Health Thermometer' it has two known service
-// UUIDs: 'generic_access' and 'health_thermometer'. The device has been
-// connected to and its services have been discovered.
-// TODO(crbug.com/719816): Add services, characteristics and descriptors,
-// and discover all the attributes.
-function getHealthThermometerDevice(options) {
-  return getDiscoveredHealthThermometerDevice(options)
-    .then(([device, fake_peripheral]) => {
-      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
-        .then(() => device.gatt.connect())
-        .then(gatt => [gatt.device, fake_peripheral]);
-    });
-}
-
-// Similar to getHealthThermometerDevice() except the device
-// is not connected and thus its services have not been
-// discovered.
-function getDiscoveredHealthThermometerDevice(
-  options = {filters: [{services: ['health_thermometer']}]}) {
-  return setUpPreconnectedDevice({
-    address: '09:09:09:09:09:09',
-    name: 'Health Thermometer',
-    knownServiceUUIDs: ['generic_access', 'health_thermometer'],
-  })
-  .then(fake_peripheral => {
-    return requestDeviceWithKeyDown(options)
-      .then(device => [device, fake_peripheral]);
-  });
-}
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js b/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
index 1f5e29de..450c27d 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
@@ -139,7 +139,7 @@
 
       let peripheral = this.peripherals_.get(address);
       if (peripheral === undefined) {
-        peripheral = new FakePeripheral(address, this.fake_central_ptr_);
+        peripheral = new FakePeripheral(address, this);
         this.peripherals_.set(address, peripheral);
       }
 
@@ -148,22 +148,9 @@
   }
 
   class FakePeripheral {
-    constructor(address, fake_central_ptr) {
+    constructor(address, fake_central) {
       this.address = address;
-      this.fake_central_ptr_ = fake_central_ptr;
-    }
-
-    // Sets the next GATT Connection request response to |code|. |code| could be
-    // an HCI Error Code from BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a
-    // number outside that range returned by specific platforms e.g. Android
-    // returns 0x101 to signal a GATT failure
-    // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
-    async setNextGATTConnectionResponse({code}) {
-      let {success} =
-        await this.fake_central_ptr_.setNextGATTConnectionResponse(
-          this.address, code);
-
-      if (success !== true) throw 'Cannot set next response.';
+      this.fake_central_ = fake_central;
     }
   }
 
diff --git a/third_party/WebKit/Source/core/editing/BUILD.gn b/third_party/WebKit/Source/core/editing/BUILD.gn
index c69f5a0..d48ab91 100644
--- a/third_party/WebKit/Source/core/editing/BUILD.gn
+++ b/third_party/WebKit/Source/core/editing/BUILD.gn
@@ -82,6 +82,7 @@
     "VisibleSelection.h",
     "VisibleUnits.cpp",
     "VisibleUnits.h",
+    "VisibleUnitsLine.cpp",
     "WritingDirection.h",
     "commands/AppendNodeCommand.cpp",
     "commands/AppendNodeCommand.h",
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
index 3e2484f8..674ec43 100644
--- a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
@@ -165,7 +165,8 @@
 }
 
 template <typename Strategy>
-static PositionWithAffinityTemplate<Strategy> HonorEditingBoundaryAtOrBefore(
+static PositionWithAffinityTemplate<Strategy>
+HonorEditingBoundaryAtOrBeforeTemplate(
     const PositionWithAffinityTemplate<Strategy>& pos,
     const PositionTemplate<Strategy>& anchor) {
   if (pos.IsNull())
@@ -198,6 +199,18 @@
                                                   *highest_root);
 }
 
+PositionWithAffinity HonorEditingBoundaryAtOrBefore(
+    const PositionWithAffinity& pos,
+    const Position& anchor) {
+  return HonorEditingBoundaryAtOrBeforeTemplate(pos, anchor);
+}
+
+PositionInFlatTreeWithAffinity HonorEditingBoundaryAtOrBefore(
+    const PositionInFlatTreeWithAffinity& pos,
+    const PositionInFlatTree& anchor) {
+  return HonorEditingBoundaryAtOrBeforeTemplate(pos, anchor);
+}
+
 template <typename Strategy>
 VisiblePositionTemplate<Strategy> HonorEditingBoundaryAtOrBeforeAlgorithm(
     const VisiblePositionTemplate<Strategy>& pos,
@@ -220,7 +233,7 @@
 }
 
 template <typename Strategy>
-static VisiblePositionTemplate<Strategy> HonorEditingBoundaryAtOrAfter(
+static VisiblePositionTemplate<Strategy> HonorEditingBoundaryAtOrAfterTemplate(
     const VisiblePositionTemplate<Strategy>& pos,
     const PositionTemplate<Strategy>& anchor) {
   DCHECK(pos.IsValid()) << pos;
@@ -255,144 +268,15 @@
                                                          *highest_root);
 }
 
-static bool HasEditableStyle(const Node& node, EditableType editable_type) {
-  if (editable_type == kHasEditableAXRole) {
-    if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) {
-      if (cache->RootAXEditableElement(&node))
-        return true;
-    }
-  }
-
-  return HasEditableStyle(node);
+VisiblePosition HonorEditingBoundaryAtOrAfter(const VisiblePosition& pos,
+                                              const Position& anchor) {
+  return HonorEditingBoundaryAtOrAfterTemplate(pos, anchor);
 }
 
-static Element* RootEditableElement(const Node& node,
-                                    EditableType editable_type) {
-  if (editable_type == kHasEditableAXRole) {
-    if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache())
-      return const_cast<Element*>(cache->RootAXEditableElement(&node));
-  }
-
-  return RootEditableElement(node);
-}
-
-static Element* RootAXEditableElementOf(const Position& position) {
-  Node* node = position.ComputeContainerNode();
-  if (!node)
-    return 0;
-
-  if (IsDisplayInsideTable(node))
-    node = node->parentNode();
-
-  return RootEditableElement(*node, kHasEditableAXRole);
-}
-
-static bool HasAXEditableStyle(const Node& node) {
-  return HasEditableStyle(node, kHasEditableAXRole);
-}
-
-static ContainerNode* HighestEditableRoot(const Position& position,
-                                          EditableType editable_type) {
-  if (editable_type == kHasEditableAXRole)
-    return HighestEditableRoot(position, RootAXEditableElementOf,
-                               HasAXEditableStyle);
-
-  return HighestEditableRoot(position);
-}
-
-static Node* PreviousLeafWithSameEditability(Node* node,
-                                             EditableType editable_type) {
-  bool editable = HasEditableStyle(*node, editable_type);
-  node = PreviousAtomicLeafNode(*node);
-  while (node) {
-    if (editable == HasEditableStyle(*node, editable_type))
-      return node;
-    node = PreviousAtomicLeafNode(*node);
-  }
-  return 0;
-}
-
-static Node* NextLeafWithSameEditability(
-    Node* node,
-    EditableType editable_type = kContentIsEditable) {
-  if (!node)
-    return 0;
-
-  bool editable = HasEditableStyle(*node, editable_type);
-  node = NextAtomicLeafNode(*node);
-  while (node) {
-    if (editable == HasEditableStyle(*node, editable_type))
-      return node;
-    node = NextAtomicLeafNode(*node);
-  }
-  return 0;
-}
-
-// FIXME: consolidate with code in previousLinePosition.
-Position PreviousRootInlineBoxCandidatePosition(
-    Node* node,
-    const VisiblePosition& visible_position,
-    EditableType editable_type) {
-  DCHECK(visible_position.IsValid()) << visible_position;
-  ContainerNode* highest_root =
-      HighestEditableRoot(visible_position.DeepEquivalent(), editable_type);
-  Node* previous_node = PreviousLeafWithSameEditability(node, editable_type);
-
-  while (previous_node &&
-         (!previous_node->GetLayoutObject() ||
-          InSameLine(
-              CreateVisiblePosition(FirstPositionInOrBeforeNode(previous_node)),
-              visible_position)))
-    previous_node =
-        PreviousLeafWithSameEditability(previous_node, editable_type);
-
-  while (previous_node && !previous_node->IsShadowRoot()) {
-    if (HighestEditableRoot(FirstPositionInOrBeforeNode(previous_node),
-                            editable_type) != highest_root)
-      break;
-
-    Position pos = isHTMLBRElement(*previous_node)
-                       ? Position::BeforeNode(previous_node)
-                       : Position::EditingPositionOf(
-                             previous_node, CaretMaxOffset(previous_node));
-
-    if (IsVisuallyEquivalentCandidate(pos))
-      return pos;
-
-    previous_node =
-        PreviousLeafWithSameEditability(previous_node, editable_type);
-  }
-  return Position();
-}
-
-Position NextRootInlineBoxCandidatePosition(
-    Node* node,
-    const VisiblePosition& visible_position,
-    EditableType editable_type) {
-  DCHECK(visible_position.IsValid()) << visible_position;
-  ContainerNode* highest_root =
-      HighestEditableRoot(visible_position.DeepEquivalent(), editable_type);
-  Node* next_node = NextLeafWithSameEditability(node, editable_type);
-  while (next_node && (!next_node->GetLayoutObject() ||
-                       InSameLine(CreateVisiblePosition(
-                                      FirstPositionInOrBeforeNode(next_node)),
-                                  visible_position)))
-    next_node = NextLeafWithSameEditability(next_node, kContentIsEditable);
-
-  while (next_node && !next_node->IsShadowRoot()) {
-    if (HighestEditableRoot(FirstPositionInOrBeforeNode(next_node),
-                            editable_type) != highest_root)
-      break;
-
-    Position pos;
-    pos = Position::EditingPositionOf(next_node, CaretMinOffset(next_node));
-
-    if (IsVisuallyEquivalentCandidate(pos))
-      return pos;
-
-    next_node = NextLeafWithSameEditability(next_node, editable_type);
-  }
-  return Position();
+VisiblePositionInFlatTree HonorEditingBoundaryAtOrAfter(
+    const VisiblePositionInFlatTree& pos,
+    const PositionInFlatTree& anchor) {
+  return HonorEditingBoundaryAtOrAfterTemplate(pos, anchor);
 }
 
 template <typename Strategy>
@@ -809,509 +693,6 @@
 
 // ---------
 
-enum LineEndpointComputationMode { kUseLogicalOrdering, kUseInlineBoxOrdering };
-template <typename Strategy>
-static PositionWithAffinityTemplate<Strategy> StartPositionForLine(
-    const PositionWithAffinityTemplate<Strategy>& c,
-    LineEndpointComputationMode mode) {
-  if (c.IsNull())
-    return PositionWithAffinityTemplate<Strategy>();
-
-  RootInlineBox* root_box =
-      RenderedPosition(c.GetPosition(), c.Affinity()).RootBox();
-  if (!root_box) {
-    // There are VisiblePositions at offset 0 in blocks without
-    // RootInlineBoxes, like empty editable blocks and bordered blocks.
-    PositionTemplate<Strategy> p = c.GetPosition();
-    if (p.AnchorNode()->GetLayoutObject() &&
-        p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() &&
-        !p.ComputeEditingOffset())
-      return c;
-
-    return PositionWithAffinityTemplate<Strategy>();
-  }
-
-  Node* start_node;
-  InlineBox* start_box;
-  if (mode == kUseLogicalOrdering) {
-    start_node = root_box->GetLogicalStartBoxWithNode(start_box);
-    if (!start_node)
-      return PositionWithAffinityTemplate<Strategy>();
-  } else {
-    // Generated content (e.g. list markers and CSS :before and :after
-    // pseudoelements) have no corresponding DOM element, and so cannot be
-    // represented by a VisiblePosition. Use whatever follows instead.
-    start_box = root_box->FirstLeafChild();
-    while (true) {
-      if (!start_box)
-        return PositionWithAffinityTemplate<Strategy>();
-
-      start_node = start_box->GetLineLayoutItem().NonPseudoNode();
-      if (start_node)
-        break;
-
-      start_box = start_box->NextLeafChild();
-    }
-  }
-
-  return PositionWithAffinityTemplate<Strategy>(
-      start_node->IsTextNode()
-          ? PositionTemplate<Strategy>(ToText(start_node),
-                                       ToInlineTextBox(start_box)->Start())
-          : PositionTemplate<Strategy>::BeforeNode(start_node));
-}
-
-template <typename Strategy>
-static PositionWithAffinityTemplate<Strategy> StartOfLineAlgorithm(
-    const PositionWithAffinityTemplate<Strategy>& c) {
-  // TODO: this is the current behavior that might need to be fixed.
-  // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
-  PositionWithAffinityTemplate<Strategy> vis_pos =
-      StartPositionForLine(c, kUseInlineBoxOrdering);
-  return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition());
-}
-
-static PositionWithAffinity StartOfLine(
-    const PositionWithAffinity& current_position) {
-  return StartOfLineAlgorithm<EditingStrategy>(current_position);
-}
-
-static PositionInFlatTreeWithAffinity StartOfLine(
-    const PositionInFlatTreeWithAffinity& current_position) {
-  return StartOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position);
-}
-
-// FIXME: Rename this function to reflect the fact it ignores bidi levels.
-VisiblePosition StartOfLine(const VisiblePosition& current_position) {
-  DCHECK(current_position.IsValid()) << current_position;
-  return CreateVisiblePosition(
-      StartOfLine(current_position.ToPositionWithAffinity()));
-}
-
-VisiblePositionInFlatTree StartOfLine(
-    const VisiblePositionInFlatTree& current_position) {
-  DCHECK(current_position.IsValid()) << current_position;
-  return CreateVisiblePosition(
-      StartOfLine(current_position.ToPositionWithAffinity()));
-}
-
-template <typename Strategy>
-static PositionWithAffinityTemplate<Strategy> LogicalStartOfLineAlgorithm(
-    const PositionWithAffinityTemplate<Strategy>& c) {
-  // TODO: this is the current behavior that might need to be fixed.
-  // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
-  PositionWithAffinityTemplate<Strategy> vis_pos =
-      StartPositionForLine(c, kUseLogicalOrdering);
-
-  if (ContainerNode* editable_root = HighestEditableRoot(c.GetPosition())) {
-    if (!editable_root->contains(vis_pos.GetPosition().ComputeContainerNode()))
-      return PositionWithAffinityTemplate<Strategy>(
-          PositionTemplate<Strategy>::FirstPositionInNode(editable_root));
-  }
-
-  return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition());
-}
-
-VisiblePosition LogicalStartOfLine(const VisiblePosition& current_position) {
-  DCHECK(current_position.IsValid()) << current_position;
-  return CreateVisiblePosition(LogicalStartOfLineAlgorithm<EditingStrategy>(
-      current_position.ToPositionWithAffinity()));
-}
-
-VisiblePositionInFlatTree LogicalStartOfLine(
-    const VisiblePositionInFlatTree& current_position) {
-  DCHECK(current_position.IsValid()) << current_position;
-  return CreateVisiblePosition(
-      LogicalStartOfLineAlgorithm<EditingInFlatTreeStrategy>(
-          current_position.ToPositionWithAffinity()));
-}
-
-template <typename Strategy>
-static VisiblePositionTemplate<Strategy> EndPositionForLine(
-    const VisiblePositionTemplate<Strategy>& c,
-    LineEndpointComputationMode mode) {
-  DCHECK(c.IsValid()) << c;
-  if (c.IsNull())
-    return VisiblePositionTemplate<Strategy>();
-
-  RootInlineBox* root_box = RenderedPosition(c).RootBox();
-  if (!root_box) {
-    // There are VisiblePositions at offset 0 in blocks without
-    // RootInlineBoxes, like empty editable blocks and bordered blocks.
-    const PositionTemplate<Strategy> p = c.DeepEquivalent();
-    if (p.AnchorNode()->GetLayoutObject() &&
-        p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() &&
-        !p.ComputeEditingOffset())
-      return c;
-    return VisiblePositionTemplate<Strategy>();
-  }
-
-  Node* end_node;
-  InlineBox* end_box;
-  if (mode == kUseLogicalOrdering) {
-    end_node = root_box->GetLogicalEndBoxWithNode(end_box);
-    if (!end_node)
-      return VisiblePositionTemplate<Strategy>();
-  } else {
-    // Generated content (e.g. list markers and CSS :before and :after
-    // pseudo elements) have no corresponding DOM element, and so cannot be
-    // represented by a VisiblePosition. Use whatever precedes instead.
-    end_box = root_box->LastLeafChild();
-    while (true) {
-      if (!end_box)
-        return VisiblePositionTemplate<Strategy>();
-
-      end_node = end_box->GetLineLayoutItem().NonPseudoNode();
-      if (end_node)
-        break;
-
-      end_box = end_box->PrevLeafChild();
-    }
-  }
-
-  PositionTemplate<Strategy> pos;
-  if (isHTMLBRElement(*end_node)) {
-    pos = PositionTemplate<Strategy>::BeforeNode(end_node);
-  } else if (end_box->IsInlineTextBox() && end_node->IsTextNode()) {
-    InlineTextBox* end_text_box = ToInlineTextBox(end_box);
-    int end_offset = end_text_box->Start();
-    if (!end_text_box->IsLineBreak())
-      end_offset += end_text_box->Len();
-    pos = PositionTemplate<Strategy>(ToText(end_node), end_offset);
-  } else {
-    pos = PositionTemplate<Strategy>::AfterNode(end_node);
-  }
-
-  return CreateVisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
-}
-
-// TODO(yosin) Rename this function to reflect the fact it ignores bidi levels.
-template <typename Strategy>
-static VisiblePositionTemplate<Strategy> EndOfLineAlgorithm(
-    const VisiblePositionTemplate<Strategy>& current_position) {
-  DCHECK(current_position.IsValid()) << current_position;
-  // TODO(yosin) this is the current behavior that might need to be fixed.
-  // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
-  VisiblePositionTemplate<Strategy> vis_pos =
-      EndPositionForLine(current_position, kUseInlineBoxOrdering);
-
-  // Make sure the end of line is at the same line as the given input
-  // position. Else use the previous position to obtain end of line. This
-  // condition happens when the input position is before the space character
-  // at the end of a soft-wrapped non-editable line. In this scenario,
-  // |endPositionForLine()| would incorrectly hand back a position in the next
-  // line instead. This fix is to account for the discrepancy between lines
-  // with "webkit-line-break:after-white-space" style versus lines without
-  // that style, which would break before a space by default.
-  if (!InSameLine(current_position, vis_pos)) {
-    vis_pos = PreviousPositionOf(current_position);
-    if (vis_pos.IsNull())
-      return VisiblePositionTemplate<Strategy>();
-    vis_pos = EndPositionForLine(vis_pos, kUseInlineBoxOrdering);
-  }
-
-  return HonorEditingBoundaryAtOrAfter(vis_pos,
-                                       current_position.DeepEquivalent());
-}
-
-// TODO(yosin) Rename this function to reflect the fact it ignores bidi levels.
-VisiblePosition EndOfLine(const VisiblePosition& current_position) {
-  return EndOfLineAlgorithm<EditingStrategy>(current_position);
-}
-
-VisiblePositionInFlatTree EndOfLine(
-    const VisiblePositionInFlatTree& current_position) {
-  return EndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position);
-}
-
-template <typename Strategy>
-static bool InSameLogicalLine(const VisiblePositionTemplate<Strategy>& a,
-                              const VisiblePositionTemplate<Strategy>& b) {
-  DCHECK(a.IsValid()) << a;
-  DCHECK(b.IsValid()) << b;
-  return a.IsNotNull() && LogicalStartOfLine(a).DeepEquivalent() ==
-                              LogicalStartOfLine(b).DeepEquivalent();
-}
-
-template <typename Strategy>
-VisiblePositionTemplate<Strategy> LogicalEndOfLineAlgorithm(
-    const VisiblePositionTemplate<Strategy>& current_position) {
-  DCHECK(current_position.IsValid()) << current_position;
-  // TODO(yosin) this is the current behavior that might need to be fixed.
-  // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
-  VisiblePositionTemplate<Strategy> vis_pos =
-      EndPositionForLine(current_position, kUseLogicalOrdering);
-
-  // Make sure the end of line is at the same line as the given input
-  // position. For a wrapping line, the logical end position for the
-  // not-last-2-lines might incorrectly hand back the logical beginning of the
-  // next line. For example,
-  // <div contenteditable dir="rtl" style="line-break:before-white-space">xyz
-  // a xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz </div>
-  // In this case, use the previous position of the computed logical end
-  // position.
-  if (!InSameLogicalLine(current_position, vis_pos))
-    vis_pos = PreviousPositionOf(vis_pos);
-
-  if (ContainerNode* editable_root =
-          HighestEditableRoot(current_position.DeepEquivalent())) {
-    if (!editable_root->contains(
-            vis_pos.DeepEquivalent().ComputeContainerNode()))
-      return CreateVisiblePosition(
-          PositionTemplate<Strategy>::LastPositionInNode(editable_root));
-  }
-
-  return HonorEditingBoundaryAtOrAfter(vis_pos,
-                                       current_position.DeepEquivalent());
-}
-
-VisiblePosition LogicalEndOfLine(const VisiblePosition& current_position) {
-  return LogicalEndOfLineAlgorithm<EditingStrategy>(current_position);
-}
-
-VisiblePositionInFlatTree LogicalEndOfLine(
-    const VisiblePositionInFlatTree& current_position) {
-  return LogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position);
-}
-
-template <typename Strategy>
-bool InSameLineAlgorithm(
-    const PositionWithAffinityTemplate<Strategy>& position1,
-    const PositionWithAffinityTemplate<Strategy>& position2) {
-  if (position1.IsNull() || position2.IsNull())
-    return false;
-  DCHECK_EQ(position1.GetDocument(), position2.GetDocument());
-  DCHECK(!position1.GetDocument()->NeedsLayoutTreeUpdate());
-
-  PositionWithAffinityTemplate<Strategy> start_of_line1 =
-      StartOfLine(position1);
-  PositionWithAffinityTemplate<Strategy> start_of_line2 =
-      StartOfLine(position2);
-  if (start_of_line1 == start_of_line2)
-    return true;
-  PositionTemplate<Strategy> canonicalized1 =
-      CanonicalPositionOf(start_of_line1.GetPosition());
-  if (canonicalized1 == start_of_line2.GetPosition())
-    return true;
-  return canonicalized1 == CanonicalPositionOf(start_of_line2.GetPosition());
-}
-
-bool InSameLine(const PositionWithAffinity& a, const PositionWithAffinity& b) {
-  return InSameLineAlgorithm<EditingStrategy>(a, b);
-}
-
-bool InSameLine(const PositionInFlatTreeWithAffinity& position1,
-                const PositionInFlatTreeWithAffinity& position2) {
-  return InSameLineAlgorithm<EditingInFlatTreeStrategy>(position1, position2);
-}
-
-bool InSameLine(const VisiblePosition& position1,
-                const VisiblePosition& position2) {
-  DCHECK(position1.IsValid()) << position1;
-  DCHECK(position2.IsValid()) << position2;
-  return InSameLine(position1.ToPositionWithAffinity(),
-                    position2.ToPositionWithAffinity());
-}
-
-bool InSameLine(const VisiblePositionInFlatTree& position1,
-                const VisiblePositionInFlatTree& position2) {
-  DCHECK(position1.IsValid()) << position1;
-  DCHECK(position2.IsValid()) << position2;
-  return InSameLine(position1.ToPositionWithAffinity(),
-                    position2.ToPositionWithAffinity());
-}
-
-template <typename Strategy>
-bool IsStartOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) {
-  DCHECK(p.IsValid()) << p;
-  return p.IsNotNull() && p.DeepEquivalent() == StartOfLine(p).DeepEquivalent();
-}
-
-bool IsStartOfLine(const VisiblePosition& p) {
-  return IsStartOfLineAlgorithm<EditingStrategy>(p);
-}
-
-bool IsStartOfLine(const VisiblePositionInFlatTree& p) {
-  return IsStartOfLineAlgorithm<EditingInFlatTreeStrategy>(p);
-}
-
-template <typename Strategy>
-bool IsEndOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) {
-  DCHECK(p.IsValid()) << p;
-  return p.IsNotNull() && p.DeepEquivalent() == EndOfLine(p).DeepEquivalent();
-}
-
-bool IsEndOfLine(const VisiblePosition& p) {
-  return IsEndOfLineAlgorithm<EditingStrategy>(p);
-}
-
-bool IsEndOfLine(const VisiblePositionInFlatTree& p) {
-  return IsEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p);
-}
-
-template <typename Strategy>
-static bool IsLogicalEndOfLineAlgorithm(
-    const VisiblePositionTemplate<Strategy>& p) {
-  DCHECK(p.IsValid()) << p;
-  return p.IsNotNull() &&
-         p.DeepEquivalent() == LogicalEndOfLine(p).DeepEquivalent();
-}
-
-bool IsLogicalEndOfLine(const VisiblePosition& p) {
-  return IsLogicalEndOfLineAlgorithm<EditingStrategy>(p);
-}
-
-bool IsLogicalEndOfLine(const VisiblePositionInFlatTree& p) {
-  return IsLogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p);
-}
-
-static inline LayoutPoint AbsoluteLineDirectionPointToLocalPointInBlock(
-    RootInlineBox* root,
-    LayoutUnit line_direction_point) {
-  DCHECK(root);
-  LineLayoutBlockFlow containing_block = root->Block();
-  FloatPoint absolute_block_point =
-      containing_block.LocalToAbsolute(FloatPoint());
-  if (containing_block.HasOverflowClip())
-    absolute_block_point -= FloatSize(containing_block.ScrolledContentOffset());
-
-  if (root->Block().IsHorizontalWritingMode())
-    return LayoutPoint(
-        LayoutUnit(line_direction_point - absolute_block_point.X()),
-        root->BlockDirectionPointInLine());
-
-  return LayoutPoint(
-      root->BlockDirectionPointInLine(),
-      LayoutUnit(line_direction_point - absolute_block_point.Y()));
-}
-
-VisiblePosition PreviousLinePosition(const VisiblePosition& visible_position,
-                                     LayoutUnit line_direction_point,
-                                     EditableType editable_type) {
-  DCHECK(visible_position.IsValid()) << visible_position;
-
-  Position p = visible_position.DeepEquivalent();
-  Node* node = p.AnchorNode();
-
-  if (!node)
-    return VisiblePosition();
-
-  LayoutObject* layout_object = node->GetLayoutObject();
-  if (!layout_object)
-    return VisiblePosition();
-
-  RootInlineBox* root = 0;
-  InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box;
-  if (box) {
-    root = box->Root().PrevRootBox();
-    // We want to skip zero height boxes.
-    // This could happen in case it is a TrailingFloatsRootInlineBox.
-    if (!root || !root->LogicalHeight() || !root->FirstLeafChild())
-      root = 0;
-  }
-
-  if (!root) {
-    Position position = PreviousRootInlineBoxCandidatePosition(
-        node, visible_position, editable_type);
-    if (position.IsNotNull()) {
-      RenderedPosition rendered_position((CreateVisiblePosition(position)));
-      root = rendered_position.RootBox();
-      if (!root)
-        return CreateVisiblePosition(position);
-    }
-  }
-
-  if (root) {
-    // FIXME: Can be wrong for multi-column layout and with transforms.
-    LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock(
-        root, line_direction_point);
-    LineLayoutItem line_layout_item =
-        root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p))
-            ->GetLineLayoutItem();
-    Node* node = line_layout_item.GetNode();
-    if (node && EditingIgnoresContent(*node))
-      return VisiblePosition::InParentBeforeNode(*node);
-    return CreateVisiblePosition(
-        line_layout_item.PositionForPoint(point_in_line));
-  }
-
-  // Could not find a previous line. This means we must already be on the first
-  // line. Move to the start of the content in this block, which effectively
-  // moves us to the start of the line we're on.
-  Element* root_element = HasEditableStyle(*node, editable_type)
-                              ? RootEditableElement(*node, editable_type)
-                              : node->GetDocument().documentElement();
-  if (!root_element)
-    return VisiblePosition();
-  return VisiblePosition::FirstPositionInNode(root_element);
-}
-
-VisiblePosition NextLinePosition(const VisiblePosition& visible_position,
-                                 LayoutUnit line_direction_point,
-                                 EditableType editable_type) {
-  DCHECK(visible_position.IsValid()) << visible_position;
-
-  Position p = visible_position.DeepEquivalent();
-  Node* node = p.AnchorNode();
-
-  if (!node)
-    return VisiblePosition();
-
-  LayoutObject* layout_object = node->GetLayoutObject();
-  if (!layout_object)
-    return VisiblePosition();
-
-  RootInlineBox* root = 0;
-  InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box;
-  if (box) {
-    root = box->Root().NextRootBox();
-    // We want to skip zero height boxes.
-    // This could happen in case it is a TrailingFloatsRootInlineBox.
-    if (!root || !root->LogicalHeight() || !root->FirstLeafChild())
-      root = 0;
-  }
-
-  if (!root) {
-    // FIXME: We need do the same in previousLinePosition.
-    Node* child = NodeTraversal::ChildAt(*node, p.ComputeEditingOffset());
-    node = child ? child : &NodeTraversal::LastWithinOrSelf(*node);
-    Position position = NextRootInlineBoxCandidatePosition(
-        node, visible_position, editable_type);
-    if (position.IsNotNull()) {
-      RenderedPosition rendered_position((CreateVisiblePosition(position)));
-      root = rendered_position.RootBox();
-      if (!root)
-        return CreateVisiblePosition(position);
-    }
-  }
-
-  if (root) {
-    // FIXME: Can be wrong for multi-column layout and with transforms.
-    LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock(
-        root, line_direction_point);
-    LineLayoutItem line_layout_item =
-        root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p))
-            ->GetLineLayoutItem();
-    Node* node = line_layout_item.GetNode();
-    if (node && EditingIgnoresContent(*node))
-      return VisiblePosition::InParentBeforeNode(*node);
-    return CreateVisiblePosition(
-        line_layout_item.PositionForPoint(point_in_line));
-  }
-
-  // Could not find a next line. This means we must already be on the last line.
-  // Move to the end of the content in this block, which effectively moves us
-  // to the end of the line we're on.
-  Element* root_element = HasEditableStyle(*node, editable_type)
-                              ? RootEditableElement(*node, editable_type)
-                              : node->GetDocument().documentElement();
-  if (!root_element)
-    return VisiblePosition();
-  return VisiblePosition::LastPositionInNode(root_element);
-}
-
-// ---------
-
 static unsigned StartSentenceBoundary(const UChar* characters,
                                       unsigned length,
                                       unsigned,
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.h b/third_party/WebKit/Source/core/editing/VisibleUnits.h
index 26e5b98a2..099b17ec 100644
--- a/third_party/WebKit/Source/core/editing/VisibleUnits.h
+++ b/third_party/WebKit/Source/core/editing/VisibleUnits.h
@@ -340,6 +340,21 @@
 IntRect ComputeTextRect(const EphemeralRangeInFlatTree&);
 FloatRect ComputeTextFloatRect(const EphemeralRange&);
 
+// Export below functions only for |VisibleUnit| family.
+PositionWithAffinity HonorEditingBoundaryAtOrBefore(const PositionWithAffinity&,
+                                                    const Position&);
+
+PositionInFlatTreeWithAffinity HonorEditingBoundaryAtOrBefore(
+    const PositionInFlatTreeWithAffinity&,
+    const PositionInFlatTree&);
+
+VisiblePosition HonorEditingBoundaryAtOrAfter(const VisiblePosition&,
+                                              const Position&);
+
+VisiblePositionInFlatTree HonorEditingBoundaryAtOrAfter(
+    const VisiblePositionInFlatTree&,
+    const PositionInFlatTree&);
+
 // Export below functions only for |SelectionModifier|.
 VisiblePosition HonorEditingBoundaryAtOrBefore(const VisiblePosition&,
                                                const Position&);
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp b/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp
new file mode 100644
index 0000000..624a662
--- /dev/null
+++ b/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// 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 "core/editing/VisibleUnits.h"
+
+#include "core/editing/EditingUtilities.h"
+#include "core/editing/RenderedPosition.h"
+#include "core/layout/api/LineLayoutBlockFlow.h"
+#include "core/layout/line/InlineTextBox.h"
+#include "core/layout/line/RootInlineBox.h"
+
+namespace blink {
+
+namespace {
+
+bool HasEditableStyle(const Node& node, EditableType editable_type) {
+  if (editable_type == kHasEditableAXRole) {
+    if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) {
+      if (cache->RootAXEditableElement(&node))
+        return true;
+    }
+  }
+
+  return HasEditableStyle(node);
+}
+
+Element* RootEditableElement(const Node& node, EditableType editable_type) {
+  if (editable_type == kHasEditableAXRole) {
+    if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache())
+      return const_cast<Element*>(cache->RootAXEditableElement(&node));
+  }
+
+  return RootEditableElement(node);
+}
+
+Element* RootAXEditableElementOf(const Position& position) {
+  Node* node = position.ComputeContainerNode();
+  if (!node)
+    return nullptr;
+
+  if (IsDisplayInsideTable(node))
+    node = node->parentNode();
+
+  return RootEditableElement(*node, kHasEditableAXRole);
+}
+
+bool HasAXEditableStyle(const Node& node) {
+  return HasEditableStyle(node, kHasEditableAXRole);
+}
+
+ContainerNode* HighestEditableRoot(const Position& position,
+                                   EditableType editable_type) {
+  if (editable_type == kHasEditableAXRole) {
+    return HighestEditableRoot(position, RootAXEditableElementOf,
+                               HasAXEditableStyle);
+  }
+
+  return HighestEditableRoot(position);
+}
+
+Node* PreviousLeafWithSameEditability(Node* node, EditableType editable_type) {
+  bool editable = HasEditableStyle(*node, editable_type);
+  node = PreviousAtomicLeafNode(*node);
+  while (node) {
+    if (editable == HasEditableStyle(*node, editable_type))
+      return node;
+    node = PreviousAtomicLeafNode(*node);
+  }
+  return nullptr;
+}
+
+Node* NextLeafWithSameEditability(
+    Node* node,
+    EditableType editable_type = kContentIsEditable) {
+  if (!node)
+    return nullptr;
+
+  bool editable = HasEditableStyle(*node, editable_type);
+  node = NextAtomicLeafNode(*node);
+  while (node) {
+    if (editable == HasEditableStyle(*node, editable_type))
+      return node;
+    node = NextAtomicLeafNode(*node);
+  }
+  return nullptr;
+}
+
+enum LineEndpointComputationMode { kUseLogicalOrdering, kUseInlineBoxOrdering };
+template <typename Strategy>
+PositionWithAffinityTemplate<Strategy> StartPositionForLine(
+    const PositionWithAffinityTemplate<Strategy>& c,
+    LineEndpointComputationMode mode) {
+  if (c.IsNull())
+    return PositionWithAffinityTemplate<Strategy>();
+
+  RootInlineBox* root_box =
+      RenderedPosition(c.GetPosition(), c.Affinity()).RootBox();
+  if (!root_box) {
+    // There are VisiblePositions at offset 0 in blocks without
+    // RootInlineBoxes, like empty editable blocks and bordered blocks.
+    PositionTemplate<Strategy> p = c.GetPosition();
+    if (p.AnchorNode()->GetLayoutObject() &&
+        p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() &&
+        !p.ComputeEditingOffset())
+      return c;
+
+    return PositionWithAffinityTemplate<Strategy>();
+  }
+
+  Node* start_node;
+  InlineBox* start_box;
+  if (mode == kUseLogicalOrdering) {
+    start_node = root_box->GetLogicalStartBoxWithNode(start_box);
+    if (!start_node)
+      return PositionWithAffinityTemplate<Strategy>();
+  } else {
+    // Generated content (e.g. list markers and CSS :before and :after
+    // pseudoelements) have no corresponding DOM element, and so cannot be
+    // represented by a VisiblePosition. Use whatever follows instead.
+    start_box = root_box->FirstLeafChild();
+    while (true) {
+      if (!start_box)
+        return PositionWithAffinityTemplate<Strategy>();
+
+      start_node = start_box->GetLineLayoutItem().NonPseudoNode();
+      if (start_node)
+        break;
+
+      start_box = start_box->NextLeafChild();
+    }
+  }
+
+  return PositionWithAffinityTemplate<Strategy>(
+      start_node->IsTextNode()
+          ? PositionTemplate<Strategy>(ToText(start_node),
+                                       ToInlineTextBox(start_box)->Start())
+          : PositionTemplate<Strategy>::BeforeNode(start_node));
+}
+
+template <typename Strategy>
+PositionWithAffinityTemplate<Strategy> StartOfLineAlgorithm(
+    const PositionWithAffinityTemplate<Strategy>& c) {
+  // TODO: this is the current behavior that might need to be fixed.
+  // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
+  PositionWithAffinityTemplate<Strategy> vis_pos =
+      StartPositionForLine(c, kUseInlineBoxOrdering);
+  return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition());
+}
+
+PositionWithAffinity StartOfLine(const PositionWithAffinity& current_position) {
+  return StartOfLineAlgorithm<EditingStrategy>(current_position);
+}
+
+PositionInFlatTreeWithAffinity StartOfLine(
+    const PositionInFlatTreeWithAffinity& current_position) {
+  return StartOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position);
+}
+
+LayoutPoint AbsoluteLineDirectionPointToLocalPointInBlock(
+    RootInlineBox* root,
+    LayoutUnit line_direction_point) {
+  DCHECK(root);
+  LineLayoutBlockFlow containing_block = root->Block();
+  FloatPoint absolute_block_point =
+      containing_block.LocalToAbsolute(FloatPoint());
+  if (containing_block.HasOverflowClip())
+    absolute_block_point -= FloatSize(containing_block.ScrolledContentOffset());
+
+  if (root->Block().IsHorizontalWritingMode()) {
+    return LayoutPoint(
+        LayoutUnit(line_direction_point - absolute_block_point.X()),
+        root->BlockDirectionPointInLine());
+  }
+
+  return LayoutPoint(
+      root->BlockDirectionPointInLine(),
+      LayoutUnit(line_direction_point - absolute_block_point.Y()));
+}
+
+}  // namespace
+
+// FIXME: consolidate with code in previousLinePosition.
+Position PreviousRootInlineBoxCandidatePosition(
+    Node* node,
+    const VisiblePosition& visible_position,
+    EditableType editable_type) {
+  DCHECK(visible_position.IsValid()) << visible_position;
+  ContainerNode* highest_root =
+      HighestEditableRoot(visible_position.DeepEquivalent(), editable_type);
+  Node* previous_node = PreviousLeafWithSameEditability(node, editable_type);
+
+  while (previous_node &&
+         (!previous_node->GetLayoutObject() ||
+          InSameLine(
+              CreateVisiblePosition(FirstPositionInOrBeforeNode(previous_node)),
+              visible_position))) {
+    previous_node =
+        PreviousLeafWithSameEditability(previous_node, editable_type);
+  }
+
+  while (previous_node && !previous_node->IsShadowRoot()) {
+    if (HighestEditableRoot(FirstPositionInOrBeforeNode(previous_node),
+                            editable_type) != highest_root)
+      break;
+
+    Position pos = isHTMLBRElement(*previous_node)
+                       ? Position::BeforeNode(previous_node)
+                       : Position::EditingPositionOf(
+                             previous_node, CaretMaxOffset(previous_node));
+
+    if (IsVisuallyEquivalentCandidate(pos))
+      return pos;
+
+    previous_node =
+        PreviousLeafWithSameEditability(previous_node, editable_type);
+  }
+  return Position();
+}
+
+Position NextRootInlineBoxCandidatePosition(
+    Node* node,
+    const VisiblePosition& visible_position,
+    EditableType editable_type) {
+  DCHECK(visible_position.IsValid()) << visible_position;
+  ContainerNode* highest_root =
+      HighestEditableRoot(visible_position.DeepEquivalent(), editable_type);
+  Node* next_node = NextLeafWithSameEditability(node, editable_type);
+  while (next_node && (!next_node->GetLayoutObject() ||
+                       InSameLine(CreateVisiblePosition(
+                                      FirstPositionInOrBeforeNode(next_node)),
+                                  visible_position)))
+    next_node = NextLeafWithSameEditability(next_node, kContentIsEditable);
+
+  while (next_node && !next_node->IsShadowRoot()) {
+    if (HighestEditableRoot(FirstPositionInOrBeforeNode(next_node),
+                            editable_type) != highest_root)
+      break;
+
+    Position pos;
+    pos = Position::EditingPositionOf(next_node, CaretMinOffset(next_node));
+
+    if (IsVisuallyEquivalentCandidate(pos))
+      return pos;
+
+    next_node = NextLeafWithSameEditability(next_node, editable_type);
+  }
+  return Position();
+}
+
+// FIXME: Rename this function to reflect the fact it ignores bidi levels.
+VisiblePosition StartOfLine(const VisiblePosition& current_position) {
+  DCHECK(current_position.IsValid()) << current_position;
+  return CreateVisiblePosition(
+      StartOfLine(current_position.ToPositionWithAffinity()));
+}
+
+VisiblePositionInFlatTree StartOfLine(
+    const VisiblePositionInFlatTree& current_position) {
+  DCHECK(current_position.IsValid()) << current_position;
+  return CreateVisiblePosition(
+      StartOfLine(current_position.ToPositionWithAffinity()));
+}
+
+template <typename Strategy>
+static PositionWithAffinityTemplate<Strategy> LogicalStartOfLineAlgorithm(
+    const PositionWithAffinityTemplate<Strategy>& c) {
+  // TODO: this is the current behavior that might need to be fixed.
+  // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
+  PositionWithAffinityTemplate<Strategy> vis_pos =
+      StartPositionForLine(c, kUseLogicalOrdering);
+
+  if (ContainerNode* editable_root = HighestEditableRoot(c.GetPosition())) {
+    if (!editable_root->contains(
+            vis_pos.GetPosition().ComputeContainerNode())) {
+      return PositionWithAffinityTemplate<Strategy>(
+          PositionTemplate<Strategy>::FirstPositionInNode(editable_root));
+    }
+  }
+
+  return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition());
+}
+
+VisiblePosition LogicalStartOfLine(const VisiblePosition& current_position) {
+  DCHECK(current_position.IsValid()) << current_position;
+  return CreateVisiblePosition(LogicalStartOfLineAlgorithm<EditingStrategy>(
+      current_position.ToPositionWithAffinity()));
+}
+
+VisiblePositionInFlatTree LogicalStartOfLine(
+    const VisiblePositionInFlatTree& current_position) {
+  DCHECK(current_position.IsValid()) << current_position;
+  return CreateVisiblePosition(
+      LogicalStartOfLineAlgorithm<EditingInFlatTreeStrategy>(
+          current_position.ToPositionWithAffinity()));
+}
+
+template <typename Strategy>
+static VisiblePositionTemplate<Strategy> EndPositionForLine(
+    const VisiblePositionTemplate<Strategy>& c,
+    LineEndpointComputationMode mode) {
+  DCHECK(c.IsValid()) << c;
+  if (c.IsNull())
+    return VisiblePositionTemplate<Strategy>();
+
+  RootInlineBox* root_box = RenderedPosition(c).RootBox();
+  if (!root_box) {
+    // There are VisiblePositions at offset 0 in blocks without
+    // RootInlineBoxes, like empty editable blocks and bordered blocks.
+    const PositionTemplate<Strategy> p = c.DeepEquivalent();
+    if (p.AnchorNode()->GetLayoutObject() &&
+        p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() &&
+        !p.ComputeEditingOffset())
+      return c;
+    return VisiblePositionTemplate<Strategy>();
+  }
+
+  Node* end_node;
+  InlineBox* end_box;
+  if (mode == kUseLogicalOrdering) {
+    end_node = root_box->GetLogicalEndBoxWithNode(end_box);
+    if (!end_node)
+      return VisiblePositionTemplate<Strategy>();
+  } else {
+    // Generated content (e.g. list markers and CSS :before and :after
+    // pseudo elements) have no corresponding DOM element, and so cannot be
+    // represented by a VisiblePosition. Use whatever precedes instead.
+    end_box = root_box->LastLeafChild();
+    while (true) {
+      if (!end_box)
+        return VisiblePositionTemplate<Strategy>();
+
+      end_node = end_box->GetLineLayoutItem().NonPseudoNode();
+      if (end_node)
+        break;
+
+      end_box = end_box->PrevLeafChild();
+    }
+  }
+
+  PositionTemplate<Strategy> pos;
+  if (isHTMLBRElement(*end_node)) {
+    pos = PositionTemplate<Strategy>::BeforeNode(end_node);
+  } else if (end_box->IsInlineTextBox() && end_node->IsTextNode()) {
+    InlineTextBox* end_text_box = ToInlineTextBox(end_box);
+    int end_offset = end_text_box->Start();
+    if (!end_text_box->IsLineBreak())
+      end_offset += end_text_box->Len();
+    pos = PositionTemplate<Strategy>(ToText(end_node), end_offset);
+  } else {
+    pos = PositionTemplate<Strategy>::AfterNode(end_node);
+  }
+
+  return CreateVisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
+}
+
+// TODO(yosin) Rename this function to reflect the fact it ignores bidi levels.
+template <typename Strategy>
+static VisiblePositionTemplate<Strategy> EndOfLineAlgorithm(
+    const VisiblePositionTemplate<Strategy>& current_position) {
+  DCHECK(current_position.IsValid()) << current_position;
+  // TODO(yosin) this is the current behavior that might need to be fixed.
+  // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
+  VisiblePositionTemplate<Strategy> vis_pos =
+      EndPositionForLine(current_position, kUseInlineBoxOrdering);
+
+  // Make sure the end of line is at the same line as the given input
+  // position. Else use the previous position to obtain end of line. This
+  // condition happens when the input position is before the space character
+  // at the end of a soft-wrapped non-editable line. In this scenario,
+  // |endPositionForLine()| would incorrectly hand back a position in the next
+  // line instead. This fix is to account for the discrepancy between lines
+  // with "webkit-line-break:after-white-space" style versus lines without
+  // that style, which would break before a space by default.
+  if (!InSameLine(current_position, vis_pos)) {
+    vis_pos = PreviousPositionOf(current_position);
+    if (vis_pos.IsNull())
+      return VisiblePositionTemplate<Strategy>();
+    vis_pos = EndPositionForLine(vis_pos, kUseInlineBoxOrdering);
+  }
+
+  return HonorEditingBoundaryAtOrAfter(vis_pos,
+                                       current_position.DeepEquivalent());
+}
+
+// TODO(yosin) Rename this function to reflect the fact it ignores bidi levels.
+VisiblePosition EndOfLine(const VisiblePosition& current_position) {
+  return EndOfLineAlgorithm<EditingStrategy>(current_position);
+}
+
+VisiblePositionInFlatTree EndOfLine(
+    const VisiblePositionInFlatTree& current_position) {
+  return EndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position);
+}
+
+template <typename Strategy>
+static bool InSameLogicalLine(const VisiblePositionTemplate<Strategy>& a,
+                              const VisiblePositionTemplate<Strategy>& b) {
+  DCHECK(a.IsValid()) << a;
+  DCHECK(b.IsValid()) << b;
+  return a.IsNotNull() && LogicalStartOfLine(a).DeepEquivalent() ==
+                              LogicalStartOfLine(b).DeepEquivalent();
+}
+
+template <typename Strategy>
+static VisiblePositionTemplate<Strategy> LogicalEndOfLineAlgorithm(
+    const VisiblePositionTemplate<Strategy>& current_position) {
+  DCHECK(current_position.IsValid()) << current_position;
+  // TODO(yosin) this is the current behavior that might need to be fixed.
+  // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
+  VisiblePositionTemplate<Strategy> vis_pos =
+      EndPositionForLine(current_position, kUseLogicalOrdering);
+
+  // Make sure the end of line is at the same line as the given input
+  // position. For a wrapping line, the logical end position for the
+  // not-last-2-lines might incorrectly hand back the logical beginning of the
+  // next line. For example,
+  // <div contenteditable dir="rtl" style="line-break:before-white-space">xyz
+  // a xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz </div>
+  // In this case, use the previous position of the computed logical end
+  // position.
+  if (!InSameLogicalLine(current_position, vis_pos))
+    vis_pos = PreviousPositionOf(vis_pos);
+
+  if (ContainerNode* editable_root =
+          HighestEditableRoot(current_position.DeepEquivalent())) {
+    if (!editable_root->contains(
+            vis_pos.DeepEquivalent().ComputeContainerNode())) {
+      return CreateVisiblePosition(
+          PositionTemplate<Strategy>::LastPositionInNode(editable_root));
+    }
+  }
+
+  return HonorEditingBoundaryAtOrAfter(vis_pos,
+                                       current_position.DeepEquivalent());
+}
+
+VisiblePosition LogicalEndOfLine(const VisiblePosition& current_position) {
+  return LogicalEndOfLineAlgorithm<EditingStrategy>(current_position);
+}
+
+VisiblePositionInFlatTree LogicalEndOfLine(
+    const VisiblePositionInFlatTree& current_position) {
+  return LogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position);
+}
+
+template <typename Strategy>
+static bool InSameLineAlgorithm(
+    const PositionWithAffinityTemplate<Strategy>& position1,
+    const PositionWithAffinityTemplate<Strategy>& position2) {
+  if (position1.IsNull() || position2.IsNull())
+    return false;
+  DCHECK_EQ(position1.GetDocument(), position2.GetDocument());
+  DCHECK(!position1.GetDocument()->NeedsLayoutTreeUpdate());
+
+  PositionWithAffinityTemplate<Strategy> start_of_line1 =
+      StartOfLine(position1);
+  PositionWithAffinityTemplate<Strategy> start_of_line2 =
+      StartOfLine(position2);
+  if (start_of_line1 == start_of_line2)
+    return true;
+  PositionTemplate<Strategy> canonicalized1 =
+      CanonicalPositionOf(start_of_line1.GetPosition());
+  if (canonicalized1 == start_of_line2.GetPosition())
+    return true;
+  return canonicalized1 == CanonicalPositionOf(start_of_line2.GetPosition());
+}
+
+bool InSameLine(const PositionWithAffinity& a, const PositionWithAffinity& b) {
+  return InSameLineAlgorithm<EditingStrategy>(a, b);
+}
+
+bool InSameLine(const PositionInFlatTreeWithAffinity& position1,
+                const PositionInFlatTreeWithAffinity& position2) {
+  return InSameLineAlgorithm<EditingInFlatTreeStrategy>(position1, position2);
+}
+
+bool InSameLine(const VisiblePosition& position1,
+                const VisiblePosition& position2) {
+  DCHECK(position1.IsValid()) << position1;
+  DCHECK(position2.IsValid()) << position2;
+  return InSameLine(position1.ToPositionWithAffinity(),
+                    position2.ToPositionWithAffinity());
+}
+
+bool InSameLine(const VisiblePositionInFlatTree& position1,
+                const VisiblePositionInFlatTree& position2) {
+  DCHECK(position1.IsValid()) << position1;
+  DCHECK(position2.IsValid()) << position2;
+  return InSameLine(position1.ToPositionWithAffinity(),
+                    position2.ToPositionWithAffinity());
+}
+
+template <typename Strategy>
+static bool IsStartOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) {
+  DCHECK(p.IsValid()) << p;
+  return p.IsNotNull() && p.DeepEquivalent() == StartOfLine(p).DeepEquivalent();
+}
+
+bool IsStartOfLine(const VisiblePosition& p) {
+  return IsStartOfLineAlgorithm<EditingStrategy>(p);
+}
+
+bool IsStartOfLine(const VisiblePositionInFlatTree& p) {
+  return IsStartOfLineAlgorithm<EditingInFlatTreeStrategy>(p);
+}
+
+template <typename Strategy>
+static bool IsEndOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) {
+  DCHECK(p.IsValid()) << p;
+  return p.IsNotNull() && p.DeepEquivalent() == EndOfLine(p).DeepEquivalent();
+}
+
+bool IsEndOfLine(const VisiblePosition& p) {
+  return IsEndOfLineAlgorithm<EditingStrategy>(p);
+}
+
+bool IsEndOfLine(const VisiblePositionInFlatTree& p) {
+  return IsEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p);
+}
+
+template <typename Strategy>
+static bool IsLogicalEndOfLineAlgorithm(
+    const VisiblePositionTemplate<Strategy>& p) {
+  DCHECK(p.IsValid()) << p;
+  return p.IsNotNull() &&
+         p.DeepEquivalent() == LogicalEndOfLine(p).DeepEquivalent();
+}
+
+bool IsLogicalEndOfLine(const VisiblePosition& p) {
+  return IsLogicalEndOfLineAlgorithm<EditingStrategy>(p);
+}
+
+bool IsLogicalEndOfLine(const VisiblePositionInFlatTree& p) {
+  return IsLogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p);
+}
+
+VisiblePosition PreviousLinePosition(const VisiblePosition& visible_position,
+                                     LayoutUnit line_direction_point,
+                                     EditableType editable_type) {
+  DCHECK(visible_position.IsValid()) << visible_position;
+
+  Position p = visible_position.DeepEquivalent();
+  Node* node = p.AnchorNode();
+
+  if (!node)
+    return VisiblePosition();
+
+  LayoutObject* layout_object = node->GetLayoutObject();
+  if (!layout_object)
+    return VisiblePosition();
+
+  RootInlineBox* root = nullptr;
+  InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box;
+  if (box) {
+    root = box->Root().PrevRootBox();
+    // We want to skip zero height boxes.
+    // This could happen in case it is a TrailingFloatsRootInlineBox.
+    if (!root || !root->LogicalHeight() || !root->FirstLeafChild())
+      root = nullptr;
+  }
+
+  if (!root) {
+    Position position = PreviousRootInlineBoxCandidatePosition(
+        node, visible_position, editable_type);
+    if (position.IsNotNull()) {
+      RenderedPosition rendered_position((CreateVisiblePosition(position)));
+      root = rendered_position.RootBox();
+      if (!root)
+        return CreateVisiblePosition(position);
+    }
+  }
+
+  if (root) {
+    // FIXME: Can be wrong for multi-column layout and with transforms.
+    LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock(
+        root, line_direction_point);
+    LineLayoutItem line_layout_item =
+        root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p))
+            ->GetLineLayoutItem();
+    Node* node = line_layout_item.GetNode();
+    if (node && EditingIgnoresContent(*node))
+      return VisiblePosition::InParentBeforeNode(*node);
+    return CreateVisiblePosition(
+        line_layout_item.PositionForPoint(point_in_line));
+  }
+
+  // Could not find a previous line. This means we must already be on the first
+  // line. Move to the start of the content in this block, which effectively
+  // moves us to the start of the line we're on.
+  Element* root_element = HasEditableStyle(*node, editable_type)
+                              ? RootEditableElement(*node, editable_type)
+                              : node->GetDocument().documentElement();
+  if (!root_element)
+    return VisiblePosition();
+  return VisiblePosition::FirstPositionInNode(root_element);
+}
+
+VisiblePosition NextLinePosition(const VisiblePosition& visible_position,
+                                 LayoutUnit line_direction_point,
+                                 EditableType editable_type) {
+  DCHECK(visible_position.IsValid()) << visible_position;
+
+  Position p = visible_position.DeepEquivalent();
+  Node* node = p.AnchorNode();
+
+  if (!node)
+    return VisiblePosition();
+
+  LayoutObject* layout_object = node->GetLayoutObject();
+  if (!layout_object)
+    return VisiblePosition();
+
+  RootInlineBox* root = nullptr;
+  InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box;
+  if (box) {
+    root = box->Root().NextRootBox();
+    // We want to skip zero height boxes.
+    // This could happen in case it is a TrailingFloatsRootInlineBox.
+    if (!root || !root->LogicalHeight() || !root->FirstLeafChild())
+      root = nullptr;
+  }
+
+  if (!root) {
+    // FIXME: We need do the same in previousLinePosition.
+    Node* child = NodeTraversal::ChildAt(*node, p.ComputeEditingOffset());
+    node = child ? child : &NodeTraversal::LastWithinOrSelf(*node);
+    Position position = NextRootInlineBoxCandidatePosition(
+        node, visible_position, editable_type);
+    if (position.IsNotNull()) {
+      RenderedPosition rendered_position((CreateVisiblePosition(position)));
+      root = rendered_position.RootBox();
+      if (!root)
+        return CreateVisiblePosition(position);
+    }
+  }
+
+  if (root) {
+    // FIXME: Can be wrong for multi-column layout and with transforms.
+    LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock(
+        root, line_direction_point);
+    LineLayoutItem line_layout_item =
+        root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p))
+            ->GetLineLayoutItem();
+    Node* node = line_layout_item.GetNode();
+    if (node && EditingIgnoresContent(*node))
+      return VisiblePosition::InParentBeforeNode(*node);
+    return CreateVisiblePosition(
+        line_layout_item.PositionForPoint(point_in_line));
+  }
+
+  // Could not find a next line. This means we must already be on the last line.
+  // Move to the end of the content in this block, which effectively moves us
+  // to the end of the line we're on.
+  Element* root_element = HasEditableStyle(*node, editable_type)
+                              ? RootEditableElement(*node, editable_type)
+                              : node->GetDocument().documentElement();
+  if (!root_element)
+    return VisiblePosition();
+  return VisiblePosition::LastPositionInNode(root_element);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
index 6784200..0fe6039 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
@@ -187,7 +187,7 @@
       shadow_depth_(
           ShadowDepthOf<Strategy>(*start_container_, *end_container_)),
       behavior_(AdjustBehaviorFlags<Strategy>(behavior)),
-      text_state_(new TextIteratorTextState(behavior_)),
+      text_state_(new TextIteratorTextState()),
       text_node_handler_(
           new TextIteratorTextNodeHandler(behavior_, text_state_)) {
   DCHECK(start_container_);
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp
index d4656e6e..10d340b 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp
@@ -450,9 +450,10 @@
                                            LayoutText* layout_object,
                                            int text_start_offset,
                                            int text_end_offset) {
-  const String& string = behavior_.EmitsOriginalText()
-                             ? layout_object->OriginalText()
-                             : layout_object->GetText();
+  String string = behavior_.EmitsOriginalText() ? layout_object->OriginalText()
+                                                : layout_object->GetText();
+  if (behavior_.EmitsSpaceForNbsp())
+    string.Replace(kNoBreakSpaceCharacter, kSpaceCharacter);
   text_state_->EmitText(text_node,
                         text_start_offset + layout_object->TextStartOffset(),
                         text_end_offset + layout_object->TextStartOffset(),
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
index 060b538..005dad5 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
@@ -27,15 +27,10 @@
 
 #include "core/editing/iterators/TextIteratorTextState.h"
 
-#include "core/editing/iterators/TextIteratorBehavior.h"
 #include "platform/wtf/text/StringBuilder.h"
 
 namespace blink {
 
-TextIteratorTextState::TextIteratorTextState(
-    const TextIteratorBehavior& behavior)
-    : behavior_(behavior) {}
-
 DEFINE_TRACE(TextIteratorTextState) {
   visitor->Trace(position_node_);
   visitor->Trace(position_offset_base_node_);
@@ -150,11 +145,6 @@
   DCHECK(text_node);
   text_ = string;
 
-  // TODO(xiaochengh): Hoist the conversion to TextIteratorTextNodeHandler, so
-  // that we can remove |behavior_| from TextIteratorTextState.
-  if (behavior_.EmitsSpaceForNbsp())
-    text_.Replace(kNoBreakSpaceCharacter, kSpaceCharacter);
-
   DCHECK(!text_.IsEmpty());
   DCHECK_LE(0, text_start_offset);
   DCHECK_LT(text_start_offset, static_cast<int>(text_.length()));
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
index 1be6394..e940484 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
@@ -30,17 +30,14 @@
 #include "core/CoreExport.h"
 #include "core/dom/Range.h"
 #include "core/editing/iterators/ForwardsTextBuffer.h"
-#include "core/editing/iterators/TextIteratorBehavior.h"
 #include "platform/wtf/text/WTFString.h"
 
 namespace blink {
 
-class TextIteratorBehavior;
-
 class CORE_EXPORT TextIteratorTextState
     : public GarbageCollectedFinalized<TextIteratorTextState> {
  public:
-  explicit TextIteratorTextState(const TextIteratorBehavior&);
+  TextIteratorTextState() {}
   ~TextIteratorTextState() {}
 
   // Return properties of the current text.
@@ -107,8 +104,6 @@
   bool has_emitted_ = false;
   UChar last_character_ = 0;
 
-  const TextIteratorBehavior behavior_;
-
   DISALLOW_COPY_AND_ASSIGN(TextIteratorTextState);
 };
 
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index d5a52e2..2028d33 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -90,6 +90,7 @@
   BUTTON_DROP_DOWN: 'buttonDropDown',
   CANVAS: 'canvas',
   CAPTION: 'caption',
+  CARET: 'caret',
   CELL: 'cell',
   CHECK_BOX: 'checkBox',
   CLIENT: 'client',
@@ -110,7 +111,6 @@
   DIALOG: 'dialog',
   DIRECTORY: 'directory',
   DISCLOSURE_TRIANGLE: 'disclosureTriangle',
-  DIV: 'div',
   DOCUMENT: 'document',
   EMBEDDED_OBJECT: 'embeddedObject',
   FEED: 'feed',
@@ -118,6 +118,7 @@
   FIGURE: 'figure',
   FOOTER: 'footer',
   FORM: 'form',
+  GENERIC_CONTAINER: 'genericContainer',
   GRID: 'grid',
   GROUP: 'group',
   HEADING: 'heading',
@@ -214,7 +215,6 @@
  */
 chrome.automation.StateType = {
   BUSY: 'busy',
-  CHECKED: 'checked',
   COLLAPSED: 'collapsed',
   DEFAULT: 'default',
   DISABLED: 'disabled',
@@ -230,7 +230,6 @@
   MULTILINE: 'multiline',
   MULTISELECTABLE: 'multiselectable',
   OFFSCREEN: 'offscreen',
-  PRESSED: 'pressed',
   PROTECTED: 'protected',
   READ_ONLY: 'readOnly',
   REQUIRED: 'required',
@@ -260,6 +259,7 @@
 chrome.automation.NameFromType = {
   UNINITIALIZED: 'uninitialized',
   ATTRIBUTE: 'attribute',
+  ATTRIBUTE_EXPLICITLY_EMPTY: 'attributeExplicitlyEmpty',
   CONTENTS: 'contents',
   PLACEHOLDER: 'placeholder',
   RELATED_ELEMENT: 'relatedElement',
@@ -919,11 +919,11 @@
 chrome.automation.AutomationNode.prototype.language;
 
 /**
- * If a checkbox or toggle button is in the mixed state.
- * @type {(boolean|undefined)}
- * @see https://developer.chrome.com/extensions/automation#type-buttonMixed
+ * Tri-state describing checkbox or radio button: 'false' | 'true' | 'mixed'
+ * @type {(string|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-checked
  */
-chrome.automation.AutomationNode.prototype.buttonMixed;
+chrome.automation.AutomationNode.prototype.checked;
 
 /**
  * The RGBA foreground color of this subtree, as an integer.