Bug 1516482 - part1 : save user gesture activation flag in top level browsing context r=nika
authorAlastor Wu <alwu@mozilla.com>
Mon, 14 Jan 2019 23:09:42 +0000
changeset 513872 8a59ebb819c260b4375863f5722d01bd2efd0815
parent 513871 041f9c2bfce5c7dcb1eacd495be83252139f96a0
child 513873 da7cefd6569a863d4515b70616d0082e3debbfae
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1516482
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1516482 - part1 : save user gesture activation flag in top level browsing context r=nika This patch moves the user gesture activation flag from saving in document to saving in browsing context. The flag would be saved in the top level browsing context and then every time we need to check for that flag, we will request it from top level browsing context. Differential Revision: https://phabricator.services.mozilla.com/D15435
docshell/base/BrowsingContext.cpp
docshell/base/BrowsingContext.h
dom/base/Document.cpp
dom/base/Document.h
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -24,16 +24,21 @@
 #include "nsGlobalWindowOuter.h"
 #include "nsContentUtils.h"
 #include "nsScriptError.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
+extern mozilla::LazyLogModule gUserInteractionPRLog;
+
+#define USER_ACTIVATION_LOG(msg, ...) \
+  MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
+
 static LazyLogModule gBrowsingContextLog("BrowsingContext");
 
 static StaticAutoPtr<BrowsingContext::Children> sRootBrowsingContexts;
 
 template <template <typename> class PtrType>
 using BrowsingContextMap =
     HashMap<uint64_t, PtrType<BrowsingContext>, DefaultHasher<uint64_t>,
             InfallibleAllocPolicy>;
@@ -59,16 +64,24 @@ static void Sync(BrowsingContext* aBrows
   RefPtr<BrowsingContext> parent = aBrowsingContext->GetParent();
   BrowsingContext* opener = aBrowsingContext->GetOpener();
   cc->SendAttachBrowsingContext(BrowsingContextId(parent ? parent->Id() : 0),
                                 BrowsingContextId(opener ? opener->Id() : 0),
                                 BrowsingContextId(aBrowsingContext->Id()),
                                 aBrowsingContext->Name());
 }
 
+BrowsingContext* BrowsingContext::TopLevelBrowsingContext() {
+  BrowsingContext* bc = this;
+  while (bc->mParent) {
+    bc = bc->mParent;
+  }
+  return bc;
+}
+
 /* static */ void BrowsingContext::Init() {
   if (!sRootBrowsingContexts) {
     sRootBrowsingContexts = new BrowsingContext::Children();
     ClearOnShutdown(&sRootBrowsingContexts);
   }
 
   if (!sBrowsingContexts) {
     sBrowsingContexts = new BrowsingContextMap<WeakPtr>();
@@ -151,17 +164,18 @@ BrowsingContext::BrowsingContext(Browsin
                                  BrowsingContext* aOpener,
                                  const nsAString& aName,
                                  uint64_t aBrowsingContextId, Type aType)
     : mType(aType),
       mBrowsingContextId(aBrowsingContextId),
       mParent(aParent),
       mOpener(aOpener),
       mName(aName),
-      mClosed(false) {
+      mClosed(false),
+      mIsActivatedByUserGesture(false) {
   if (mParent) {
     mBrowsingContextGroup = mParent->mBrowsingContextGroup;
   } else if (mOpener) {
     mBrowsingContextGroup = mOpener->mBrowsingContextGroup;
   } else {
     mBrowsingContextGroup = new BrowsingContextGroup();
   }
 
@@ -303,16 +317,37 @@ nsISupports* BrowsingContext::GetParentO
   return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
 }
 
 JSObject* BrowsingContext::WrapObject(JSContext* aCx,
                                       JS::Handle<JSObject*> aGivenProto) {
   return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
 }
 
+void BrowsingContext::NotifyUserGestureActivation() {
+  // We would set the user gesture activation flag on the top level browsing
+  // context, which would automatically be sync to other top level browsing
+  // contexts which are in the different process.
+  RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext();
+  topLevelBC->SetUserGestureActivation();
+  // TODO(alwu) : sync the value to ChromeBrowsingContext.
+}
+
+void BrowsingContext::SetUserGestureActivation() {
+  MOZ_ASSERT(!mParent, "Set user activation flag on non top-level context!");
+  USER_ACTIVATION_LOG(
+      "Set user gesture activation for browsing context 0x%08" PRIx64, Id());
+  mIsActivatedByUserGesture = true;
+}
+
+bool BrowsingContext::GetUserGestureActivation() {
+  RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext();
+  return topLevelBC->mIsActivatedByUserGesture;
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mChildren, mParent)
   if (XRE_IsParentProcess()) {
     ChromeBrowsingContext::Cast(tmp)->Unlink();
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -135,16 +135,28 @@ class BrowsingContext : public nsWrapper
 
   static void GetRootBrowsingContexts(
       nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts);
 
   nsISupports* GetParentObject() const;
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
+  // This function would be called when its corresponding document is activated
+  // by user gesture, and we would set the flag in the top level browsing
+  // context.
+  void NotifyUserGestureActivation();
+
+  // This function would only be called in the top level browsing context. It
+  // would set the user gesture activation flag.
+  void SetUserGestureActivation();
+
+  // Return true if it corresponding document is activated by user gesture.
+  bool GetUserGestureActivation();
+
   // Return the window proxy object that corresponds to this browsing context.
   inline JSObject* GetWindowProxy() const { return mWindowProxy; }
   // Set the window proxy object that corresponds to this browsing context.
   void SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) {
     mWindowProxy = aWindowProxy;
   }
 
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(BrowsingContext)
@@ -199,16 +211,18 @@ class BrowsingContext : public nsWrapper
       mWindowProxy = obj;
     }
   }
   // Clear the window proxy object that corresponds to this browsing context.
   // This should be called if the window proxy object is finalized, or it can't
   // reach its browsing context anymore.
   void ClearWindowProxy() { mWindowProxy = nullptr; }
 
+  BrowsingContext* TopLevelBrowsingContext();
+
   // Type of BrowsingContent
   const Type mType;
 
   // Unique id identifying BrowsingContext
   const uint64_t mBrowsingContextId;
 
   RefPtr<BrowsingContextGroup> mBrowsingContextGroup;
   RefPtr<BrowsingContext> mParent;
@@ -217,16 +231,20 @@ class BrowsingContext : public nsWrapper
   nsCOMPtr<nsIDocShell> mDocShell;
   nsString mName;
   // This is not a strong reference, but using a JS::Heap for that should be
   // fine. The JSObject stored in here should be a proxy with a
   // nsOuterWindowProxy handler, which will update the pointer from its
   // objectMoved hook and clear it from its finalize hook.
   JS::Heap<JSObject*> mWindowProxy;
   bool mClosed;
+
+  // This flag is only valid in the top level browsing context, it indicates
+  // whether the corresponding document has been activated by user gesture.
+  bool mIsActivatedByUserGesture;
 };
 
 /**
  * Gets a WindowProxy object for a BrowsingContext that lives in a different
  * process (creating the object if it doesn't already exist). The WindowProxy
  * object will be in the compartment that aCx is currently in. This should only
  * be called if aContext doesn't hold a docshell, otherwise the BrowsingContext
  * lives in this process, and a same-process WindowProxy should be used (see
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -306,17 +306,17 @@ extern bool sDisablePrefetchHTTPSPref;
 
 namespace mozilla {
 namespace dom {
 
 typedef nsTArray<Link*> LinkArray;
 
 static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
 static LazyLogModule gCspPRLog("CSP");
-static LazyLogModule gUserInteractionPRLog("UserInteraction");
+LazyLogModule gUserInteractionPRLog("UserInteraction");
 
 static nsresult GetHttpChannelHelper(nsIChannel* aChannel,
                                      nsIHttpChannel** aHttpChannel) {
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     httpChannel.forget(aHttpChannel);
     return NS_OK;
   }
@@ -11661,26 +11661,39 @@ void Document::SetUserHasInteracted() {
   nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->GetLoadInfo() : nullptr;
   if (loadInfo) {
     loadInfo->SetDocumentHasUserInteracted(true);
   }
 
   MaybeAllowStorageForOpenerAfterUserInteraction();
 }
 
+BrowsingContext* Document::GetBrowsingContext() const {
+  nsPIDOMWindowOuter* outer = GetWindow();
+  return outer ? outer->GetBrowsingContext() : nullptr;
+}
+
 void Document::NotifyUserGestureActivation() {
-  // Activate this document and all documents up to the top level
-  // content document.
-  Document* doc = this;
-  while (doc && !doc->mUserGestureActivated) {
-    MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
-            ("Document %p has been activated by user.", this));
-    doc->mUserGestureActivated = true;
-    doc = doc->GetSameTypeParentDocument();
-  }
+  if (HasBeenUserGestureActivated()) {
+    return;
+  }
+
+  RefPtr<BrowsingContext> bc = GetBrowsingContext();
+  if (!bc) {
+    return;
+  }
+  bc->NotifyUserGestureActivation();
+}
+
+bool Document::HasBeenUserGestureActivated() {
+  RefPtr<BrowsingContext> bc = GetBrowsingContext();
+  if (!bc) {
+    return false;
+  }
+  return bc->GetUserGestureActivation();
 }
 
 void Document::MaybeNotifyAutoplayBlocked() {
   Document* topLevelDoc = GetTopLevelContentDocument();
   if (!topLevelDoc) {
     return;
   }
 
@@ -11938,36 +11951,16 @@ void Document::MaybeStoreUserInteraction
   // This value will be reset by the timer.
   mHasUserInteractionTimerScheduled = true;
 }
 
 void Document::ResetUserInteractionTimer() {
   mHasUserInteractionTimerScheduled = false;
 }
 
-bool Document::HasBeenUserGestureActivated() {
-  if (mUserGestureActivated) {
-    return true;
-  }
-
-  // If any ancestor document is activated, so are we.
-  Document* doc = GetSameTypeParentDocument();
-  while (doc) {
-    if (doc->mUserGestureActivated) {
-      // An ancestor is also activated. Record activation on the unactivated
-      // sub-branch to speed up future queries.
-      NotifyUserGestureActivation();
-      break;
-    }
-    doc = doc->GetSameTypeParentDocument();
-  }
-
-  return mUserGestureActivated;
-}
-
 bool Document::IsExtensionPage() const {
   return Preferences::GetBool("media.autoplay.allow-extension-background-pages",
                               true) &&
          BasePrincipal::Cast(NodePrincipal())->AddonPolicy();
 }
 
 Document* Document::GetSameTypeParentDocument() {
   nsCOMPtr<nsIDocShellTreeItem> current = GetDocShell();
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3297,16 +3297,18 @@ class Document : public nsINode,
 
   // This function is used for mochitest only.
   void ClearUserGestureActivation();
 
   // Return true if NotifyUserGestureActivation() has been called on any
   // document in the document tree.
   bool HasBeenUserGestureActivated();
 
+  BrowsingContext* GetBrowsingContext() const;
+
   // This document is a WebExtension page, it might be a background page, a
   // popup, a visible tab, a visible iframe ...e.t.c.
   bool IsExtensionPage() const;
 
   bool HasScriptsBlockedBySandbox();
 
   bool InlineScriptAllowedByCSP();