Enforce user gesture requirement on browser side for WebUI.
WebContentsImpl now tracks the last time it received an input event that
could be considered user interaction. When the browser process receives
a WebUI message that requires a user gesture, it checks the WebContents
hosting the WebUI to make sure that the user recently interacted with
it.
This also cleans up a few incidental bits of code:
- RenderWidgetHost no longer prefilters events before notifying the
delegate. This exposed some broken event filtering, tracked at
https://crbug.com/827659.
- Since the delegate method no longer prefilters input events,
RenderWidgetHostDelegate::OnUserInteraction() is now named
RenderWidgetHostDelegate::DidReceiveInputEvent().
- The IPC message used to pass WebUI messages from the renderer to the
browser sends the source URL. However, this requires trusting the
untrustworthy renderer to not lie about the URL. Instead, just use
GetLastCommittedURL().
Bug: 823864
Change-Id: I687776c8c7f7f93f78c968dd432b1674d65629c0
Reviewed-on: https://chromium-review.googlesource.com/1023174
Commit-Queue: Daniel Cheng <[email protected]>
Reviewed-by: Alex Moshchuk <[email protected]>
Reviewed-by: Lei Zhang <[email protected]>
Reviewed-by: Ilya Sherman <[email protected]>
Cr-Commit-Position: refs/heads/master@{#553001}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index fbd264ad..7085f4e 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -141,6 +141,7 @@
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/accessibility/ax_tree_combiner.h"
#include "ui/base/layout.h"
+#include "ui/events/base_event_utils.h"
#include "ui/events/blink/web_input_event_traits.h"
#include "ui/gl/gl_switches.h"
@@ -1532,6 +1533,20 @@
SetVisibility(Visibility::HIDDEN);
}
+bool WebContentsImpl::HasRecentInteractiveInputEvent() const {
+ static constexpr base::TimeDelta kMaxInterval =
+ base::TimeDelta::FromSeconds(5);
+ base::TimeDelta delta =
+ ui::EventTimeForNow() - last_interactive_input_event_time_;
+ // Note: the expectation is that the caller is typically expecting an input
+ // event, e.g. validating that a WebUI message that requires a gesture is
+ // actually attached to a gesture. Logging to UMA here should hopefully give
+ // sufficient data if 5 seconds is actually sufficient (or even too high a
+ // threshhold).
+ UMA_HISTOGRAM_TIMES("Tabs.TimeSinceLastInteraction", delta);
+ return delta <= kMaxInterval;
+}
+
#if defined(OS_ANDROID)
std::set<RenderWidgetHostImpl*> WebContentsImpl::GetAllRenderWidgetHosts() {
std::set<RenderWidgetHostImpl*> set;
@@ -3535,9 +3550,8 @@
browser_plugin_embedder_->SystemDragEnded();
}
-void WebContentsImpl::UserGestureDone() {
- OnUserInteraction(GetRenderViewHost()->GetWidget(),
- blink::WebInputEvent::kUndefined);
+void WebContentsImpl::NavigatedByUser() {
+ OnUserInteraction(blink::WebInputEvent::kUndefined);
}
void WebContentsImpl::SetClosedByUserGesture(bool value) {
@@ -5581,21 +5595,25 @@
source_id);
}
-void WebContentsImpl::OnUserInteraction(
+void WebContentsImpl::DidReceiveInputEvent(
RenderWidgetHostImpl* render_widget_host,
const blink::WebInputEvent::Type type) {
+ // Ideally, this list would be based more off of
+ // https://whatwg.org/C/interaction.html#triggered-by-user-activation.
+ if (type != blink::WebInputEvent::kMouseDown &&
+ type != blink::WebInputEvent::kGestureScrollBegin &&
+ type != blink::WebInputEvent::kTouchStart &&
+ type != blink::WebInputEvent::kRawKeyDown)
+ return;
+
// Ignore unless the widget is currently in the frame tree.
if (!HasMatchingWidgetHost(&frame_tree_, render_widget_host))
return;
- for (auto& observer : observers_)
- observer.DidGetUserInteraction(type);
+ if (type != blink::WebInputEvent::kGestureScrollBegin)
+ last_interactive_input_event_time_ = ui::EventTimeForNow();
- ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get();
- // Exclude scroll events as user gestures for resource load dispatches.
- // rdh is NULL in unittests.
- if (rdh && type != blink::WebInputEvent::kMouseWheel)
- rdh->OnUserGesture();
+ OnUserInteraction(type);
}
void WebContentsImpl::FocusOwningWebContents(
@@ -5982,6 +6000,20 @@
delegate_->UpdatePreferredSize(this, new_size);
}
+void WebContentsImpl::OnUserInteraction(const blink::WebInputEvent::Type type) {
+ for (auto& observer : observers_)
+ observer.DidGetUserInteraction(type);
+
+ // TODO(https://crbug.com/827659): This used to check if type != kMouseWheel.
+ // However, due to the caller already filtering event types, this would never
+ // be called with type == kMouseWheel so checking for that here is pointless.
+ // However, mouse wheel events *also* generate a kGestureScrollBegin event...
+ // which is *not* filtered out. Maybe they should be?
+ ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get();
+ if (rdh) // null in unittests. =(
+ rdh->OnUserGesture();
+}
+
std::unique_ptr<WebUIImpl> WebContentsImpl::CreateWebUI(const GURL& url) {
std::unique_ptr<WebUIImpl> web_ui = std::make_unique<WebUIImpl>(this);
WebUIController* controller =