Bug 1425316 P2 Improve nsGlobalWindowInner::CallOnChildren() to take variable arguments and allow iteration to be aborted. r=asuth
authorBen Kelly <ben@wanderview.com>
Tue, 19 Dec 2017 10:04:48 -0500
changeset 448675 81fe3f741dc336b2ce1df1c5d9718ee5b179a24a
parent 448674 56b3aab1d763d41b4672b06cc1034f6f5895357d
child 448676 ca80e585ab6d3e85030c583d2139167e8c112ff6
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1425316
milestone59.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 1425316 P2 Improve nsGlobalWindowInner::CallOnChildren() to take variable arguments and allow iteration to be aborted. r=asuth
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -6125,26 +6125,28 @@ nsGlobalWindowInner::SyncStateFromParent
 
   // Now apply only the number of Suspend() calls to reach the target
   // suspend count after applying the Freeze() calls.
   for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
     Suspend();
   }
 }
 
-template<typename Method>
-void
-nsGlobalWindowInner::CallOnChildren(Method aMethod)
+template<typename Method, typename... Args>
+nsGlobalWindowInner::CallState
+nsGlobalWindowInner::CallOnChildren(Method aMethod, Args& ...aArgs)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(IsCurrentInnerWindow());
 
+  CallState state = CallState::Continue;
+
   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
   if (!docShell) {
-    return;
+    return state;
   }
 
   int32_t childCount = 0;
   docShell->GetChildCount(&childCount);
 
   for (int32_t i = 0; i < childCount; ++i) {
     nsCOMPtr<nsIDocShellTreeItem> childShell;
     docShell->GetChildAt(i, getter_AddRefs(childShell));
@@ -6160,18 +6162,29 @@ nsGlobalWindowInner::CallOnChildren(Meth
 
     // This is a bit hackish. Only freeze/suspend windows which are truly our
     // subwindows.
     nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
     if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
       continue;
     }
 
-    (inner->*aMethod)();
-  }
+    // Call the child method using our helper CallChild() template method.
+    // This allows us to handle both void returning methods and methods
+    // that return CallState explicitly.  For void returning methods we
+    // assume CallState::Continue.
+    typedef decltype((inner->*aMethod)(aArgs...)) returnType;
+    state = CallChild<returnType>(inner, aMethod, aArgs...);
+
+    if (state == CallState::Stop) {
+      return state;
+    }
+  }
+
+  return state;
 }
 
 Maybe<ClientInfo>
 nsGlobalWindowInner::GetClientInfo() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   Maybe<ClientInfo> clientInfo;
   if (mClientSource) {
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -1092,18 +1092,52 @@ protected:
   // Get the parent, returns null if this is a toplevel window
   nsPIDOMWindowOuter* GetParentInternal();
 
 public:
   // popup tracking
   bool IsPopupSpamWindow();
 
 private:
-  template<typename Method>
-  void CallOnChildren(Method aMethod);
+  // A type that methods called by CallOnChildren can return.  If Stop
+  // is returned then CallOnChildren will stop calling further children.
+  // If Continue is returned then CallOnChildren will keep calling further
+  // children.
+  enum class CallState
+  {
+    Continue,
+    Stop,
+  };
+
+  // Call the given method on the immediate children of this window.  The
+  // CallState returned by the last child method invocation is returned or
+  // CallState::Continue if the method returns void.
+  template<typename Method, typename... Args>
+  CallState CallOnChildren(Method aMethod, Args& ...aArgs);
+
+  // Helper to convert a void returning child method into an implicit
+  // CallState::Continue value.
+  template<typename Return, typename Method, typename... Args>
+  typename std::enable_if<std::is_void<Return>::value,
+                          nsGlobalWindowInner::CallState>::type
+  CallChild(nsGlobalWindowInner* aWindow, Method aMethod, Args& ...aArgs)
+  {
+    (aWindow->*aMethod)(aArgs...);
+    return nsGlobalWindowInner::CallState::Continue;
+  }
+
+  // Helper that passes through the CallState value from a child method.
+  template<typename Return, typename Method, typename... Args>
+  typename std::enable_if<std::is_same<Return,
+                                       nsGlobalWindowInner::CallState>::value,
+                          nsGlobalWindowInner::CallState>::type
+  CallChild(nsGlobalWindowInner* aWindow, Method aMethod, Args& ...aArgs)
+  {
+    return (aWindow->*aMethod)(aArgs...);
+  }
 
   void FreezeInternal();
   void ThawInternal();
 
 public:
   // Timeout Functions
   // |interval| is in milliseconds.
   int32_t SetTimeoutOrInterval(JSContext* aCx,