Commit c5fece63 authored by Navid Zolghadr's avatar Navid Zolghadr Committed by Commit Bot

Use async targeting to find drag and drop targets

Add callback supports to the RenderWidgetTargeter
and use the async targeting in drag and drop code
and register a callback to send drag events using
the callback. Note that since drag and drop events
aren't WebInputEvent it is not possible to use
FindTargetAndDispatch function of RenderWidgetTargeter
at this time.

This is exactly the same as the CL before:
https://chromium-review.googlesource.com/c/chromium/src/+/1641383
but rebased on top of another change that moves the
drop operations to take ownership of the OSExchangeData:
https://chromium-review.googlesource.com/c/chromium/src/+/1666910

TBR=sadrul@chromium.org

Bug: 804633, 967801
Change-Id: If4f53e880bb805c87d13869348a57cd857adee5a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1693442Reviewed-by: default avatarNavid Zolghadr <nzolghadr@chromium.org>
Commit-Queue: Navid Zolghadr <nzolghadr@chromium.org>
Auto-Submit: Navid Zolghadr <nzolghadr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#678475}
parent 4fc9d002
......@@ -1389,6 +1389,13 @@ RenderWidgetHostInputEventRouter::GetRenderWidgetHostAtPoint(
.view->GetRenderWidgetHost());
}
void RenderWidgetHostInputEventRouter::GetRenderWidgetHostAtPointAsynchronously(
RenderWidgetHostViewBase* root_view,
const gfx::PointF& point,
RenderWidgetTargeter::RenderWidgetHostAtPointCallback callback) {
event_targeter_->FindTargetAndCallback(root_view, point, std::move(callback));
}
RenderWidgetTargetResult
RenderWidgetHostInputEventRouter::FindTouchscreenGestureEventTarget(
RenderWidgetHostViewBase* root_view,
......@@ -1761,6 +1768,16 @@ RenderWidgetHostInputEventRouter::GetRenderWidgetTargeterForTests() {
return event_targeter_.get();
}
RenderWidgetTargetResult
RenderWidgetHostInputEventRouter::FindTargetSynchronouslyAtPoint(
RenderWidgetHostViewBase* root_view,
const gfx::PointF& location) {
gfx::PointF transformed_pt;
// The transformed point is already in the return value of FindViewAtLocation.
return FindViewAtLocation(root_view, location, viz::EventSource::MOUSE,
&transformed_pt);
}
RenderWidgetTargetResult
RenderWidgetHostInputEventRouter::FindTargetSynchronously(
RenderWidgetHostViewBase* root_view,
......
......@@ -132,6 +132,17 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter
const gfx::PointF& point,
gfx::PointF* transformed_point);
// Finds the RenderWidgetHostImpl inside the |root_view| at |point| where
// |point| is with respect to |root_view|'s coordinates. If a RWHI is found,
// it is passed along with the coordinate of the point with
// respect to the RWHI's coordinates to the callback function. If
// |root_view| is nullptr or RWHI is not found, the callback is called with
// nullptr and no location.
void GetRenderWidgetHostAtPointAsynchronously(
RenderWidgetHostViewBase* root_view,
const gfx::PointF& point,
RenderWidgetTargeter::RenderWidgetHostAtPointCallback callback);
// RenderWidgetTargeter::Delegate:
RenderWidgetHostViewBase* FindViewFromFrameSinkId(
const viz::FrameSinkId& frame_sink_id) const override;
......@@ -293,6 +304,10 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter
const RenderWidgetHostViewBase* view);
// RenderWidgetTargeter::Delegate:
RenderWidgetTargetResult FindTargetSynchronouslyAtPoint(
RenderWidgetHostViewBase* root_view,
const gfx::PointF& location) override;
RenderWidgetTargetResult FindTargetSynchronously(
RenderWidgetHostViewBase* root_view,
const blink::WebInputEvent& event) override;
......
......@@ -22,17 +22,6 @@ namespace content {
namespace {
bool MergeEventIfPossible(const blink::WebInputEvent& event,
ui::WebScopedInputEvent* blink_event) {
if (!blink::WebInputEvent::IsTouchEventType(event.GetType()) &&
!blink::WebInputEvent::IsGestureEventType(event.GetType()) &&
ui::CanCoalesce(event, **blink_event)) {
ui::Coalesce(event, blink_event->get());
return true;
}
return false;
}
gfx::PointF ComputeEventLocation(const blink::WebInputEvent& event) {
if (blink::WebInputEvent::IsMouseEventType(event.GetType()) ||
event.GetType() == blink::WebInputEvent::kMouseWheel) {
......@@ -118,7 +107,24 @@ RenderWidgetTargetResult::RenderWidgetTargetResult(
RenderWidgetTargetResult::~RenderWidgetTargetResult() = default;
RenderWidgetTargeter::TargetingRequest::TargetingRequest() = default;
RenderWidgetTargeter::TargetingRequest::TargetingRequest(
base::WeakPtr<RenderWidgetHostViewBase> root_view,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency) {
this->root_view = std::move(root_view);
this->location = ComputeEventLocation(event);
this->event = ui::WebInputEventTraits::Clone(event);
this->latency = latency;
}
RenderWidgetTargeter::TargetingRequest::TargetingRequest(
base::WeakPtr<RenderWidgetHostViewBase> root_view,
const gfx::PointF& location,
RenderWidgetHostAtPointCallback callback) {
this->root_view = std::move(root_view);
this->location = location;
this->callback = std::move(callback);
}
RenderWidgetTargeter::TargetingRequest::TargetingRequest(
TargetingRequest&& request) = default;
......@@ -128,6 +134,68 @@ operator=(TargetingRequest&&) = default;
RenderWidgetTargeter::TargetingRequest::~TargetingRequest() = default;
void RenderWidgetTargeter::TargetingRequest::RunCallback(
RenderWidgetHostViewBase* target,
base::Optional<gfx::PointF> point) {
if (!callback.is_null()) {
std::move(callback).Run(target ? target->GetWeakPtr() : nullptr, point);
}
}
bool RenderWidgetTargeter::TargetingRequest::MergeEventIfPossible(
const blink::WebInputEvent& new_event) {
if (event && !blink::WebInputEvent::IsTouchEventType(new_event.GetType()) &&
!blink::WebInputEvent::IsGestureEventType(new_event.GetType()) &&
ui::CanCoalesce(new_event, *event.get())) {
ui::Coalesce(new_event, event.get());
return true;
}
return false;
}
void RenderWidgetTargeter::TargetingRequest::StartQueueingTimeTracker() {
tracker =
std::make_unique<TracingUmaTracker>("Event.AsyncTargeting.TimeInQueue");
}
void RenderWidgetTargeter::TargetingRequest::StopQueueingTimeTracker() {
if (tracker)
tracker->Stop();
}
bool RenderWidgetTargeter::TargetingRequest::IsWebInputEventRequest() const {
return !!event;
}
const blink::WebInputEvent& RenderWidgetTargeter::TargetingRequest::GetEvent()
const {
return *event.get();
}
RenderWidgetHostViewBase* RenderWidgetTargeter::TargetingRequest::GetRootView()
const {
return root_view.get();
}
gfx::PointF RenderWidgetTargeter::TargetingRequest::GetLocation() const {
return location;
}
viz::FrameSinkId
RenderWidgetTargeter::TargetingRequest::GetExpectedFrameSinkId() const {
return expected_frame_sink_id;
}
void RenderWidgetTargeter::TargetingRequest::SetExpectedFrameSinkId(
const viz::FrameSinkId& id) {
expected_frame_sink_id = id;
}
const ui::LatencyInfo& RenderWidgetTargeter::TargetingRequest::GetLatency()
const {
return latency;
}
RenderWidgetTargeter::RenderWidgetTargeter(Delegate* delegate)
: trace_id_(base::RandUint64()),
is_viz_hit_testing_debug_enabled_(
......@@ -151,35 +219,48 @@ void RenderWidgetTargeter::FindTargetAndDispatch(
static_cast<const blink::WebGestureEvent&>(event).SourceDevice() ==
blink::WebGestureDevice::kTouchpad)));
if (request_in_flight_) {
if (!requests_.empty()) {
auto& request = requests_.back();
if (MergeEventIfPossible(event, &request.event))
if (request.MergeEventIfPossible(event))
return;
}
TargetingRequest request;
request.root_view = root_view->GetWeakPtr();
request.event = ui::WebInputEventTraits::Clone(event);
request.latency = latency;
request.tracker =
std::make_unique<TracingUmaTracker>("Event.AsyncTargeting.TimeInQueue");
TargetingRequest request(root_view->GetWeakPtr(), event, latency);
ResolveTargetingRequest(std::move(request));
}
void RenderWidgetTargeter::FindTargetAndCallback(
RenderWidgetHostViewBase* root_view,
const gfx::PointF& point,
RenderWidgetHostAtPointCallback callback) {
TargetingRequest request(root_view->GetWeakPtr(), point, std::move(callback));
ResolveTargetingRequest(std::move(request));
}
void RenderWidgetTargeter::ResolveTargetingRequest(TargetingRequest request) {
if (request_in_flight_) {
request.StartQueueingTimeTracker();
requests_.push(std::move(request));
return;
}
RenderWidgetTargetResult result =
delegate_->FindTargetSynchronously(root_view, event);
const gfx::PointF event_location = ComputeEventLocation(event);
RenderWidgetTargetResult result;
if (request.IsWebInputEventRequest()) {
result = delegate_->FindTargetSynchronously(request.GetRootView(),
request.GetEvent());
} else {
result = delegate_->FindTargetSynchronouslyAtPoint(request.GetRootView(),
request.GetLocation());
}
RenderWidgetHostViewBase* target = result.view;
auto* event_ptr = &event;
async_depth_ = 0;
if (result.should_query_view) {
TRACE_EVENT_WITH_FLOW2(
"viz,benchmark", "Event.Pipeline", TRACE_ID_GLOBAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_OUT, "step", "QueryClient(Start)",
"event_location", event_location.ToString());
"event_location", request.GetLocation().ToString());
// TODO(kenrb, sadrul): When all event types support asynchronous hit
// testing, we should be able to have FindTargetSynchronously return the
......@@ -189,17 +270,15 @@ void RenderWidgetTargeter::FindTargetAndDispatch(
// root_view and the original event location for the initial query.
// Do not compare hit test results if we are forced to do async hit testing
// by HitTestQuery.
QueryClient(root_view, root_view, *event_ptr, latency, event_location,
nullptr, gfx::PointF());
QueryClient(std::move(request));
} else {
FoundTarget(root_view, target, *event_ptr, latency, result.target_location,
result.latched_target, viz::FrameSinkId());
FoundTarget(target, result.target_location, result.latched_target,
&request);
// Verify the event targeting results from surface layer viz hit testing if
// --use-viz-hit-test-surface-layer is enabled.
if (result.should_verify_result && !target->IsRenderWidgetHostViewGuest()) {
QueryAndVerifyClient(root_view, root_view, *event_ptr, latency,
event_location, nullptr, gfx::PointF(),
target->GetFrameSinkId());
request.SetExpectedFrameSinkId(target->GetFrameSinkId());
QueryAndVerifyClient(std::move(request));
}
}
}
......@@ -213,17 +292,14 @@ bool RenderWidgetTargeter::HasEventsPendingDispatch() const {
}
void RenderWidgetTargeter::QueryClientInternal(
RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const gfx::PointF& target_location,
RenderWidgetHostViewBase* last_request_target,
const gfx::PointF& last_target_location,
const viz::FrameSinkId& expected_frame_sink_id) {
TargetingRequest request) {
// Async event targeting and verifying use two different queues, so they don't
// block each other.
bool is_verifying = expected_frame_sink_id.is_valid();
bool is_verifying = request.GetExpectedFrameSinkId().is_valid();
DCHECK((!is_verifying && !request_in_flight_) ||
(is_verifying && !verify_request_in_flight_));
......@@ -232,15 +308,14 @@ void RenderWidgetTargeter::QueryClientInternal(
// understand why this happens. https://crbug.com/859492.
// We do not verify hit testing result under this circumstance.
if (!target_client) {
FoundTarget(root_view, target, event, latency, target_location, false,
viz::FrameSinkId());
FoundTarget(target, target_location, false, &request);
return;
}
if (is_verifying) {
verify_request_in_flight_ = true;
verify_request_in_flight_ = std::move(request);
} else {
request_in_flight_ = true;
request_in_flight_ = std::move(request);
async_depth_++;
}
TracingUmaTracker tracker("Event.AsyncTargeting.ResponseTime");
......@@ -249,67 +324,46 @@ void RenderWidgetTargeter::QueryClientInternal(
hit_test_timeout.reset(new OneShotTimeoutMonitor(
base::BindOnce(
&RenderWidgetTargeter::AsyncHitTestTimedOut,
weak_ptr_factory_.GetWeakPtr(), root_view->GetWeakPtr(),
target->GetWeakPtr(), target_location,
weak_ptr_factory_.GetWeakPtr(), target->GetWeakPtr(), target_location,
last_request_target ? last_request_target->GetWeakPtr() : nullptr,
last_target_location, ui::WebInputEventTraits::Clone(event), latency,
expected_frame_sink_id),
last_target_location, is_verifying),
async_hit_test_timeout_delay_));
TRACE_EVENT_WITH_FLOW2(
"viz,benchmark", "Event.Pipeline", TRACE_ID_GLOBAL(trace_id_),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
"QueryClient", "event", blink::WebInputEvent::GetName(event.GetType()));
"QueryClient", "event_location", request.GetLocation().ToString());
target_client->FrameSinkIdAt(
target_location, trace_id_,
base::BindOnce(
&RenderWidgetTargeter::FoundFrameSinkId,
weak_ptr_factory_.GetWeakPtr(), root_view->GetWeakPtr(),
target->GetWeakPtr(), ui::WebInputEventTraits::Clone(event), latency,
weak_ptr_factory_.GetWeakPtr(), target->GetWeakPtr(),
is_verifying ? ++last_verify_request_id_ : ++last_request_id_,
target_location, std::move(tracker), expected_frame_sink_id));
target_location, std::move(tracker), is_verifying));
}
void RenderWidgetTargeter::QueryClient(
RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const gfx::PointF& target_location,
RenderWidgetHostViewBase* last_request_target,
const gfx::PointF& last_target_location) {
QueryClientInternal(root_view, target, event, latency, target_location,
last_request_target, last_target_location,
viz::FrameSinkId());
void RenderWidgetTargeter::QueryClient(TargetingRequest request) {
auto* target = request.GetRootView();
auto target_location = request.GetLocation();
QueryClientInternal(target, target_location, nullptr, gfx::PointF(),
std::move(request));
}
void RenderWidgetTargeter::QueryAndVerifyClient(
RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const gfx::PointF& target_location,
RenderWidgetHostViewBase* last_request_target,
const gfx::PointF& last_target_location,
const viz::FrameSinkId& expected_frame_sink_id) {
void RenderWidgetTargeter::QueryAndVerifyClient(TargetingRequest request) {
if (verify_request_in_flight_) {
TargetingRequest request;
request.root_view = root_view->GetWeakPtr();
request.event = ui::WebInputEventTraits::Clone(event);
request.latency = latency;
request.expected_frame_sink_id = expected_frame_sink_id;
verify_requests_.push(std::move(request));
return;
}
QueryClientInternal(root_view, target, event, latency, target_location,
last_request_target, last_target_location,
expected_frame_sink_id);
auto* target = request.GetRootView();
auto target_location = request.GetLocation();
QueryClientInternal(target, target_location, nullptr, gfx::PointF(),
std::move(request));
}
void RenderWidgetTargeter::FlushEventQueue(bool is_verifying) {
bool events_being_flushed = false;
bool& request_in_flight =
base::Optional<TargetingRequest>& request_in_flight =
is_verifying ? verify_request_in_flight_ : request_in_flight_;
auto* requests = is_verifying ? &verify_requests_ : &requests_;
while (!request_in_flight && !requests->empty()) {
......@@ -317,11 +371,10 @@ void RenderWidgetTargeter::FlushEventQueue(bool is_verifying) {
requests->pop();
// The root-view has gone away. Ignore this event, and try to process the
// next event.
if (!request.root_view) {
if (!request.GetRootView()) {
continue;
}
if (request.tracker)
request.tracker->Stop();
request.StopQueueingTimeTracker();
// Only notify the delegate once that the current event queue is being
// flushed. Once all the events are flushed, notify the delegate again.
if (!is_verifying && !events_being_flushed) {
......@@ -329,13 +382,9 @@ void RenderWidgetTargeter::FlushEventQueue(bool is_verifying) {
events_being_flushed = true;
}
if (is_verifying) {
QueryAndVerifyClient(request.root_view.get(), request.root_view.get(),
*request.event, request.latency,
ComputeEventLocation(*request.event), nullptr,
gfx::PointF(), request.expected_frame_sink_id);
QueryAndVerifyClient(std::move(request));
} else {
FindTargetAndDispatch(request.root_view.get(), *request.event,
request.latency);
ResolveTargetingRequest(std::move(request));
}
}
if (!is_verifying)
......@@ -343,25 +392,24 @@ void RenderWidgetTargeter::FlushEventQueue(bool is_verifying) {
}
void RenderWidgetTargeter::FoundFrameSinkId(
base::WeakPtr<RenderWidgetHostViewBase> root_view,
base::WeakPtr<RenderWidgetHostViewBase> target,
ui::WebScopedInputEvent event,
const ui::LatencyInfo& latency,
uint32_t request_id,
const gfx::PointF& target_location,
TracingUmaTracker tracker,
const viz::FrameSinkId& expected_frame_sink_id,
const bool is_verification_request,
const viz::FrameSinkId& frame_sink_id,
const gfx::PointF& transformed_location) {
if (expected_frame_sink_id.is_valid()) {
if (is_verification_request) {
tracker.Stop();
} else {
tracker.StopAndRecord();
}
uint32_t last_id = expected_frame_sink_id.is_valid() ? last_verify_request_id_
: last_request_id_;
bool in_flight = expected_frame_sink_id.is_valid() ? verify_request_in_flight_
: request_in_flight_;
uint32_t last_id =
is_verification_request ? last_verify_request_id_ : last_request_id_;
bool in_flight = is_verification_request
? verify_request_in_flight_.has_value()
: request_in_flight_.has_value();
if (request_id != last_id || !in_flight) {
// This is a response to a request that already timed out, so the event
// should have already been dispatched. Mark the renderer as responsive
......@@ -370,15 +418,20 @@ void RenderWidgetTargeter::FoundFrameSinkId(
return;
}
if (expected_frame_sink_id.is_valid()) {
verify_request_in_flight_ = false;
TargetingRequest request = is_verification_request
? std::move(verify_request_in_flight_.value())
: std::move(request_in_flight_.value());
if (request.GetExpectedFrameSinkId().is_valid()) {
verify_request_in_flight_.reset();
async_verify_hit_test_timeout_.reset(nullptr);
} else {
request_in_flight_ = false;
request_in_flight_.reset();
async_hit_test_timeout_.reset(nullptr);
if (is_viz_hit_testing_debug_enabled_ &&
event->GetType() == blink::WebInputEvent::Type::kMouseDown) {
if (is_viz_hit_testing_debug_enabled_ && request.IsWebInputEventRequest() &&
request.GetEvent().GetType() ==
blink::WebInputEvent::Type::kMouseDown) {
hit_test_async_queried_debug_queue_.push_back(target->GetFrameSinkId());
}
}
......@@ -399,46 +452,45 @@ void RenderWidgetTargeter::FoundFrameSinkId(
TRACE_EVENT_FLAG_FLOW_IN, "step", "FoundTarget");
}
FoundTarget(root_view.get(), view, *event, latency, transformed_location,
false, expected_frame_sink_id);
FoundTarget(view, transformed_location, false, &request);
} else {
QueryClientInternal(root_view.get(), view, *event, latency,
transformed_location, target.get(), target_location,
expected_frame_sink_id);
QueryClientInternal(view, transformed_location, target.get(),
target_location, std::move(request));
}
}
void RenderWidgetTargeter::FoundTarget(
RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const base::Optional<gfx::PointF>& target_location,
bool latched_target,
const viz::FrameSinkId& expected_frame_sink_id) {
TargetingRequest* request) {
DCHECK(request);
if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites() &&
!latched_target && !expected_frame_sink_id.is_valid()) {
!latched_target && !request->GetExpectedFrameSinkId().is_valid()) {
UMA_HISTOGRAM_COUNTS_100("Event.AsyncTargeting.AsyncClientDepth",
async_depth_);
}
// RenderWidgetHostViewMac can be deleted asynchronously, in which case the
// View will be valid but there will no longer be a RenderWidgetHostImpl.
if (!root_view || !root_view->GetRenderWidgetHost())
if (!request->GetRootView() || !request->GetRootView()->GetRenderWidgetHost())
return;
if (is_viz_hit_testing_debug_enabled_ &&
!hit_test_async_queried_debug_queue_.empty()) {
GetHostFrameSinkManager()->SetHitTestAsyncQueriedDebugRegions(
root_view->GetRootFrameSinkId(), hit_test_async_queried_debug_queue_);
request->GetRootView()->GetRootFrameSinkId(),
hit_test_async_queried_debug_queue_);
hit_test_async_queried_debug_queue_.clear();
}
if (features::IsVizHitTestingSurfaceLayerEnabled() &&
expected_frame_sink_id.is_valid()) {
request->GetExpectedFrameSinkId().is_valid()) {
static const char* kResultsMatchHistogramName =
"Event.VizHitTestSurfaceLayer.ResultsMatch";
bool results_match = target->GetFrameSinkId() == expected_frame_sink_id;
bool results_match =
target->GetFrameSinkId() == request->GetExpectedFrameSinkId();
HitTestResultsMatch match_result =
HitTestResultsMatch::kHitTestResultChanged;
if (results_match) {
......@@ -447,10 +499,17 @@ void RenderWidgetTargeter::FoundTarget(
// If the results do not match, it is possible that the hit test data
// changed during verification. We do synchronous hit test again to make
// sure the result is reliable.
RenderWidgetTargetResult result =
delegate_->FindTargetSynchronously(root_view, event);
RenderWidgetTargetResult result;
if (request->IsWebInputEventRequest()) {
result = delegate_->FindTargetSynchronously(request->GetRootView(),
request->GetEvent());
} else {
result = delegate_->FindTargetSynchronouslyAtPoint(
request->GetRootView(), request->GetLocation());
}
if (!result.should_query_view && result.view &&
expected_frame_sink_id == result.view->GetFrameSinkId()) {
request->GetExpectedFrameSinkId() == result.view->GetFrameSinkId()) {
// If the result did not change, it is likely that viz hit test finds
// the wrong target.
match_result = HitTestResultsMatch::kDoNotMatch;
......@@ -465,48 +524,54 @@ void RenderWidgetTargeter::FoundTarget(
return;
}
delegate_->DispatchEventToTarget(root_view, target, event, latency,
if (request->IsWebInputEventRequest()) {
delegate_->DispatchEventToTarget(request->GetRootView(), target,
request->GetEvent(), request->GetLatency(),
target_location);
} else {
request->RunCallback(target, target_location);
}
FlushEventQueue(false);
}
void RenderWidgetTargeter::AsyncHitTestTimedOut(
base::WeakPtr<RenderWidgetHostViewBase> current_request_root_view,
base::WeakPtr<RenderWidgetHostViewBase> current_request_target,
const gfx::PointF& current_target_location,
base::WeakPtr<RenderWidgetHostViewBase> last_request_target,
const gfx::PointF& last_target_location,
ui::WebScopedInputEvent event,
const ui::LatencyInfo& latency,
const viz::FrameSinkId& expected_frame_sink_id) {
const bool is_verification_request) {
DCHECK(request_in_flight_ || verify_request_in_flight_);
TargetingRequest request = is_verification_request
? std::move(verify_request_in_flight_.value())
: std::move(request_in_flight_.value());
// If we time out during a verification, we early out to avoid dispatching
// event to root frame.
if (expected_frame_sink_id.is_valid()) {
verify_request_in_flight_ = false;
if (request.GetExpectedFrameSinkId().is_valid()) {
verify_request_in_flight_.reset();
return;
} else {
request_in_flight_ = false;
request_in_flight_.reset();
}
if (!current_request_root_view)
if (!request.GetRootView())
return;
// Mark view as unresponsive so further events will not be sent to it.
if (current_request_target)
unresponsive_views_.insert(current_request_target.get());
if (current_request_root_view.get() == current_request_target.get()) {
if (request.GetRootView() == current_request_target.get()) {
// When a request to the top-level frame times out then the event gets
// sent there anyway. It will trigger the hung renderer dialog if the
// renderer fails to process it.
FoundTarget(current_request_root_view.get(),
current_request_root_view.get(), *event, latency,
current_target_location, false, viz::FrameSinkId());
FoundTarget(current_request_target.get(), current_target_location, false,
&request);
} else {
FoundTarget(current_request_root_view.get(), last_request_target.get(),
*event, latency, last_target_location, false,
viz::FrameSinkId());
FoundTarget(last_request_target.get(), last_target_location, false,
&request);
}
}
......
......@@ -6,12 +6,12 @@
#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_TARGETER_H_
#include <queue>
#include <unordered_set>
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/content_constants_internal.h"
#include "content/common/content_export.h"
#include "ui/events/blink/web_input_event_traits.h"
......@@ -60,10 +60,18 @@ class TracingUmaTracker;
class RenderWidgetTargeter {
public:
using RenderWidgetHostAtPointCallback =
base::OnceCallback<void(base::WeakPtr<RenderWidgetHostViewBase>,
base::Optional<gfx::PointF>)>;
class Delegate {
public:
virtual ~Delegate() {}
virtual RenderWidgetTargetResult FindTargetSynchronouslyAtPoint(
RenderWidgetHostViewBase* root_view,
const gfx::PointF& location) = 0;
virtual RenderWidgetTargetResult FindTargetSynchronously(
RenderWidgetHostViewBase* root_view,
const blink::WebInputEvent& event) = 0;
......@@ -97,6 +105,13 @@ class RenderWidgetTargeter {
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency);
// Finds the appropriate target inside |root_view| for |point|, and passes the
// target along with the transformed coordinates of the point with respect to
// the target's coordinates.
void FindTargetAndCallback(RenderWidgetHostViewBase* root_view,
const gfx::PointF& point,
RenderWidgetHostAtPointCallback callback);
void ViewWillBeDestroyed(RenderWidgetHostViewBase* view);
bool HasEventsPendingDispatch() const;
......@@ -107,121 +122,135 @@ class RenderWidgetTargeter {
}
size_t num_requests_in_queue_for_testing() { return requests_.size(); }
bool is_request_in_flight_for_testing() { return request_in_flight_; }
bool is_request_in_flight_for_testing() {
return request_in_flight_.has_value();
}
private:
class TargetingRequest {
public:
TargetingRequest(base::WeakPtr<RenderWidgetHostViewBase>,
const blink::WebInputEvent&,
const ui::LatencyInfo&);
TargetingRequest(base::WeakPtr<RenderWidgetHostViewBase>,
const gfx::PointF&,
RenderWidgetHostAtPointCallback);
TargetingRequest(TargetingRequest&& request);
TargetingRequest& operator=(TargetingRequest&& other);
~TargetingRequest();
void RunCallback(RenderWidgetHostViewBase* target,
base::Optional<gfx::PointF> point);
bool MergeEventIfPossible(const blink::WebInputEvent& event);
bool IsWebInputEventRequest() const;
const blink::WebInputEvent& GetEvent() const;
RenderWidgetHostViewBase* GetRootView() const;
gfx::PointF GetLocation() const;
const ui::LatencyInfo& GetLatency() const;
// Queued TargetingRequest
void StartQueueingTimeTracker();
void StopQueueingTimeTracker();
// Verification TargetingRequest
viz::FrameSinkId GetExpectedFrameSinkId() const;
void SetExpectedFrameSinkId(const viz::FrameSinkId& id);
private:
base::WeakPtr<RenderWidgetHostViewBase> root_view;
RenderWidgetHostAtPointCallback callback;
// |location| is in the coordinate space of |root_view| which is
// either set directly when event is null or calculated from the event.
gfx::PointF location;
// |event| if set is in the coordinate space of |root_view|.
ui::WebScopedInputEvent event;
ui::LatencyInfo latency;
// |expected_frame_sink_id| is only valid if the request is for
// verification. It is temporarily added for v2 viz hit testing.
// V2 uses cc generated hit test data and we need to verify its correctness.
// The variable is the target frame sink id v2 finds in synchronous hit
// testing. It should be the same as the async hit testing target if v2
// works correctly.
viz::FrameSinkId expected_frame_sink_id;
// To track how long the request has been queued.
std::unique_ptr<TracingUmaTracker> tracker;
DISALLOW_COPY_AND_ASSIGN(TargetingRequest);
};
void ResolveTargetingRequest(TargetingRequest);
// Attempts to target and dispatch all events in the queue. It stops if it has
// to query a client, or if the queue becomes empty.
void FlushEventQueue(bool is_verifying);
// Queries |target| to find the correct target for |event|.
// |event| is in the coordinate space of |root_view|.
// |target_location|, if set, is the location in |target|'s coordinate space.
// Queries |target| to find the correct target for |request|.
// |target_location| is the location in |target|'s coordinate space.
// |last_request_target| and |last_target_location| provide a fallback target
// the case that the query times out. These should be null values when
// in the case that the query times out. These should be null values when
// querying the root view, and the target's immediate parent view otherwise.
// |expected_frame_sink_id| is temporarily added for v2 viz hit testing.
// V2 uses cc generated hit test data and we need to verify its correctness.
// The variable is the target frame sink id v2 finds in synchronous hit
// testing. It should be the same as the async hit testing target if v2 works
// correctly.
// TODO(sunxd): Remove |expected_frame_sink_id| after verifying synchronous
// hit testing correctness. See https://crbug.com/871996.
void QueryClientInternal(RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
void QueryClientInternal(RenderWidgetHostViewBase* target,
const gfx::PointF& target_location,
RenderWidgetHostViewBase* last_request_target,
const gfx::PointF& last_target_location,
const viz::FrameSinkId& expected_frame_sink_id);
TargetingRequest request);
void QueryClient(RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const gfx::PointF& target_location,
RenderWidgetHostViewBase* last_request_target,
const gfx::PointF& last_target_location);
void QueryClient(TargetingRequest request);
void QueryAndVerifyClient(RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
const gfx::PointF& target_location,
RenderWidgetHostViewBase* last_request_target,
const gfx::PointF& last_target_location,
const viz::FrameSinkId& expected_frame_sink_id);
void QueryAndVerifyClient(TargetingRequest request);
// |event| is in the coordinate space of |root_view|. |target_location|, if
// |target_location|, if
// set, is the location in |target|'s coordinate space.
// |target| is the current target that will be queried using its
// InputTargetClient interface.
// |frame_sink_id| is returned from the InputTargetClient to indicate where
// the event should be routed, and |transformed_location| is the point in
// that new target's coordinate space.
// |expected_frame_sink_id| is the expected hit test result based on
// synchronous event targeting with cc generated data.
void FoundFrameSinkId(base::WeakPtr<RenderWidgetHostViewBase> root_view,
base::WeakPtr<RenderWidgetHostViewBase> target,
ui::WebScopedInputEvent event,
const ui::LatencyInfo& latency,
// |is_verification_request| is true if this targeting request was for
// verification only.
void FoundFrameSinkId(base::WeakPtr<RenderWidgetHostViewBase> target,
uint32_t request_id,
const gfx::PointF& target_location,
TracingUmaTracker tracker,
const viz::FrameSinkId& expected_frame_sink_id,
const bool is_verification_request,
const viz::FrameSinkId& frame_sink_id,
const gfx::PointF& transformed_location);
// |event| is in the coordinate space of |root_view|. |target_location|, if
// |target_location|, if
// set, is the location in |target|'s coordinate space. If |latched_target| is
// false, we explicitly did hit-testing for this event, instead of using a
// known target.
void FoundTarget(RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target,
const blink::WebInputEvent& event,
const ui::LatencyInfo& latency,
void FoundTarget(RenderWidgetHostViewBase* target,
const base::Optional<gfx::PointF>& target_location,
bool latched_target,
const viz::FrameSinkId& expected_frame_sink_id);
TargetingRequest* request);
// Callback when the hit testing timer fires, to resume event processing
// without further waiting for a response to the last targeting request.
void AsyncHitTestTimedOut(
base::WeakPtr<RenderWidgetHostViewBase> current_request_root_view,
base::WeakPtr<RenderWidgetHostViewBase> current_request_target,
const gfx::PointF& current_target_location,
base::WeakPtr<RenderWidgetHostViewBase> last_request_target,
const gfx::PointF& last_target_location,
ui::WebScopedInputEvent event,
const ui::LatencyInfo& latency,
const viz::FrameSinkId& expected_frame_sink_id);
const bool is_verification_request);
base::TimeDelta async_hit_test_timeout_delay() {
return async_hit_test_timeout_delay_;
}
struct TargetingRequest {
TargetingRequest();
TargetingRequest(TargetingRequest&& request);
TargetingRequest& operator=(TargetingRequest&& other);
~TargetingRequest();
base::WeakPtr<RenderWidgetHostViewBase> root_view;
ui::WebScopedInputEvent event;
ui::LatencyInfo latency;
viz::FrameSinkId expected_frame_sink_id;
std::unique_ptr<TracingUmaTracker> tracker;
};
bool request_in_flight_ = false;
base::Optional<TargetingRequest> request_in_flight_;
uint32_t last_request_id_ = 0;
std::queue<TargetingRequest> requests_;
// With viz-hit-testing-surface-layer being enabled, we do async hit testing
// for already dispatched events for verification. These verification requests
// should not block normal hit testing requests.
bool verify_request_in_flight_ = false;
base::Optional<TargetingRequest> verify_request_in_flight_;
uint32_t last_verify_request_id_ = 0;
std::queue<TargetingRequest> verify_requests_;
......
......@@ -675,6 +675,7 @@ WebContentsViewAura::WebContentsViewAura(WebContentsImpl* web_contents,
MSG_ROUTING_NONE),
drag_start_process_id_(ChildProcessHost::kInvalidUniqueID),
drag_start_view_id_(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE),
drag_in_progress_(false),
init_rwhv_with_null_parent_for_testing_(false) {}
void WebContentsViewAura::SetDelegateForTesting(
......@@ -1230,25 +1231,23 @@ void WebContentsViewAura::OnMouseEvent(ui::MouseEvent* event) {
////////////////////////////////////////////////////////////////////////////////
// WebContentsViewAura, aura::client::DragDropDelegate implementation:
void WebContentsViewAura::OnDragEntered(const ui::DropTargetEvent& event) {
#if defined(OS_WIN)
async_drop_navigation_observer_.reset();
#endif
gfx::PointF transformed_pt;
void WebContentsViewAura::DragEnteredCallback(
ui::DropTargetEvent event,
std::unique_ptr<DropData> drop_data,
base::WeakPtr<RenderWidgetHostViewBase> target,
base::Optional<gfx::PointF> transformed_pt) {
drag_in_progress_ = true;
if (!target)
return;
RenderWidgetHostImpl* target_rwh =
web_contents_->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
event.location_f(), &transformed_pt);
RenderWidgetHostImpl::From(target->GetRenderWidgetHost());
if (!IsValidDragTarget(target_rwh))
return;
current_rwh_for_drag_ = target_rwh->GetWeakPtr();
current_rvh_for_drag_ =
GetRenderViewHostID(web_contents_->GetRenderViewHost());
current_drop_data_.reset(new DropData());
PrepareDropData(current_drop_data_.get(), event.data());
current_drop_data_.reset(drop_data.release());
current_rwh_for_drag_->FilterDropData(current_drop_data_.get());
blink::WebDragOperationsMask op = ConvertToWeb(event.source_operations());
......@@ -1264,26 +1263,57 @@ void WebContentsViewAura::OnDragEntered(const ui::DropTargetEvent& event) {
if (drag_dest_delegate_)
drag_dest_delegate_->DragInitialize(web_contents_);
DCHECK(transformed_pt.has_value());
gfx::PointF screen_pt(display::Screen::GetScreen()->GetCursorScreenPoint());
current_rwh_for_drag_->DragTargetDragEnter(
*current_drop_data_, transformed_pt, screen_pt, op,
*current_drop_data_, transformed_pt.value(), screen_pt, op,
ui::EventFlagsToWebEventModifiers(event.flags()));
if (drag_dest_delegate_) {
drag_dest_delegate_->OnReceiveDragData(event.data());
drag_dest_delegate_->OnDragEnter();
}
}
int WebContentsViewAura::OnDragUpdated(const ui::DropTargetEvent& event) {
gfx::PointF transformed_pt;
RenderWidgetHostImpl* target_rwh =
web_contents_->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
void WebContentsViewAura::OnDragEntered(const ui::DropTargetEvent& event) {
#if defined(OS_WIN)
async_drop_navigation_observer_.reset();
#endif
std::unique_ptr<DropData> drop_data = std::make_unique<DropData>();
// Calling this here as event.data might become invalid inside the callback.
PrepareDropData(drop_data.get(), event.data());
if (drag_dest_delegate_) {
drag_dest_delegate_->OnReceiveDragData(event.data());
}
web_contents_->GetInputEventRouter()
->GetRenderWidgetHostAtPointAsynchronously(
web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
event.location_f(), &transformed_pt);
event.location_f(),
base::BindOnce(&WebContentsViewAura::DragEnteredCallback,
weak_ptr_factory_.GetWeakPtr(), event,
std::move(drop_data)));
}
void WebContentsViewAura::DragUpdatedCallback(
ui::DropTargetEvent event,
std::unique_ptr<DropData> drop_data,
base::WeakPtr<RenderWidgetHostViewBase> target,
base::Optional<gfx::PointF> transformed_pt) {
// If drag is not in progress it means drag has already finished and we get
// this callback after that already. This happens for example when drag leaves
// out window and we get the exit signal while still waiting for this
// targeting callback to be called for the previous drag update signal. In
// this case we just ignore this operation.
if (!drag_in_progress_)
return;
if (!target)
return;
RenderWidgetHostImpl* target_rwh =
RenderWidgetHostImpl::From(target->GetRenderWidgetHost());
if (!IsValidDragTarget(target_rwh))
return ui::DragDropTypes::DRAG_NONE;
return;
aura::Window* root_window = GetNativeView()->GetRootWindow();
aura::client::ScreenPositionClient* screen_position_client =
......@@ -1305,24 +1335,41 @@ int WebContentsViewAura::OnDragUpdated(const ui::DropTargetEvent& event) {
current_rwh_for_drag_->DragTargetDragLeave(transformed_leave_point,
screen_pt);
}
OnDragEntered(event);
DragEnteredCallback(event, std::move(drop_data), target, transformed_pt);
}
if (!current_drop_data_)
return ui::DragDropTypes::DRAG_NONE;
if (!current_drop_data_) {
return;
}
DCHECK(transformed_pt.has_value());
blink::WebDragOperationsMask op = ConvertToWeb(event.source_operations());
target_rwh->DragTargetDragOver(
transformed_pt, screen_pt, op,
transformed_pt.value(), screen_pt, op,
ui::EventFlagsToWebEventModifiers(event.flags()));
if (drag_dest_delegate_)
drag_dest_delegate_->OnDragOver();
}
int WebContentsViewAura::OnDragUpdated(const ui::DropTargetEvent& event) {
std::unique_ptr<DropData> drop_data = std::make_unique<DropData>();
// Calling this here as event.data might become invalid inside the callback.
PrepareDropData(drop_data.get(), event.data());
web_contents_->GetInputEventRouter()
->GetRenderWidgetHostAtPointAsynchronously(
web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
event.location_f(),
base::BindOnce(&WebContentsViewAura::DragUpdatedCallback,
weak_ptr_factory_.GetWeakPtr(), event,
std::move(drop_data)));
return ConvertFromWeb(current_drag_op_);
}
void WebContentsViewAura::OnDragExited() {
drag_in_progress_ = false;
if (current_rvh_for_drag_ !=
GetRenderViewHostID(web_contents_->GetRenderViewHost()) ||
!current_drop_data_) {
......@@ -1340,32 +1387,40 @@ void WebContentsViewAura::OnDragExited() {
current_drop_data_.reset();
}
int WebContentsViewAura::OnPerformDrop(
const ui::DropTargetEvent& event,
std::unique_ptr<ui::OSExchangeData> data) {
gfx::PointF transformed_pt;
void WebContentsViewAura::PerformDropCallback(
ui::DropTargetEvent event,
std::unique_ptr<ui::OSExchangeData> data,
base::WeakPtr<RenderWidgetHostViewBase> target,
base::Optional<gfx::PointF> transformed_pt) {
drag_in_progress_ = false;
if (!target)
return;
RenderWidgetHostImpl* target_rwh =
web_contents_->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
event.location_f(), &transformed_pt);
RenderWidgetHostImpl::From(target->GetRenderWidgetHost());
if (!IsValidDragTarget(target_rwh))
return ui::DragDropTypes::DRAG_NONE;
return;
DCHECK(transformed_pt.has_value());
gfx::PointF screen_pt(display::Screen::GetScreen()->GetCursorScreenPoint());
if (target_rwh != current_rwh_for_drag_.get()) {
if (current_rwh_for_drag_)
current_rwh_for_drag_->DragTargetDragLeave(transformed_pt, screen_pt);
OnDragEntered(event);
current_rwh_for_drag_->DragTargetDragLeave(transformed_pt.value(),
screen_pt);
std::unique_ptr<DropData> drop_data = std::make_unique<DropData>();
PrepareDropData(drop_data.get(), *data.get());
DragEnteredCallback(event, std::move(drop_data), target, transformed_pt);
}
if (!current_drop_data_)
return ui::DragDropTypes::DRAG_NONE;
if (!current_drop_data_) {
return;
}
const int key_modifiers = ui::EventFlagsToWebEventModifiers(event.flags());
#if defined(OS_WIN)
if (ShouldIncludeVirtualFiles(*current_drop_data_) &&
event.data().HasVirtualFilenames()) {
data->HasVirtualFilenames()) {
// Asynchronously retrieve the actual content of any virtual files now (this
// step is not needed for "real" files already on the file system, e.g.
// those dropped on Chromium from the desktop). When all content has been
......@@ -1379,23 +1434,34 @@ int WebContentsViewAura::OnPerformDrop(
// GetVirtualFilesAsTempFiles will immediately return false if there are no
// virtual files to retrieve (all items are folders e.g.) and no callback
// will be received.
if (event.data().GetVirtualFilesAsTempFiles(std::move(callback))) {
if (data->GetVirtualFilesAsTempFiles(std::move(callback))) {
// Cache the parameters as they were at the time of the drop. This is
// needed for checking that the drop target is still valid when the async
// operation completes.
async_drop_navigation_observer_ =
std::make_unique<AsyncDropNavigationObserver>(
web_contents_, std::move(current_drop_data_), target_rwh,
transformed_pt, screen_pt, key_modifiers);
return ConvertFromWeb(current_drag_op_);
transformed_pt.value(), screen_pt, key_modifiers);
return;
}
}
#endif
CompleteDrop(target_rwh, *current_drop_data_, transformed_pt, screen_pt,
key_modifiers);
CompleteDrop(target_rwh, *current_drop_data_, transformed_pt.value(),
screen_pt, key_modifiers);
current_drop_data_.reset();
}
int WebContentsViewAura::OnPerformDrop(
const ui::DropTargetEvent& event,
std::unique_ptr<ui::OSExchangeData> data) {
web_contents_->GetInputEventRouter()
->GetRenderWidgetHostAtPointAsynchronously(
web_contents_->GetRenderViewHost()->GetWidget()->GetView(),
event.location_f(),
base::BindOnce(&WebContentsViewAura::PerformDropCallback,
weak_ptr_factory_.GetWeakPtr(), event,
std::move(data)));
return ConvertFromWeb(current_drag_op_);
}
......
......@@ -73,6 +73,7 @@ class CONTENT_EXPORT WebContentsViewAura
FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
DragDropVirtualFilesOriginateFromRenderer);
FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropUrlData);
FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropOnOopif);
class WindowObserver;
......@@ -193,6 +194,18 @@ class CONTENT_EXPORT WebContentsViewAura
void OnDragExited() override;
int OnPerformDrop(const ui::DropTargetEvent& event,
std::unique_ptr<ui::OSExchangeData> data) override;
void DragEnteredCallback(ui::DropTargetEvent event,
std::unique_ptr<DropData> drop_data,
base::WeakPtr<RenderWidgetHostViewBase> target,
base::Optional<gfx::PointF> transformed_pt);
void DragUpdatedCallback(ui::DropTargetEvent event,
std::unique_ptr<DropData> drop_data,
base::WeakPtr<RenderWidgetHostViewBase> target,
base::Optional<gfx::PointF> transformed_pt);
void PerformDropCallback(ui::DropTargetEvent event,
std::unique_ptr<ui::OSExchangeData> data,
base::WeakPtr<RenderWidgetHostViewBase> target,
base::Optional<gfx::PointF> transformed_pt);
// Completes a drop operation by communicating the drop data to the renderer
// process.
......@@ -269,13 +282,16 @@ class CONTENT_EXPORT WebContentsViewAura
// Responsible for handling gesture-nav and pull-to-refresh UI.
std::unique_ptr<GestureNavSimple> gesture_nav_simple_;
// This is true when the drag is in process from the perspective of this
// class. It means it gets true when drag enters and gets reset when either
// drop happens or drag exits.
bool drag_in_progress_;
bool init_rwhv_with_null_parent_for_testing_;
#if defined(OS_WIN)
// Used to ensure that the virtual files retrieval callback bound to this
// object is canceled when this object is destroyed.
// Used to ensure the drag and drop callbacks bound to this
// object are canceled when this object is destroyed.
base::WeakPtrFactory<WebContentsViewAura> weak_ptr_factory_{this};
#endif
DISALLOW_COPY_AND_ASSIGN(WebContentsViewAura);
};
......
......@@ -41,9 +41,12 @@
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/dragdrop/drop_target_event.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_sink.h"
......@@ -87,11 +90,28 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
shell()->web_contents());
}
void SetUpOnMainThread() override {
// Setup the server to allow serving separate sites, so we can perform
// cross-process navigation.
host_resolver()->AddRule("*", "127.0.0.1");
}
void SetUpCommandLine(base::CommandLine* cmd) override {
cmd->AppendSwitchASCII(switches::kTouchEventFeatureDetection,
switches::kTouchEventFeatureDetectionEnabled);
}
void OnDropComplete(RenderWidgetHostImpl* target_rwh,
const DropData& drop_data,
const gfx::PointF& client_pt,
const gfx::PointF& screen_pt,
int key_modifiers,
bool drop_allowed) {
// Cache the data for verification.
drop_target_widget_ = target_rwh;
std::move(async_drop_closure_).Run();
}
void TestOverscrollNavigation(bool touch_handler) {
ASSERT_NO_FATAL_FAILURE(StartTestWithPage("/overscroll_navigation.html"));
WebContentsImpl* web_contents =
......@@ -231,6 +251,11 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
ContentBrowserTest::PostRunTestOnMainThread();
}
RenderWidgetHostImpl* drop_target_widget_;
// A closure indicating that async drop operation has completed.
base::OnceClosure async_drop_closure_;
private:
std::unique_ptr<RenderFrameSubmissionObserver> frame_observer_;
......@@ -487,6 +512,73 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
window->AddChild(shell()->web_contents()->GetContentNativeView());
}
IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, DragDropOnOopif) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL(
"a.com", "/overlapping_cross_site_iframe.html");
EXPECT_TRUE(NavigateToURL(shell(), url));
WebContentsImpl* contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
WebContentsViewAura* view =
static_cast<WebContentsViewAura*>(contents->GetView());
// Drop on the root frame.
{
std::unique_ptr<ui::OSExchangeData> data =
std::make_unique<ui::OSExchangeData>();
view->RegisterDropCallbackForTesting(base::BindOnce(
&WebContentsViewAuraTest::OnDropComplete, base::Unretained(this)));
base::RunLoop run_loop;
async_drop_closure_ = run_loop.QuitClosure();
gfx::PointF point = {10, 10};
ui::DropTargetEvent event(*data.get(), point, point,
ui::DragDropTypes::DRAG_COPY);
view->OnDragEntered(event);
view->OnPerformDrop(event, std::move(data));
run_loop.Run();
EXPECT_EQ(drop_target_widget_,
RenderWidgetHostImpl::From(contents->GetFrameTree()
->root()
->current_frame_host()
->GetRenderWidgetHost()));
}
// Drop on the element in the root frame overlapping the embedded OOPIF.
{
std::unique_ptr<ui::OSExchangeData> data =
std::make_unique<ui::OSExchangeData>();
view->RegisterDropCallbackForTesting(base::BindOnce(
&WebContentsViewAuraTest::OnDropComplete, base::Unretained(this)));
base::RunLoop run_loop;
async_drop_closure_ = run_loop.QuitClosure();
int left =
EvalJs(contents,
"document.getElementById('target').getBoundingClientRect().left")
.ExtractInt();
int top =
EvalJs(contents,
"document.getElementById('target').getBoundingClientRect().top")
.ExtractInt();
gfx::PointF point = {left + 5, top + 5};
ui::DropTargetEvent event(*data.get(), point, point,
ui::DragDropTypes::DRAG_COPY);
view->OnDragEntered(event);
view->OnPerformDrop(event, std::move(data));
run_loop.Run();
EXPECT_EQ(drop_target_widget_,
RenderWidgetHostImpl::From(contents->GetFrameTree()
->root()
->current_frame_host()
->GetRenderWidgetHost()));
}
}
IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, ContentWindowClose) {
ASSERT_NO_FATAL_FAILURE(StartTestWithPage("/overscroll_navigation.html"));
......
<html>
<title>Overlapping cross site iframe</title>
This is the main body content.
<iframe id="iframe" style="width:300px; height:300px; position:absolute; left:100px; top:100px"></iframe>
<div id="target" style="background-color: yellow; position: absolute; top: 120px; left: 120px; width: 100px; height: 100px"></div>
<script>
var url = window.location.protocol + '//b.com';
if (window.location.port)
url += ':' + window.location.port;
url += "/cross_site_iframe_factory.html?b()";
document.getElementById('iframe').src = url;
</script>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment