Move persistent gesture state to Document, add DocumentUserGestureToken

Non-test gesture tokens are now created as a DocumentUserGestureToken, which take a Document* (which can be null, but such uses are limited). When it is created, it sets the m_hasReceivedUserGesture flag on Document.

Remove UserGestureIndicator::s_processedUserGestureSinceLoad, as Document::m_hasReceivedUserGesture is very similar and not process-wide.

BUG=624061

Review-Url: https://codereview.chromium.org/2408333004
Cr-Commit-Position: refs/heads/master@{#427481}
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 3a26bb5..1af7506 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1976,7 +1976,7 @@
   // A bunch of tests expect to run code in the context of a user gesture, which
   // can grant additional privileges (e.g. the ability to create popups).
   std::unique_ptr<blink::WebScopedUserGesture> gesture(
-      has_user_gesture ? new blink::WebScopedUserGesture : nullptr);
+      has_user_gesture ? new blink::WebScopedUserGesture(frame_) : nullptr);
   v8::HandleScope handle_scope(blink::mainThreadIsolate());
   v8::Local<v8::Value> result =
       frame_->executeScriptAndReturnValue(WebScriptSource(jscript));
@@ -2310,7 +2310,7 @@
     return false;
 
   int32_t message_length = static_cast<int32_t>(message.length());
-  if (WebUserGestureIndicator::processedUserGestureSinceLoad()) {
+  if (WebUserGestureIndicator::processedUserGestureSinceLoad(frame_)) {
     UMA_HISTOGRAM_COUNTS("JSDialogs.CharacterCount.UserGestureSinceLoad",
                          message_length);
   } else {
@@ -5016,8 +5016,8 @@
   // If the request was initiated in the context of a user gesture then make
   // sure that the navigation also executes in the context of a user gesture.
   std::unique_ptr<blink::WebScopedUserGesture> gesture(
-      request_params.has_user_gesture ? new blink::WebScopedUserGesture
-          : nullptr);
+      request_params.has_user_gesture ? new blink::WebScopedUserGesture(frame_)
+                                      : nullptr);
 
   NavigateInternal(common_params, StartNavigationParams(), request_params,
                    std::move(stream_override));
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 72df560..6268f68 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -643,8 +643,11 @@
                                           const base::ListValue& args,
                                           bool user_gesture) {
   std::unique_ptr<WebScopedUserGesture> web_user_gesture;
-  if (user_gesture)
-    web_user_gesture.reset(new WebScopedUserGesture);
+  if (user_gesture) {
+    blink::WebLocalFrame* web_frame =
+        render_frame ? render_frame->GetWebFrame() : nullptr;
+    web_user_gesture.reset(new WebScopedUserGesture(web_frame));
+  }
 
   script_context_set_->ForEach(
       extension_id, render_frame,
diff --git a/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc b/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
index 060573ee..021636e1 100644
--- a/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
+++ b/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
@@ -427,7 +427,7 @@
 void GuestViewInternalCustomBindings::RunWithGesture(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   // Gesture is required to request fullscreen.
-  blink::WebScopedUserGesture user_gesture;
+  blink::WebScopedUserGesture user_gesture(context()->web_frame());
   CHECK_EQ(args.Length(), 1);
   CHECK(args[0]->IsFunction());
   v8::Local<v8::Value> no_args;
diff --git a/extensions/renderer/messaging_bindings.cc b/extensions/renderer/messaging_bindings.cc
index b9f20ff..5462e373 100644
--- a/extensions/renderer/messaging_bindings.cc
+++ b/extensions/renderer/messaging_bindings.cc
@@ -203,7 +203,8 @@
   std::unique_ptr<blink::WebScopedWindowFocusAllowedIndicator>
       allow_window_focus;
   if (message.user_gesture) {
-    web_user_gesture.reset(new blink::WebScopedUserGesture);
+    web_user_gesture.reset(
+        new blink::WebScopedUserGesture(script_context->web_frame()));
 
     if (script_context->web_frame()) {
       blink::WebDocument document = script_context->web_frame()->document();
diff --git a/extensions/renderer/user_gestures_native_handler.cc b/extensions/renderer/user_gestures_native_handler.cc
index c01a4ae..6d31094 100644
--- a/extensions/renderer/user_gestures_native_handler.cc
+++ b/extensions/renderer/user_gestures_native_handler.cc
@@ -36,7 +36,7 @@
 
 void UserGesturesNativeHandler::RunWithUserGesture(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
-  blink::WebScopedUserGesture user_gesture;
+  blink::WebScopedUserGesture user_gesture(context()->web_frame());
   CHECK_EQ(args.Length(), 1);
   CHECK(args[0]->IsFunction());
   v8::Local<v8::Value> no_args;
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 12d05783..70fc1bc4 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1045,6 +1045,7 @@
     "dom/DOMMatrixTest.cpp",
     "dom/DocumentStatisticsCollectorTest.cpp",
     "dom/DocumentTest.cpp",
+    "dom/DocumentUserGestureTokenTest.cpp",
     "dom/ElementTest.cpp",
     "dom/ElementVisibilityObserverTest.cpp",
     "dom/ExecutionContextTaskTest.cpp",
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 3ee106f..3e7176e3 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -457,6 +457,7 @@
       m_hasXMLDeclaration(0),
       m_designMode(false),
       m_isRunningExecCommand(false),
+      m_hasReceivedUserGesture(false),
       m_hasAnnotatedRegions(false),
       m_annotatedRegionsDirty(false),
       m_useSecureKeyboardEntryWhenActive(false),
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index 40ec07d3..b0ecc5f 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -1314,6 +1314,14 @@
 
   PropertyRegistry* propertyRegistry();
 
+  // Indicates whether the user has interacted with this particular Document.
+  void setHasReceivedUserGesture() { m_hasReceivedUserGesture = true; }
+  // Consume user interaction state. Used for an experiment that restricts
+  // adding history entries to Documents that have had a user gesture since
+  // last history state change.
+  void clearHasReceivedUserGesture() { m_hasReceivedUserGesture = false; }
+  bool hasReceivedUserGesture() const { return m_hasReceivedUserGesture; }
+
  protected:
   Document(const DocumentInit&, DocumentClassFlags = DefaultDocumentClass);
 
@@ -1558,6 +1566,7 @@
 
   bool m_designMode;
   bool m_isRunningExecCommand;
+  bool m_hasReceivedUserGesture;
 
   HeapHashSet<WeakMember<const LiveNodeListBase>> m_listsInvalidatedAtDocument;
   // Oilpan keeps track of all registered NodeLists.
diff --git a/third_party/WebKit/Source/core/dom/DocumentUserGestureToken.h b/third_party/WebKit/Source/core/dom/DocumentUserGestureToken.h
new file mode 100644
index 0000000..fda12fb
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/DocumentUserGestureToken.h
@@ -0,0 +1,45 @@
+// Copyright 2016 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.
+
+#ifndef DocumentUserGestureToken_h
+#define DocumentUserGestureToken_h
+
+#include "core/dom/Document.h"
+#include "core/frame/LocalFrame.h"
+#include "platform/UserGestureIndicator.h"
+
+namespace blink {
+
+// Associates a UserGestureToken with a Document, if a non-null Document*
+// is provided in the constructor.
+class DocumentUserGestureToken final : public UserGestureToken {
+  WTF_MAKE_NONCOPYABLE(DocumentUserGestureToken);
+
+ public:
+  static PassRefPtr<UserGestureToken> create(
+      Document* document,
+      Status status = PossiblyExistingGesture) {
+    return adoptRef(new DocumentUserGestureToken(document, status));
+  }
+
+  ~DocumentUserGestureToken() final {}
+
+ private:
+  DocumentUserGestureToken(Document* document, Status status)
+      : UserGestureToken(status) {
+    if (!document)
+      return;
+    document->setHasReceivedUserGesture();
+    for (Frame* frame = document->frame()->tree().parent(); frame;
+         frame = frame->tree().parent()) {
+      // TODO(japhet): Make this work for RemoteFrames: http://crbug.com/658800
+      if (frame->isLocalFrame())
+        toLocalFrame(frame)->document()->setHasReceivedUserGesture();
+    }
+  }
+};
+
+}  // namespace blink
+
+#endif  // DocumentUserGestureToken_h
diff --git a/third_party/WebKit/Source/core/dom/DocumentUserGestureTokenTest.cpp b/third_party/WebKit/Source/core/dom/DocumentUserGestureTokenTest.cpp
new file mode 100644
index 0000000..5df9cf4
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/DocumentUserGestureTokenTest.cpp
@@ -0,0 +1,36 @@
+// Copyright 2016 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/dom/DocumentUserGestureToken.h"
+
+#include "core/testing/DummyPageHolder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(DocumentUserGestureTokenTest, DocumentUserGestureState) {
+  std::unique_ptr<DummyPageHolder> dummyPageHolder =
+      DummyPageHolder::create(IntSize(800, 600));
+  Document& document = dummyPageHolder->document();
+  ASSERT_FALSE(document.hasReceivedUserGesture());
+
+  // A nullptr Document* will not set user gesture state.
+  DocumentUserGestureToken::create(nullptr);
+  EXPECT_FALSE(document.hasReceivedUserGesture());
+
+  // A non-null Document* will set state, but a subsequent nullptr Document*
+  // token will not override it.
+  DocumentUserGestureToken::create(&document);
+  EXPECT_TRUE(document.hasReceivedUserGesture());
+  DocumentUserGestureToken::create(nullptr);
+  EXPECT_TRUE(document.hasReceivedUserGesture());
+  document.clearHasReceivedUserGesture();
+  ASSERT_FALSE(document.hasReceivedUserGesture());
+
+  // UserGestureToken::Status doesn't impact Document gesture state.
+  DocumentUserGestureToken::create(&document, UserGestureToken::NewGesture);
+  EXPECT_TRUE(document.hasReceivedUserGesture());
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 2cf59106..917bb05 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -138,7 +138,6 @@
 #include "core/svg/SVGElement.h"
 #include "platform/EventDispatchForbiddenScope.h"
 #include "platform/RuntimeEnabledFeatures.h"
-#include "platform/UserGestureIndicator.h"
 #include "platform/graphics/CompositorMutableProperties.h"
 #include "platform/graphics/CompositorMutation.h"
 #include "platform/scroll/ScrollableArea.h"
@@ -2607,7 +2606,7 @@
     return;
 
   if (document().focusedElement() == this &&
-      UserGestureIndicator::processedUserGestureSinceLoad()) {
+      document().hasReceivedUserGesture()) {
     // Bring up the keyboard in the context of anything triggered by a user
     // gesture. Since tracking that across arbitrary boundaries (eg.
     // animations) is difficult, for now we match IE's heuristic and bring
diff --git a/third_party/WebKit/Source/core/html/AutoplayExperimentTest.cpp b/third_party/WebKit/Source/core/html/AutoplayExperimentTest.cpp
index b3fa768..8c42fa19 100644
--- a/third_party/WebKit/Source/core/html/AutoplayExperimentTest.cpp
+++ b/third_party/WebKit/Source/core/html/AutoplayExperimentTest.cpp
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/html/AutoplayExperimentHelper.h"
 #include "platform/UserGestureIndicator.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -134,7 +135,7 @@
   }
 
   void startPlaybackWithUserGesture() {
-    UserGestureIndicator indicator(UserGestureToken::create());
+    UserGestureIndicator indicator(DocumentUserGestureToken::create(nullptr));
     EXPECT_TRUE(UserGestureIndicator::processingUserGesture());
     startPlayback();
   }
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElementTest.cpp b/third_party/WebKit/Source/core/html/HTMLVideoElementTest.cpp
index 3122fe0..8d91cf19 100644
--- a/third_party/WebKit/Source/core/html/HTMLVideoElementTest.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLVideoElementTest.cpp
@@ -5,6 +5,7 @@
 #include "core/html/HTMLVideoElement.h"
 
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/loader/EmptyClients.h"
 #include "core/page/NetworkStateNotifier.h"
 #include "core/testing/DummyPageHolder.h"
@@ -139,7 +140,8 @@
   EXPECT_CALL(*player, setBufferingStrategy(
                            WebMediaPlayer::BufferingStrategy::Aggressive));
   {
-    UserGestureIndicator gesture(UserGestureToken::create());
+    UserGestureIndicator gesture(
+        DocumentUserGestureToken::create(&m_video->document()));
     m_video->pause();
   }
   ::testing::Mock::VerifyAndClearExpectations(player);
diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp
index 75d6b41..a40a5bc0 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandler.cpp
@@ -34,6 +34,7 @@
 #include "core/clipboard/DataTransfer.h"
 #include "core/dom/DOMNodeIds.h"
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/TouchList.h"
 #include "core/dom/shadow/FlatTreeTraversal.h"
 #include "core/dom/shadow/ShadowRoot.h"
@@ -572,10 +573,6 @@
       WebPointerProperties::Button::NoButton)
     return WebInputEventResult::HandledSuppressed;
 
-  UserGestureIndicator gestureIndicator(UserGestureToken::create());
-  m_frame->localFrameRoot()->eventHandler().m_lastMouseDownUserGestureToken =
-      UserGestureIndicator::currentToken();
-
   if (m_eventHandlerWillResetCapturingMouseEventsNode)
     m_capturingMouseEventsNode = nullptr;
   m_mouseEventManager->handleMousePressEventUpdateStates(mouseEvent);
@@ -619,6 +616,11 @@
     return result;
   }
 
+  UserGestureIndicator gestureIndicator(
+      DocumentUserGestureToken::create(m_frame->document()));
+  m_frame->localFrameRoot()->eventHandler().m_lastMouseDownUserGestureToken =
+      UserGestureIndicator::currentToken();
+
   if (RuntimeEnabledFeatures::middleClickAutoscrollEnabled()) {
     // We store whether middle click autoscroll is in progress before calling
     // stopAutoscroll() because it will set m_autoscrollType to NoAutoscroll on
@@ -897,20 +899,6 @@
   if (!mouseEvent.fromTouch())
     m_frame->selection().setCaretBlinkingSuspended(false);
 
-  std::unique_ptr<UserGestureIndicator> gestureIndicator;
-
-  if (m_frame->localFrameRoot()
-          ->eventHandler()
-          .m_lastMouseDownUserGestureToken) {
-    gestureIndicator = wrapUnique(new UserGestureIndicator(
-        m_frame->localFrameRoot()
-            ->eventHandler()
-            .m_lastMouseDownUserGestureToken.release()));
-  } else {
-    gestureIndicator =
-        wrapUnique(new UserGestureIndicator(UserGestureToken::create()));
-  }
-
   if (RuntimeEnabledFeatures::middleClickAutoscrollEnabled()) {
     if (Page* page = m_frame->page())
       page->autoscrollController().handleMouseReleaseForMiddleClickAutoscroll(
@@ -951,6 +939,24 @@
   if (subframe)
     return passMouseReleaseEventToSubframe(mev, subframe);
 
+  // Mouse events will be associated with the Document where mousedown
+  // occurred. If, e.g., there is a mousedown, then a drag to a different
+  // Document and mouseup there, the mouseup's gesture will be associated with
+  // the mousedown's Document. It's not absolutely certain that this is the
+  // correct behavior.
+  std::unique_ptr<UserGestureIndicator> gestureIndicator;
+  if (m_frame->localFrameRoot()
+          ->eventHandler()
+          .m_lastMouseDownUserGestureToken) {
+    gestureIndicator = wrapUnique(new UserGestureIndicator(
+        m_frame->localFrameRoot()
+            ->eventHandler()
+            .m_lastMouseDownUserGestureToken.release()));
+  } else {
+    gestureIndicator = wrapUnique(new UserGestureIndicator(
+        DocumentUserGestureToken::create(m_frame->document())));
+  }
+
   WebInputEventResult eventResult = updatePointerTargetAndDispatchEvents(
       EventTypeNames::mouseup, mev.innerNode(), mev.event());
 
diff --git a/third_party/WebKit/Source/core/input/GestureManager.cpp b/third_party/WebKit/Source/core/input/GestureManager.cpp
index 4171e89c..dddf939 100644
--- a/third_party/WebKit/Source/core/input/GestureManager.cpp
+++ b/third_party/WebKit/Source/core/input/GestureManager.cpp
@@ -5,6 +5,7 @@
 #include "core/input/GestureManager.h"
 
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/editing/SelectionController.h"
 #include "core/events/GestureEvent.h"
 #include "core/frame/FrameHost.h"
@@ -139,8 +140,6 @@
   uint64_t preDispatchDomTreeVersion = m_frame->document()->domTreeVersion();
   uint64_t preDispatchStyleVersion = m_frame->document()->styleVersion();
 
-  UserGestureIndicator gestureIndicator(UserGestureToken::create());
-
   HitTestResult currentHitTest = targetedEvent.hitTestResult();
 
   // We use the adjusted position so the application isn't surprised to see a
@@ -183,6 +182,8 @@
   Node* tappedNode = currentHitTest.innerNode();
   IntPoint tappedPosition = gestureEvent.position();
   Node* tappedNonTextNode = tappedNode;
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      tappedNode ? &tappedNode->document() : nullptr));
 
   if (tappedNonTextNode && tappedNonTextNode->isTextNode())
     tappedNonTextNode = FlatTreeTraversal::parent(*tappedNonTextNode);
diff --git a/third_party/WebKit/Source/core/input/KeyboardEventManager.cpp b/third_party/WebKit/Source/core/input/KeyboardEventManager.cpp
index eaa25b9..c1c00a7c 100644
--- a/third_party/WebKit/Source/core/input/KeyboardEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/KeyboardEventManager.cpp
@@ -4,6 +4,7 @@
 
 #include "KeyboardEventManager.h"
 
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/Element.h"
 #include "core/editing/Editor.h"
 #include "core/events/KeyboardEvent.h"
@@ -104,7 +105,8 @@
   if (!node)
     return WebInputEventResult::NotHandled;
 
-  UserGestureIndicator gestureIndicator(UserGestureToken::create());
+  UserGestureIndicator gestureIndicator(
+      DocumentUserGestureToken::create(m_frame->document()));
 
   // In IE, access keys are special, they are handled after default keydown
   // processing, but cannot be canceled - this is hard to match.  On Mac OS X,
diff --git a/third_party/WebKit/Source/core/input/PointerEventManager.cpp b/third_party/WebKit/Source/core/input/PointerEventManager.cpp
index 7dfc2be..f3b1dd0d 100644
--- a/third_party/WebKit/Source/core/input/PointerEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/PointerEventManager.cpp
@@ -4,6 +4,7 @@
 
 #include "core/input/PointerEventManager.h"
 
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/ElementTraversal.h"
 #include "core/dom/shadow/FlatTreeTraversal.h"
 #include "core/events/MouseEvent.h"
@@ -172,7 +173,8 @@
     if (eventType == EventTypeNames::pointerup &&
         pointerEvent->pointerType() == "touch") {
       gestureIndicator =
-          wrapUnique(new UserGestureIndicator(UserGestureToken::create()));
+          wrapUnique(new UserGestureIndicator(DocumentUserGestureToken::create(
+              target->toNode() ? &target->toNode()->document() : nullptr)));
     }
 
     DispatchEventResult dispatchResult = target->dispatchEvent(pointerEvent);
diff --git a/third_party/WebKit/Source/core/input/TouchEventManager.cpp b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
index 2bc292f..8cbaab5 100644
--- a/third_party/WebKit/Source/core/input/TouchEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
@@ -5,6 +5,7 @@
 #include "core/input/TouchEventManager.h"
 
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/events/TouchEvent.h"
 #include "core/frame/Deprecation.h"
 #include "core/frame/EventHandlerRegistry.h"
@@ -524,10 +525,10 @@
 
   std::unique_ptr<UserGestureIndicator> gestureIndicator;
   if (isTap || isSameOrigin) {
-    gestureIndicator = wrapUnique(
-        new UserGestureIndicator(m_touchSequenceUserGestureToken
-                                     ? m_touchSequenceUserGestureToken.release()
-                                     : UserGestureToken::create()));
+    gestureIndicator = wrapUnique(new UserGestureIndicator(
+        m_touchSequenceUserGestureToken
+            ? m_touchSequenceUserGestureToken.release()
+            : DocumentUserGestureToken::create(m_touchSequenceDocument)));
 
     m_touchSequenceUserGestureToken = UserGestureIndicator::currentToken();
     // These are cases we'd like to migrate to not hold a user gesture.
diff --git a/third_party/WebKit/Source/core/inspector/DevToolsHost.cpp b/third_party/WebKit/Source/core/inspector/DevToolsHost.cpp
index c4a73f0..8d94374 100644
--- a/third_party/WebKit/Source/core/inspector/DevToolsHost.cpp
+++ b/third_party/WebKit/Source/core/inspector/DevToolsHost.cpp
@@ -32,6 +32,7 @@
 #include "bindings/core/v8/ScriptState.h"
 #include "bindings/core/v8/V8ScriptRunner.h"
 #include "core/clipboard/Pasteboard.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/ExecutionContext.h"
 #include "core/events/Event.h"
 #include "core/events/EventTarget.h"
@@ -132,7 +133,8 @@
   if (!scriptState)
     return;
   ScriptState::Scope scope(scriptState);
-  UserGestureIndicator gestureIndicator(UserGestureToken::create());
+  UserGestureIndicator gestureIndicator(
+      DocumentUserGestureToken::create(m_frontendFrame->document()));
   v8::MicrotasksScope microtasks(scriptState->isolate(),
                                  v8::MicrotasksScope::kRunMicrotasks);
   v8::Local<v8::String> source =
diff --git a/third_party/WebKit/Source/core/inspector/ThreadDebugger.cpp b/third_party/WebKit/Source/core/inspector/ThreadDebugger.cpp
index cefc8d3..f3e1edfd 100644
--- a/third_party/WebKit/Source/core/inspector/ThreadDebugger.cpp
+++ b/third_party/WebKit/Source/core/inspector/ThreadDebugger.cpp
@@ -17,6 +17,7 @@
 #include "bindings/core/v8/V8Node.h"
 #include "bindings/core/v8/V8NodeList.h"
 #include "bindings/core/v8/V8ScriptRunner.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/inspector/ConsoleMessage.h"
 #include "core/inspector/InspectorDOMDebuggerAgent.h"
 #include "core/inspector/InspectorTraceEvents.h"
@@ -136,8 +137,8 @@
 }
 
 void ThreadDebugger::beginUserGesture() {
-  m_userGestureIndicator =
-      wrapUnique(new UserGestureIndicator(UserGestureToken::create()));
+  m_userGestureIndicator = wrapUnique(
+      new UserGestureIndicator(DocumentUserGestureToken::create(nullptr)));
 }
 
 void ThreadDebugger::endUserGesture() {
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index 9b34e0f..21ebb27 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -393,10 +393,6 @@
     FrameLoadType loadType,
     HistoryCommitType historyCommitType,
     HistoryNavigationType navigationType) {
-  if (m_frame->settings()->historyEntryRequiresUserGesture() &&
-      historyCommitType == StandardCommit)
-    UserGestureIndicator::clearProcessedUserGestureSinceLoad();
-
   HistoryItem* oldItem = m_currentItem;
   if (isBackForwardLoadType(loadType) && m_provisionalItem)
     m_currentItem = m_provisionalItem.release();
@@ -789,10 +785,12 @@
   HistoryCommitType historyCommitType = loadTypeToCommitType(type);
   if (!m_currentItem)
     historyCommitType = HistoryInertCommit;
-  if (m_frame->settings()->historyEntryRequiresUserGesture() &&
-      !UserGestureIndicator::processedUserGestureSinceLoad() &&
-      initiatingDocument)
-    historyCommitType = HistoryInertCommit;
+  if (m_frame->settings()->historyEntryRequiresUserGesture()) {
+    if (initiatingDocument && !initiatingDocument->hasReceivedUserGesture())
+      historyCommitType = HistoryInertCommit;
+    else if (historyCommitType == StandardCommit)
+      m_frame->document()->clearHasReceivedUserGesture();
+  }
 
   setHistoryItemStateForCommit(
       type, historyCommitType,
@@ -937,8 +935,8 @@
     return FrameLoadTypeReload;
 
   if (m_frame->settings()->historyEntryRequiresUserGesture() &&
-      !UserGestureIndicator::processedUserGestureSinceLoad() &&
-      request.originDocument())
+      request.originDocument() &&
+      !request.originDocument()->hasReceivedUserGesture())
     return FrameLoadTypeReplaceCurrentItem;
 
   return FrameLoadTypeStandard;
diff --git a/third_party/WebKit/Source/core/page/DragController.cpp b/third_party/WebKit/Source/core/page/DragController.cpp
index 8dd8b7d8..520d524 100644
--- a/third_party/WebKit/Source/core/page/DragController.cpp
+++ b/third_party/WebKit/Source/core/page/DragController.cpp
@@ -34,6 +34,7 @@
 #include "core/clipboard/DataTransferAccessPolicy.h"
 #include "core/dom/Document.h"
 #include "core/dom/DocumentFragment.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/Element.h"
 #include "core/dom/Node.h"
 #include "core/dom/Text.h"
@@ -243,6 +244,8 @@
   DCHECK(dragData);
   m_documentUnderMouse = m_page->deprecatedLocalMainFrame()->documentAtPoint(
       dragData->clientPosition());
+  UserGestureIndicator gesture(DocumentUserGestureToken::create(
+      m_documentUnderMouse, UserGestureToken::NewGesture));
   if ((m_dragDestinationAction & DragDestinationActionDHTML) &&
       m_documentIsHandlingDrag) {
     LocalFrame* mainFrame = m_page->deprecatedLocalMainFrame();
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index 37ab917..2f9b0c23 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -463,7 +463,6 @@
     frameHost().visualViewport().setScrollOffset(ScrollOffset(),
                                                  ProgrammaticScroll);
     m_hostsUsingFeatures.updateMeasurementsAndClear();
-    UserGestureIndicator::clearProcessedUserGestureSinceLoad();
   }
 }
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
index 41d7905..230d730 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
@@ -29,6 +29,7 @@
 #include "modules/accessibility/AXNodeObject.h"
 
 #include "core/InputTypeNames.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/Element.h"
 #include "core/dom/NodeTraversal.h"
 #include "core/dom/Text.h"
@@ -2154,14 +2155,14 @@
 }
 
 void AXNodeObject::increment() {
-  UserGestureIndicator gestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      getDocument(), UserGestureToken::NewGesture));
   alterSliderValue(true);
 }
 
 void AXNodeObject::decrement() {
-  UserGestureIndicator gestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      getDocument(), UserGestureToken::NewGesture));
   alterSliderValue(false);
 }
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
index 9ef76f4d..5ab82692 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
@@ -30,6 +30,7 @@
 
 #include "SkMatrix44.h"
 #include "core/css/resolver/StyleResolver.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/editing/EditingUtilities.h"
 #include "core/editing/VisibleUnits.h"
 #include "core/frame/FrameView.h"
@@ -1347,8 +1348,8 @@
   Element* actionElem = actionElement();
   if (!actionElem)
     return false;
-  UserGestureIndicator gestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      &actionElem->document(), UserGestureToken::NewGesture));
   actionElem->accessKeyAction(true);
   return true;
 }
diff --git a/third_party/WebKit/Source/modules/notifications/Notification.cpp b/third_party/WebKit/Source/modules/notifications/Notification.cpp
index af9ad5b..980b5ac 100644
--- a/third_party/WebKit/Source/modules/notifications/Notification.cpp
+++ b/third_party/WebKit/Source/modules/notifications/Notification.cpp
@@ -35,6 +35,7 @@
 #include "bindings/core/v8/SerializedScriptValueFactory.h"
 #include "bindings/modules/v8/V8NotificationAction.h"
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/ExecutionContext.h"
 #include "core/dom/ExecutionContextTask.h"
 #include "core/dom/ScopedWindowFocusAllowedIndicator.h"
@@ -208,8 +209,10 @@
 }
 
 void Notification::dispatchClickEvent() {
-  UserGestureIndicator gestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  ExecutionContext* context = getExecutionContext();
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      context->isDocument() ? toDocument(context) : nullptr,
+      UserGestureToken::NewGesture));
   ScopedWindowFocusAllowedIndicator windowFocusAllowed(getExecutionContext());
   dispatchEvent(Event::create(EventTypeNames::click));
 }
diff --git a/third_party/WebKit/Source/modules/remoteplayback/RemotePlaybackTest.cpp b/third_party/WebKit/Source/modules/remoteplayback/RemotePlaybackTest.cpp
index 5a7bb3b..cbcd481 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/RemotePlaybackTest.cpp
+++ b/third_party/WebKit/Source/modules/remoteplayback/RemotePlaybackTest.cpp
@@ -7,6 +7,7 @@
 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
 #include "bindings/core/v8/V8BindingForTesting.h"
 #include "bindings/modules/v8/RemotePlaybackAvailabilityCallback.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/html/HTMLMediaElement.h"
 #include "core/html/HTMLVideoElement.h"
 #include "core/testing/DummyPageHolder.h"
@@ -75,8 +76,8 @@
   EXPECT_CALL(*resolve, call(::testing::_)).Times(0);
   EXPECT_CALL(*reject, call(::testing::_)).Times(1);
 
-  UserGestureIndicator indicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator indicator(DocumentUserGestureToken::create(
+      &pageHolder->document(), UserGestureToken::NewGesture));
   remotePlayback->prompt().then(resolve->bind(), reject->bind());
   cancelPrompt(remotePlayback);
 }
@@ -131,8 +132,8 @@
   EXPECT_CALL(*resolve, call(::testing::_)).Times(0);
   EXPECT_CALL(*reject, call(::testing::_)).Times(1);
 
-  UserGestureIndicator indicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator indicator(DocumentUserGestureToken::create(
+      &pageHolder->document(), UserGestureToken::NewGesture));
   remotePlayback->prompt().then(resolve->bind(), reject->bind());
   HTMLMediaElementRemotePlayback::setBooleanAttribute(
       HTMLNames::disableremoteplaybackAttr, *element, true);
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
index 89f73ac..668324f 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
@@ -5,6 +5,7 @@
 #include "modules/webaudio/BaseAudioContext.h"
 
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/frame/FrameOwner.h"
 #include "core/frame/FrameView.h"
 #include "core/frame/Settings.h"
@@ -180,8 +181,8 @@
   createChildFrame();
   childDocument().settings()->setMediaPlaybackRequiresUserGesture(true);
 
-  UserGestureIndicator userGestureScope(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator userGestureScope(DocumentUserGestureToken::create(
+      &childDocument(), UserGestureToken::NewGesture));
 
   BaseAudioContext* audioContext =
       BaseAudioContext::create(childDocument(), ASSERT_NO_EXCEPTION);
@@ -202,8 +203,8 @@
   BaseAudioContext* audioContext =
       BaseAudioContext::create(childDocument(), ASSERT_NO_EXCEPTION);
 
-  UserGestureIndicator userGestureScope(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator userGestureScope(DocumentUserGestureToken::create(
+      &childDocument(), UserGestureToken::NewGesture));
 
   audioContext->resumeContext(getScriptStateFrom(childDocument()));
   rejectPendingResolvers(audioContext);
@@ -237,8 +238,8 @@
   BaseAudioContext* audioContext =
       BaseAudioContext::create(childDocument(), ASSERT_NO_EXCEPTION);
 
-  UserGestureIndicator userGestureScope(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator userGestureScope(DocumentUserGestureToken::create(
+      &childDocument(), UserGestureToken::NewGesture));
   audioContext->maybeRecordStartAttempt();
   recordAutoplayStatus(audioContext);
 
@@ -258,8 +259,8 @@
       BaseAudioContext::create(childDocument(), ASSERT_NO_EXCEPTION);
   audioContext->maybeRecordStartAttempt();
 
-  UserGestureIndicator userGestureScope(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator userGestureScope(DocumentUserGestureToken::create(
+      &childDocument(), UserGestureToken::NewGesture));
   audioContext->resumeContext(getScriptStateFrom(childDocument()));
   rejectPendingResolvers(audioContext);
   recordAutoplayStatus(audioContext);
@@ -279,8 +280,8 @@
   BaseAudioContext* audioContext =
       BaseAudioContext::create(childDocument(), ASSERT_NO_EXCEPTION);
 
-  UserGestureIndicator userGestureScope(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator userGestureScope(DocumentUserGestureToken::create(
+      &childDocument(), UserGestureToken::NewGesture));
   audioContext->maybeRecordStartAttempt();
   audioContext->resumeContext(getScriptStateFrom(childDocument()));
   rejectPendingResolvers(audioContext);
diff --git a/third_party/WebKit/Source/platform/UserGestureIndicator.cpp b/third_party/WebKit/Source/platform/UserGestureIndicator.cpp
index 7f9a9b9..21675c4b 100644
--- a/third_party/WebKit/Source/platform/UserGestureIndicator.cpp
+++ b/third_party/WebKit/Source/platform/UserGestureIndicator.cpp
@@ -114,7 +114,6 @@
 }
 
 UserGestureToken* UserGestureIndicator::s_rootToken = nullptr;
-bool UserGestureIndicator::s_processedUserGestureSinceLoad = false;
 
 UserGestureIndicator::UserGestureIndicator(PassRefPtr<UserGestureToken> token)
     : m_token(token) {
@@ -128,7 +127,6 @@
     RecordUserGestureMerge(*s_rootToken, *m_token);
     m_token->transferGestureTo(s_rootToken);
   }
-  s_processedUserGestureSinceLoad = true;
 }
 
 UserGestureIndicator::~UserGestureIndicator() {
@@ -175,17 +173,4 @@
   return s_rootToken;
 }
 
-// static
-void UserGestureIndicator::clearProcessedUserGestureSinceLoad() {
-  if (isMainThread())
-    s_processedUserGestureSinceLoad = false;
-}
-
-// static
-bool UserGestureIndicator::processedUserGestureSinceLoad() {
-  if (!isMainThread())
-    return false;
-  return s_processedUserGestureSinceLoad;
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/UserGestureIndicator.h b/third_party/WebKit/Source/platform/UserGestureIndicator.h
index 41565a54..54602b8 100644
--- a/third_party/WebKit/Source/platform/UserGestureIndicator.h
+++ b/third_party/WebKit/Source/platform/UserGestureIndicator.h
@@ -60,12 +60,7 @@
   enum Status { NewGesture, PossiblyExistingGesture };
   enum TimeoutPolicy { Default, OutOfProcess, HasPaused };
 
-  static PassRefPtr<UserGestureToken> create(
-      Status status = PossiblyExistingGesture) {
-    return adoptRef(new UserGestureToken(status));
-  }
-
-  ~UserGestureToken() {}
+  virtual ~UserGestureToken() {}
   bool hasGestures() const;
   void transferGestureTo(UserGestureToken*);
   bool consumeGesture();
@@ -81,9 +76,10 @@
   void setUserGestureUtilizedCallback(UserGestureUtilizedCallback*);
   void userGestureUtilized();
 
- private:
+ protected:
   UserGestureToken(Status);
 
+ private:
   bool hasTimedOut() const;
 
   size_t m_consumableGestures;
@@ -117,17 +113,10 @@
 
   static UserGestureToken* currentToken();
 
-  // Reset the notion of "since load".
-  static void clearProcessedUserGestureSinceLoad();
-
-  // Returns whether a user gesture has occurred since page load.
-  static bool processedUserGestureSinceLoad();
-
   explicit UserGestureIndicator(PassRefPtr<UserGestureToken>);
   ~UserGestureIndicator();
 
  private:
-  static bool s_processedUserGestureSinceLoad;
   static UserGestureToken* s_rootToken;
 
   RefPtr<UserGestureToken> m_token;
diff --git a/third_party/WebKit/Source/platform/UserGestureIndicatorTest.cpp b/third_party/WebKit/Source/platform/UserGestureIndicatorTest.cpp
index a2a8a85..3af554b 100644
--- a/third_party/WebKit/Source/platform/UserGestureIndicatorTest.cpp
+++ b/third_party/WebKit/Source/platform/UserGestureIndicatorTest.cpp
@@ -8,43 +8,51 @@
 
 namespace blink {
 
+class TestUserGestureToken final : public UserGestureToken {
+  WTF_MAKE_NONCOPYABLE(TestUserGestureToken);
+
+ public:
+  static PassRefPtr<UserGestureToken> create(
+      Status status = PossiblyExistingGesture) {
+    return adoptRef(new TestUserGestureToken(status));
+  }
+
+  ~TestUserGestureToken() final {}
+
+ private:
+  TestUserGestureToken(Status status) : UserGestureToken(status) {}
+};
+
 // Checks for the initial state of UserGestureIndicator.
 TEST(UserGestureIndicatorTest, InitialState) {
   EXPECT_FALSE(UserGestureIndicator::utilizeUserGesture());
-  EXPECT_FALSE(UserGestureIndicator::processedUserGestureSinceLoad());
   EXPECT_EQ(nullptr, UserGestureIndicator::currentToken());
   EXPECT_FALSE(UserGestureIndicator::consumeUserGesture());
 }
 
 TEST(UserGestureIndicatorTest, ConstructedWithNewUserGesture) {
-  UserGestureIndicator::clearProcessedUserGestureSinceLoad();
   UserGestureIndicator userGestureScope(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+      TestUserGestureToken::create(UserGestureToken::NewGesture));
 
   EXPECT_TRUE(UserGestureIndicator::utilizeUserGesture());
-  EXPECT_TRUE(UserGestureIndicator::processedUserGestureSinceLoad());
   EXPECT_NE(nullptr, UserGestureIndicator::currentToken());
 
   EXPECT_TRUE(UserGestureIndicator::consumeUserGesture());
 }
 
 TEST(UserGestureIndicatorTest, ConstructedWithUserGesture) {
-  UserGestureIndicator::clearProcessedUserGestureSinceLoad();
-  UserGestureIndicator userGestureScope(UserGestureToken::create());
+  UserGestureIndicator userGestureScope(TestUserGestureToken::create());
 
   EXPECT_TRUE(UserGestureIndicator::utilizeUserGesture());
-  EXPECT_TRUE(UserGestureIndicator::processedUserGestureSinceLoad());
   EXPECT_NE(nullptr, UserGestureIndicator::currentToken());
 
   EXPECT_TRUE(UserGestureIndicator::consumeUserGesture());
 }
 
 TEST(UserGestureIndicatorTest, ConstructedWithNoUserGesture) {
-  UserGestureIndicator::clearProcessedUserGestureSinceLoad();
   UserGestureIndicator userGestureScope(nullptr);
 
   EXPECT_FALSE(UserGestureIndicator::utilizeUserGesture());
-  EXPECT_FALSE(UserGestureIndicator::processedUserGestureSinceLoad());
   EXPECT_EQ(nullptr, UserGestureIndicator::currentToken());
 
   EXPECT_FALSE(UserGestureIndicator::consumeUserGesture());
@@ -53,10 +61,9 @@
 // Check that after UserGestureIndicator destruction state will be cleared.
 TEST(UserGestureIndicatorTest, DestructUserGestureIndicator) {
   {
-    UserGestureIndicator userGestureScope(UserGestureToken::create());
+    UserGestureIndicator userGestureScope(TestUserGestureToken::create());
 
     EXPECT_TRUE(UserGestureIndicator::utilizeUserGesture());
-    EXPECT_TRUE(UserGestureIndicator::processedUserGestureSinceLoad());
     EXPECT_NE(nullptr, UserGestureIndicator::currentToken());
   }
 
@@ -69,16 +76,15 @@
 TEST(UserGestureIndicatorTest, ScopedNewUserGestureIndicators) {
   // Root GestureIndicator and GestureToken.
   UserGestureIndicator userGestureScope(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+      TestUserGestureToken::create(UserGestureToken::NewGesture));
 
   EXPECT_TRUE(UserGestureIndicator::utilizeUserGesture());
-  EXPECT_TRUE(UserGestureIndicator::processedUserGestureSinceLoad());
   EXPECT_NE(nullptr, UserGestureIndicator::currentToken());
   {
     // Construct inner UserGestureIndicator.
     // It should share GestureToken with the root indicator.
     UserGestureIndicator innerUserGesture(
-        UserGestureToken::create(UserGestureToken::NewGesture));
+        TestUserGestureToken::create(UserGestureToken::NewGesture));
 
     EXPECT_TRUE(UserGestureIndicator::utilizeUserGesture());
     EXPECT_NE(nullptr, UserGestureIndicator::currentToken());
@@ -117,7 +123,7 @@
   UsedCallback cb;
 
   {
-    UserGestureIndicator userGestureScope(UserGestureToken::create());
+    UserGestureIndicator userGestureScope(TestUserGestureToken::create());
     UserGestureIndicator::currentToken()->setUserGestureUtilizedCallback(&cb);
     EXPECT_EQ(0u, cb.getAndResetUsedCount());
 
@@ -137,7 +143,7 @@
   EXPECT_EQ(0u, cb.getAndResetUsedCount());
 
   {
-    UserGestureIndicator userGestureScope(UserGestureToken::create());
+    UserGestureIndicator userGestureScope(TestUserGestureToken::create());
     UserGestureIndicator::currentToken()->setUserGestureUtilizedCallback(&cb);
 
     // Consume also invokes the callback
@@ -152,7 +158,7 @@
 
   {
     std::unique_ptr<UserGestureIndicator> userGestureScope(
-        new UserGestureIndicator(UserGestureToken::create()));
+        new UserGestureIndicator(TestUserGestureToken::create()));
     RefPtr<UserGestureToken> token = UserGestureIndicator::currentToken();
     token->setUserGestureUtilizedCallback(&cb);
     userGestureScope.reset();
diff --git a/third_party/WebKit/Source/web/SuspendableScriptExecutor.cpp b/third_party/WebKit/Source/web/SuspendableScriptExecutor.cpp
index f6a70651..20aba329 100644
--- a/third_party/WebKit/Source/web/SuspendableScriptExecutor.cpp
+++ b/third_party/WebKit/Source/web/SuspendableScriptExecutor.cpp
@@ -8,6 +8,7 @@
 #include "bindings/core/v8/ScriptSourceCode.h"
 #include "bindings/core/v8/V8PersistentValueVector.h"
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/frame/LocalFrame.h"
 #include "platform/UserGestureIndicator.h"
 #include "public/platform/WebVector.h"
@@ -54,8 +55,9 @@
 Vector<v8::Local<v8::Value>> WebScriptExecutor::execute(LocalFrame* frame) {
   std::unique_ptr<UserGestureIndicator> indicator;
   if (m_userGesture) {
-    indicator = wrapUnique(new UserGestureIndicator(
-        UserGestureToken::create(UserGestureToken::NewGesture)));
+    indicator =
+        wrapUnique(new UserGestureIndicator(DocumentUserGestureToken::create(
+            frame->document(), UserGestureToken::NewGesture)));
   }
 
   Vector<v8::Local<v8::Value>> results;
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index a54fa13..e5deda9 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -30,6 +30,7 @@
 
 #include "web/WebFrameWidgetImpl.h"
 
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/editing/EditingUtilities.h"
 #include "core/editing/Editor.h"
 #include "core/editing/FrameSelection.h"
@@ -349,8 +350,9 @@
         break;
       case WebInputEvent::MouseDown:
         eventType = EventTypeNames::mousedown;
-        gestureIndicator = wrapUnique(new UserGestureIndicator(
-            UserGestureToken::create(UserGestureToken::NewGesture)));
+        gestureIndicator = wrapUnique(
+            new UserGestureIndicator(DocumentUserGestureToken::create(
+                &node->document(), UserGestureToken::NewGesture)));
         m_mouseCaptureGestureToken = gestureIndicator->currentToken();
         break;
       case WebInputEvent::MouseUp:
@@ -523,8 +525,8 @@
   if (m_suppressNextKeypressEvent && !inputMethodController.hasComposition())
     return text.isEmpty();
 
-  UserGestureIndicator gestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      focused->document(), UserGestureToken::NewGesture));
 
   // When the range of composition underlines overlap with the range between
   // selectionStart and selectionEnd, WebKit somehow won't paint the selection
@@ -541,12 +543,13 @@
 // This code needs to be refactored  (http://crbug.com/629721).
 bool WebFrameWidgetImpl::commitText(const WebString& text,
                                     int relativeCaretPosition) {
-  UserGestureIndicator gestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
   LocalFrame* focused = focusedLocalFrameAvailableForIme();
   if (!focused)
     return false;
 
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      focused->document(), UserGestureToken::NewGesture));
+
   if (WebPlugin* plugin = focusedPluginIfInputMethodSupported(focused))
     return plugin->commitText(text, relativeCaretPosition);
 
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index c0dffe0..0b5db43 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -100,6 +100,7 @@
 #include "bindings/core/v8/V8PerIsolateData.h"
 #include "core/HTMLNames.h"
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/IconURL.h"
 #include "core/dom/MessagePort.h"
 #include "core/dom/Node.h"
@@ -1900,8 +1901,8 @@
 
   String script = decodeURLEscapeSequences(
       url.getString().substring(strlen("javascript:")));
-  UserGestureIndicator gestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      frame()->document(), UserGestureToken::NewGesture));
   v8::HandleScope handleScope(toIsolate(frame()));
   v8::Local<v8::Value> result =
       frame()->script().executeScriptInMainWorldAndReturnValue(
diff --git a/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp b/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
index c707176e..99e6289e 100644
--- a/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
@@ -37,6 +37,7 @@
 #include "core/HTMLNames.h"
 #include "core/clipboard/DataObject.h"
 #include "core/clipboard/DataTransfer.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/ExecutionContext.h"
 #include "core/dom/Fullscreen.h"
 #include "core/events/DragEvent.h"
@@ -478,7 +479,8 @@
       kurl.getString().substring(strlen("javascript:")));
 
   UserGestureIndicator gestureIndicator(
-      popupsAllowed ? UserGestureToken::create(UserGestureToken::NewGesture)
+      popupsAllowed ? DocumentUserGestureToken::create(
+                          frame->document(), UserGestureToken::NewGesture)
                     : nullptr);
   v8::HandleScope handleScope(toIsolate(frame));
   v8::Local<v8::Value> result =
diff --git a/third_party/WebKit/Source/web/WebScopedUserGesture.cpp b/third_party/WebKit/Source/web/WebScopedUserGesture.cpp
index ea902b6c..4b48117a 100644
--- a/third_party/WebKit/Source/web/WebScopedUserGesture.cpp
+++ b/third_party/WebKit/Source/web/WebScopedUserGesture.cpp
@@ -30,8 +30,10 @@
 
 #include "public/web/WebScopedUserGesture.h"
 
+#include "core/dom/DocumentUserGestureToken.h"
 #include "platform/UserGestureIndicator.h"
 #include "public/web/WebUserGestureToken.h"
+#include "web/WebLocalFrameImpl.h"
 
 namespace blink {
 
@@ -40,9 +42,10 @@
     m_indicator.reset(new UserGestureIndicator(token));
 }
 
-WebScopedUserGesture::WebScopedUserGesture() {
-  m_indicator.reset(new UserGestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture)));
+WebScopedUserGesture::WebScopedUserGesture(WebLocalFrame* frame) {
+  m_indicator.reset(new UserGestureIndicator(DocumentUserGestureToken::create(
+      frame ? toWebLocalFrameImpl(frame)->frame()->document() : nullptr,
+      UserGestureToken::NewGesture)));
 }
 
 WebScopedUserGesture::~WebScopedUserGesture() {}
diff --git a/third_party/WebKit/Source/web/WebUserGestureIndicator.cpp b/third_party/WebKit/Source/web/WebUserGestureIndicator.cpp
index de7ec82..e1eaf1b 100644
--- a/third_party/WebKit/Source/web/WebUserGestureIndicator.cpp
+++ b/third_party/WebKit/Source/web/WebUserGestureIndicator.cpp
@@ -32,6 +32,7 @@
 
 #include "platform/UserGestureIndicator.h"
 #include "public/web/WebUserGestureToken.h"
+#include "web/WebLocalFrameImpl.h"
 
 namespace blink {
 
@@ -43,8 +44,10 @@
   return UserGestureIndicator::consumeUserGesture();
 }
 
-bool WebUserGestureIndicator::processedUserGestureSinceLoad() {
-  return UserGestureIndicator::processedUserGestureSinceLoad();
+bool WebUserGestureIndicator::processedUserGestureSinceLoad(
+    WebLocalFrame* frame) {
+  Document* document = toWebLocalFrameImpl(frame)->frame()->document();
+  return document->hasReceivedUserGesture();
 }
 
 WebUserGestureToken WebUserGestureIndicator::currentUserGestureToken() {
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index 11741f01..0e060e0 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -34,6 +34,7 @@
 #include "core/HTMLNames.h"
 #include "core/clipboard/DataObject.h"
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/Fullscreen.h"
 #include "core/dom/LayoutTreeBuilderTraversal.h"
 #include "core/dom/Text.h"
@@ -260,26 +261,26 @@
   // If a UserGestureIndicator is created for a user gesture since the last
   // page load and *userGestureObserved is false, the UserGestureNotifier
   // will notify the client and set *userGestureObserved to true.
-  UserGestureNotifier(WebAutofillClient*, bool* userGestureObserved);
+  UserGestureNotifier(WebLocalFrameImpl*, bool* userGestureObserved);
   ~UserGestureNotifier();
 
  private:
-  WebAutofillClient* const m_client;
+  Persistent<WebLocalFrameImpl> m_frame;
   bool* const m_userGestureObserved;
 };
 
-UserGestureNotifier::UserGestureNotifier(WebAutofillClient* client,
+UserGestureNotifier::UserGestureNotifier(WebLocalFrameImpl* frame,
                                          bool* userGestureObserved)
-    : m_client(client), m_userGestureObserved(userGestureObserved) {
+    : m_frame(frame), m_userGestureObserved(userGestureObserved) {
   DCHECK(m_userGestureObserved);
 }
 
 UserGestureNotifier::~UserGestureNotifier() {
   if (!*m_userGestureObserved &&
-      UserGestureIndicator::processedUserGestureSinceLoad()) {
+      m_frame->frame()->document()->hasReceivedUserGesture()) {
     *m_userGestureObserved = true;
-    if (m_client)
-      m_client->firstUserGestureObserved();
+    if (m_frame && m_frame->autofillClient())
+      m_frame->autofillClient()->firstUserGestureObserved();
   }
 }
 
@@ -2223,7 +2224,7 @@
     return WebInputEventResult::NotHandled;
 
   WebAutofillClient* autofillClient = mainFrameImpl()->autofillClient();
-  UserGestureNotifier notifier(autofillClient, &m_userGestureObserved);
+  UserGestureNotifier notifier(mainFrameImpl(), &m_userGestureObserved);
   // On the first input event since page load, |notifier| instructs the
   // autofill client to unblock values of password input fields of any forms
   // on the page. There is a single input event, GestureTap, which can both
@@ -2294,8 +2295,9 @@
         break;
       case WebInputEvent::MouseDown:
         eventType = EventTypeNames::mousedown;
-        gestureIndicator = wrapUnique(new UserGestureIndicator(
-            UserGestureToken::create(UserGestureToken::NewGesture)));
+        gestureIndicator = wrapUnique(
+            new UserGestureIndicator(DocumentUserGestureToken::create(
+                &node->document(), UserGestureToken::NewGesture)));
         m_mouseCaptureGestureToken = gestureIndicator->currentToken();
         break;
       case WebInputEvent::MouseUp:
@@ -2468,8 +2470,8 @@
   if (m_suppressNextKeypressEvent && !inputMethodController.hasComposition())
     return text.isEmpty();
 
-  UserGestureIndicator gestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      focused->document(), UserGestureToken::NewGesture));
 
   // When the range of composition underlines overlap with the range between
   // selectionStart and selectionEnd, WebKit somehow won't paint the selection
@@ -2500,13 +2502,13 @@
 }
 
 bool WebViewImpl::commitText(const WebString& text, int relativeCaretPosition) {
-  UserGestureIndicator gestureIndicator(
-      UserGestureToken::create(UserGestureToken::NewGesture));
-
   LocalFrame* focused = focusedLocalFrameAvailableForIme();
   if (!focused)
     return false;
 
+  UserGestureIndicator gestureIndicator(DocumentUserGestureToken::create(
+      focused->document(), UserGestureToken::NewGesture));
+
   if (WebPlugin* plugin = focusedPluginIfInputMethodSupported(focused))
     return plugin->commitText(text, relativeCaretPosition);
 
@@ -3661,10 +3663,7 @@
 
   DCHECK(m_currentDragData);
   m_currentDragData = DataObject::create(webDragData);
-
-  WebAutofillClient* autofillClient =
-      mainFrameImpl() ? mainFrameImpl()->autofillClient() : 0;
-  UserGestureNotifier notifier(autofillClient, &m_userGestureObserved);
+  UserGestureNotifier notifier(mainFrameImpl(), &m_userGestureObserved);
 
   // If this webview transitions from the "drop accepting" state to the "not
   // accepting" state, then our IPC message reply indicating that may be in-
@@ -3683,8 +3682,6 @@
   DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint,
                     static_cast<DragOperation>(m_operationsAllowed));
 
-  UserGestureIndicator gesture(
-      UserGestureToken::create(UserGestureToken::NewGesture));
   m_page->dragController().performDrag(&dragData);
 
   m_dragOperation = WebDragOperationNone;
@@ -4481,8 +4478,12 @@
   switch (event.type) {
     case WebInputEvent::MouseDown:
       eventType = EventTypeNames::mousedown;
-      gestureIndicator = wrapUnique(new UserGestureIndicator(
-          UserGestureToken::create(UserGestureToken::NewGesture)));
+      if (!page() || !page()->pointerLockController().element())
+        break;
+      gestureIndicator =
+          wrapUnique(new UserGestureIndicator(DocumentUserGestureToken::create(
+              &page()->pointerLockController().element()->document(),
+              UserGestureToken::NewGesture)));
       m_pointerLockGestureToken = gestureIndicator->currentToken();
       break;
     case WebInputEvent::MouseUp:
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index 3787fb6..e2bd5d4 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -39,6 +39,7 @@
 #include "core/css/resolver/StyleResolver.h"
 #include "core/css/resolver/ViewportStyleResolver.h"
 #include "core/dom/Document.h"
+#include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/Fullscreen.h"
 #include "core/dom/NodeComputedStyle.h"
 #include "core/dom/Range.h"
@@ -6739,7 +6740,7 @@
       PlatformMouseEvent::RealOrIndistinguishable, String(), nullptr);
   FrameLoadRequest frameRequest(document, ResourceRequest(destination));
   frameRequest.setTriggeringEvent(event);
-  UserGestureIndicator gesture(UserGestureToken::create());
+  UserGestureIndicator gesture(DocumentUserGestureToken::create(document));
   toLocalFrame(webViewHelper.webView()->page()->mainFrame())
       ->loader()
       .load(frameRequest);
@@ -7461,7 +7462,7 @@
   webViewImpl->updateAllLifecyclePhases();
 
   Document* document = webViewImpl->mainFrameImpl()->frame()->document();
-  UserGestureIndicator gesture(UserGestureToken::create());
+  UserGestureIndicator gesture(DocumentUserGestureToken::create(document));
   Element* divFullscreen = document->getElementById("div1");
   Fullscreen::requestFullscreen(*divFullscreen, Fullscreen::PrefixedRequest);
   webViewImpl->didEnterFullscreen();
@@ -7496,7 +7497,7 @@
   webViewImpl->updateAllLifecyclePhases();
 
   Document* document = webViewImpl->mainFrameImpl()->frame()->document();
-  UserGestureIndicator gesture(UserGestureToken::create());
+  UserGestureIndicator gesture(DocumentUserGestureToken::create(document));
   Element* divFullscreen = document->getElementById("div1");
   Fullscreen::requestFullscreen(*divFullscreen, Fullscreen::PrefixedRequest);
   webViewImpl->didEnterFullscreen();
@@ -7540,7 +7541,7 @@
   webViewImpl->updateAllLifecyclePhases();
 
   Document* document = webViewImpl->mainFrameImpl()->frame()->document();
-  UserGestureIndicator gesture(UserGestureToken::create());
+  UserGestureIndicator gesture(DocumentUserGestureToken::create(document));
   Fullscreen::requestFullscreen(*document->documentElement(),
                                 Fullscreen::PrefixedRequest);
   webViewImpl->didEnterFullscreen();
@@ -7581,7 +7582,7 @@
       toWebLocalFrameImpl(webViewHelper.webView()->mainFrame()->firstChild())
           ->frame()
           ->document();
-  UserGestureIndicator gesture(UserGestureToken::create());
+  UserGestureIndicator gesture(DocumentUserGestureToken::create(document));
   Element* divFullscreen = document->getElementById("div1");
   Fullscreen::requestFullscreen(*divFullscreen, Fullscreen::PrefixedRequest);
   webViewImpl->didEnterFullscreen();
@@ -7625,7 +7626,7 @@
   EXPECT_FLOAT_EQ(5.0, webViewImpl->maximumPageScaleFactor());
 
   Document* document = webViewImpl->mainFrameImpl()->frame()->document();
-  UserGestureIndicator gesture(UserGestureToken::create());
+  UserGestureIndicator gesture(DocumentUserGestureToken::create(document));
   Fullscreen::requestFullscreen(*document->documentElement(),
                                 Fullscreen::PrefixedRequest);
   webViewImpl->didEnterFullscreen();
@@ -7662,7 +7663,7 @@
   LayoutViewItem layoutViewItem =
       webViewHelper.webView()->mainFrameImpl()->frameView()->layoutViewItem();
   Document* document = webViewImpl->mainFrameImpl()->frame()->document();
-  UserGestureIndicator gesture(UserGestureToken::create());
+  UserGestureIndicator gesture(DocumentUserGestureToken::create(document));
   Fullscreen::requestFullscreen(*document->documentElement(),
                                 Fullscreen::PrefixedRequest);
   webViewImpl->didEnterFullscreen();
@@ -7724,7 +7725,7 @@
 
   {
     Document* document = webViewImpl->mainFrameImpl()->frame()->document();
-    UserGestureIndicator gesture(UserGestureToken::create());
+    UserGestureIndicator gesture(DocumentUserGestureToken::create(document));
     Fullscreen::requestFullscreen(*document->body(),
                                   Fullscreen::PrefixedRequest);
   }
@@ -7786,7 +7787,7 @@
 
   Document* document = webViewImpl->mainFrameImpl()->frame()->document();
   UserGestureIndicator gesture(
-      UserGestureToken::create(UserGestureToken::NewGesture));
+      DocumentUserGestureToken::create(document, UserGestureToken::NewGesture));
   Fullscreen::requestFullscreen(*document->documentElement(),
                                 Fullscreen::PrefixedRequest);
   webViewImpl->didEnterFullscreen();
diff --git a/third_party/WebKit/Source/web/tests/WebUserGestureTokenTest.cpp b/third_party/WebKit/Source/web/tests/WebUserGestureTokenTest.cpp
index 0095ec9e..9a67b40 100644
--- a/third_party/WebKit/Source/web/tests/WebUserGestureTokenTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebUserGestureTokenTest.cpp
@@ -30,6 +30,7 @@
 
 #include "public/web/WebUserGestureToken.h"
 
+#include "core/dom/DocumentUserGestureToken.h"
 #include "platform/UserGestureIndicator.h"
 #include "public/web/WebScopedUserGesture.h"
 #include "public/web/WebUserGestureIndicator.h"
@@ -40,8 +41,6 @@
 TEST(WebUserGestureTokenTest, Basic) {
   WebUserGestureToken token;
   EXPECT_FALSE(token.hasGestures());
-  UserGestureIndicator::clearProcessedUserGestureSinceLoad();
-  EXPECT_FALSE(UserGestureIndicator::processedUserGestureSinceLoad());
 
   {
     WebScopedUserGesture indicator(token);
@@ -49,11 +48,10 @@
   }
 
   {
-    UserGestureIndicator indicator(
-        UserGestureToken::create(UserGestureToken::NewGesture));
+    UserGestureIndicator indicator(DocumentUserGestureToken::create(
+        nullptr, UserGestureToken::NewGesture));
     EXPECT_TRUE(WebUserGestureIndicator::isProcessingUserGesture());
     token = WebUserGestureIndicator::currentUserGestureToken();
-    EXPECT_TRUE(UserGestureIndicator::processedUserGestureSinceLoad());
   }
 
   EXPECT_TRUE(token.hasGestures());
@@ -72,8 +70,6 @@
     WebScopedUserGesture indicator(token);
     EXPECT_FALSE(WebUserGestureIndicator::isProcessingUserGesture());
   }
-
-  EXPECT_TRUE(UserGestureIndicator::processedUserGestureSinceLoad());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/web/WebScopedUserGesture.h b/third_party/WebKit/public/web/WebScopedUserGesture.h
index bf34f711..3e620147 100644
--- a/third_party/WebKit/public/web/WebScopedUserGesture.h
+++ b/third_party/WebKit/public/web/WebScopedUserGesture.h
@@ -37,6 +37,7 @@
 namespace blink {
 
 class UserGestureIndicator;
+class WebLocalFrame;
 class WebUserGestureToken;
 
 // An instance of this class, while kept alive, will indicate that we are in
@@ -55,7 +56,7 @@
 class WebScopedUserGesture {
  public:
   BLINK_EXPORT explicit WebScopedUserGesture(const WebUserGestureToken& token);
-  BLINK_EXPORT WebScopedUserGesture();
+  BLINK_EXPORT WebScopedUserGesture(WebLocalFrame*);
   BLINK_EXPORT ~WebScopedUserGesture();
 
  private:
diff --git a/third_party/WebKit/public/web/WebUserGestureIndicator.h b/third_party/WebKit/public/web/WebUserGestureIndicator.h
index 7e2c3d9..44b1cacb 100644
--- a/third_party/WebKit/public/web/WebUserGestureIndicator.h
+++ b/third_party/WebKit/public/web/WebUserGestureIndicator.h
@@ -35,6 +35,7 @@
 
 namespace blink {
 
+class WebLocalFrame;
 class WebUserGestureToken;
 
 class WebUserGestureIndicator {
@@ -46,9 +47,9 @@
   // consumed.
   BLINK_EXPORT static bool consumeUserGesture();
 
-  // Returns true if a user gesture was processed on the current page since the
-  // time the page was loaded.
-  BLINK_EXPORT static bool processedUserGestureSinceLoad();
+  // Returns true if a user gesture was processed on the provided frame since
+  // the time the frame was loaded.
+  BLINK_EXPORT static bool processedUserGestureSinceLoad(WebLocalFrame*);
 
   // Returns a token for the currently active user gesture. It can be used to
   // continue processing the user gesture later on using a