Bug 1465466 Part 5 - Don't use APZ in recording/replaying processes, r=kats,mconley.
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 23 Jul 2018 21:51:24 +0000
changeset 822120 4d1551e0a5f2b3b7c32d7c8fc2ef2f4a36428759
parent 822119 8eb5cd519eda2619deda1061b565398f0076997b
child 822121 6479d0bf85bc163082859c3d70f6b8c5e1d199f6
push id117296
push userbmo:gl@mozilla.com
push dateTue, 24 Jul 2018 20:28:07 +0000
reviewerskats, mconley
bugs1465466
milestone63.0a1
Bug 1465466 Part 5 - Don't use APZ in recording/replaying processes, r=kats,mconley.
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
gfx/layers/apz/src/APZCTreeManager.cpp
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2044,29 +2044,29 @@ ContentParent::NotifyTabDestroyed(const 
   }
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvOpenRecordReplayChannel(const uint32_t& aChannelId,
                                            FileDescriptor* aConnection)
 {
   // We should only get this message from the child if it is recording or replaying.
-  if (!recordreplay::IsRecordingOrReplaying()) {
+  if (!this->IsRecordingOrReplaying()) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   recordreplay::parent::OpenChannel(Pid(), aChannelId, aConnection);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvCreateReplayingProcess(const uint32_t& aChannelId)
 {
   // We should only get this message from the child if it is recording or replaying.
-  if (!recordreplay::IsRecordingOrReplaying()) {
+  if (!this->IsRecordingOrReplaying()) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   while (aChannelId >= mReplayingChildren.length()) {
     if (!mReplayingChildren.append(nullptr)) {
       return IPC_FAIL_NO_REASON(this);
     }
   }
@@ -2087,17 +2087,17 @@ ContentParent::RecvCreateReplayingProces
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvTerminateReplayingProcess(const uint32_t& aChannelId)
 {
   // We should only get this message from the child if it is recording or replaying.
-  if (!recordreplay::IsRecordingOrReplaying()) {
+  if (!this->IsRecordingOrReplaying()) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   if (aChannelId < mReplayingChildren.length() && mReplayingChildren[aChannelId]) {
     DelayedDeleteSubprocess(mReplayingChildren[aChannelId]);
     mReplayingChildren[aChannelId] = nullptr;
   }
   return IPC_OK();
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1263,16 +1263,20 @@ public:
                                const bool& aAnonymize,
                                const bool& aMinimizeMemoryUsage,
                                const MaybeFileDesc& aDMDFile) override;
 
   bool CanCommunicateWith(ContentParentId aOtherProcess);
 
   nsresult SaveRecording(nsIFile* aFile, bool* aRetval);
 
+  bool IsRecordingOrReplaying() const {
+    return mRecordReplayState != eNotRecordingOrReplaying;
+  }
+
 private:
 
   // If you add strong pointers to cycle collected objects here, be sure to
   // release these objects in ShutDownProcess.  See the comment there for more
   // details.
 
   ContentProcessHost* mSubprocess;
   const TimeStamp mLaunchTS; // used to calculate time to start content process
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -170,16 +170,17 @@ TabParent::TabParent(nsIContentParent* a
 #endif
   , mLayerTreeEpoch(1)
   , mPreserveLayers(false)
   , mRenderLayers(true)
   , mHasLayers(false)
   , mHasPresented(false)
   , mHasBeforeUnload(false)
   , mIsMouseEnterIntoWidgetEventSuppressed(false)
+  , mIsActiveRecordReplayTab(false)
 {
   MOZ_ASSERT(aManager);
   // When the input event queue is disabled, we don't need to handle the case
   // that some input events are dispatched before PBrowserConstructor.
   mIsReadyToHandleInputEvents = !ContentParent::IsInputEventQueueSupported();
 }
 
 TabParent::~TabParent()
@@ -369,16 +370,18 @@ TabParent::DestroyInternal()
   // is shut down.
   const ManagedContainer<PPluginWidgetParent>& kids =
     ManagedPPluginWidgetParent();
   for (auto iter = kids.ConstIter(); !iter.Done(); iter.Next()) {
     static_cast<mozilla::plugins::PluginWidgetParent*>(
        iter.Get()->GetKey())->ParentDestroy();
   }
 #endif
+
+  SetIsActiveRecordReplayTab(false);
 }
 
 void
 TabParent::Destroy()
 {
   // Aggressively release the window to avoid leaking the world in shutdown
   // corner cases.
   mBrowserDOMWindow = nullptr;
@@ -2869,16 +2872,21 @@ TabParent::SetDocShellIsActive(bool isAc
     }
   }
 #endif
 
   // Let's inform the priority manager. This operation can end up with the
   // changing of the process priority.
   ProcessPriorityManager::TabActivityChanged(this, isActive);
 
+  // Keep track of how many active recording/replaying tabs there are.
+  if (Manager()->AsContentParent()->IsRecordingOrReplaying()) {
+    SetIsActiveRecordReplayTab(isActive);
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::GetDocShellIsActive(bool* aIsActive)
 {
   *aIsActive = mDocShellIsActive;
   return NS_OK;
@@ -3587,16 +3595,27 @@ TabParent::LiveResizeStarted()
 }
 
 void
 TabParent::LiveResizeStopped()
 {
   SuppressDisplayport(false);
 }
 
+/* static */ size_t TabParent::gNumActiveRecordReplayTabs;
+
+void
+TabParent::SetIsActiveRecordReplayTab(bool aIsActive)
+{
+  if (aIsActive != mIsActiveRecordReplayTab) {
+    gNumActiveRecordReplayTabs += aIsActive ? 1 : -1;
+    mIsActiveRecordReplayTab = aIsActive;
+  }
+}
+
 NS_IMETHODIMP
 FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
 {
   nsAuthInformationHolder* holder =
     static_cast<nsAuthInformationHolder*>(aAuthInfo);
 
   if (!net::gNeckoChild->SendOnAuthAvailable(mCallbackId,
                                              holder->User(),
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -586,16 +586,20 @@ public:
 
   // LiveResizeListener implementation
   void LiveResizeStarted() override;
   void LiveResizeStopped() override;
 
   void SetReadyToHandleInputEvents() { mIsReadyToHandleInputEvents = true; }
   bool IsReadyToHandleInputEvents() { return mIsReadyToHandleInputEvents; }
 
+  static bool AreRecordReplayTabsActive() {
+    return gNumActiveRecordReplayTabs != 0;
+  }
+
 protected:
   bool ReceiveMessage(const nsString& aMessage,
                       bool aSync,
                       ipc::StructuredCloneData* aData,
                       mozilla::jsipc::CpowHolder* aCpows,
                       nsIPrincipal* aPrincipal,
                       nsTArray<ipc::StructuredCloneData>* aJSONRetVal = nullptr);
 
@@ -781,16 +785,25 @@ private:
   // True when the remote browser is created and ready to handle input events.
   bool mIsReadyToHandleInputEvents;
 
   // True if we suppress the eMouseEnterIntoWidget event due to the TabChild was
   // not ready to handle it. We will resend it when the next time we fire a
   // mouse event and the TabChild is ready.
   bool mIsMouseEnterIntoWidgetEventSuppressed;
 
+  // How many record/replay tabs have active docshells in this process.
+  static size_t gNumActiveRecordReplayTabs;
+
+  // Whether this tab is contributing to gNumActiveRecordReplayTabs.
+  bool mIsActiveRecordReplayTab;
+
+  // Update whether this is an active record/replay tab.
+  void SetIsActiveRecordReplayTab(bool aIsActive);
+
 public:
   static TabParent* GetTabParentFromLayersId(layers::LayersId aLayersId);
 };
 
 struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final
 {
 public:
   AutoUseNewTab(TabParent* aNewTab, nsCString* aURLToLoad)
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -11,16 +11,17 @@
 #include "Compositor.h"                 // for Compositor
 #include "DragTracker.h"                // for DragTracker
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "HitTestingTreeNode.h"         // for HitTestingTreeNode
 #include "InputBlockState.h"            // for InputBlockState
 #include "InputData.h"                  // for InputData, etc
 #include "Layers.h"                     // for Layer, etc
 #include "mozilla/dom/MouseEventBinding.h" // for MouseEvent constants
+#include "mozilla/dom/TabParent.h"      // for AreRecordReplayTabsActive
 #include "mozilla/dom/Touch.h"          // for Touch
 #include "mozilla/gfx/gfxVars.h"        // for gfxVars
 #include "mozilla/gfx/GPUParent.h"      // for GPUParent
 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
 #include "mozilla/gfx/Point.h"          // for Point
 #include "mozilla/layers/APZSampler.h"  // for APZSampler
 #include "mozilla/layers/APZThreadUtils.h"  // for AssertOnControllerThread, etc
 #include "mozilla/layers/APZUpdater.h"  // for APZUpdater
@@ -1131,16 +1132,23 @@ APZCTreeManager::FlushApzRepaints(Layers
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid,
                                    uint64_t* aOutInputBlockId)
 {
   APZThreadUtils::AssertOnControllerThread();
 
+  // Ignore input events when there are active tabs that are recording or
+  // replaying. APZ does not work with the special layers constructed by
+  // the middleman processes being communicated with here.
+  if (dom::TabParent::AreRecordReplayTabsActive()) {
+    return nsEventStatus_eIgnore;
+  }
+
   // Use a RAII class for updating the focus sequence number of this event
   AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
 
 #if defined(MOZ_WIDGET_ANDROID)
   MOZ_ASSERT(mToolbarAnimator);
   ScreenPoint scrollOffset;
   {
     RecursiveMutexAutoLock lock(mTreeLock);