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.