Bug 1198381 - Extract nsITimeoutHandler from nsIScriptTimeoutHandler. r?smaug draft
authorAndreas Farre <farre@mozilla.com>
Wed, 05 Oct 2016 14:26:08 +0200
changeset 430886 20580177b3ef0a34ef4e47e191754fca144056c7
parent 430885 e83e0c0a66bfcda24e2cc9aafc5a05f4cc428b89
child 430887 cc0c1b51809e5471312dd536e008956354d21788
push id33921
push userbmo:afarre@mozilla.com
push dateFri, 28 Oct 2016 07:54:08 +0000
reviewerssmaug
bugs1198381
milestone52.0a1
Bug 1198381 - Extract nsITimeoutHandler from nsIScriptTimeoutHandler. r?smaug MozReview-Commit-ID: HJHrbodWYVf
dom/base/Timeout.cpp
dom/base/Timeout.h
dom/base/moz.build
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsIScriptTimeoutHandler.h
dom/base/nsITimeoutHandler.h
dom/base/nsJSTimeoutHandler.cpp
--- a/dom/base/Timeout.cpp
+++ b/dom/base/Timeout.cpp
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Timeout.h"
 
 #include "nsGlobalWindow.h"
-#include "nsIScriptTimeoutHandler.h"
+#include "nsITimeoutHandler.h"
 #include "nsITimer.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 Timeout::Timeout()
   : mCleared(false),
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -9,25 +9,25 @@
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsGlobalWindow;
 class nsIPrincipal;
-class nsIScriptTimeoutHandler;
+class nsITimeoutHandler;
 class nsITimer;
 
 namespace mozilla {
 namespace dom {
 
 /*
  * Timeout struct that holds information about each script
- * timeout.  Holds a strong reference to an nsIScriptTimeoutHandler, which
+ * timeout.  Holds a strong reference to an nsITimeoutHandler, which
  * abstracts the language specific cruft.
  */
 class Timeout final
   : public LinkedListElement<Timeout>
 {
 public:
   Timeout();
 
@@ -80,17 +80,17 @@ public:
 
   uint32_t mNestingLevel;
 
   // The popup state at timeout creation time if not created from
   // another timeout
   PopupControlState mPopupState;
 
   // The language-specific information about the callback.
-  nsCOMPtr<nsIScriptTimeoutHandler> mScriptHandler;
+  nsCOMPtr<nsITimeoutHandler> mScriptHandler;
 
 private:
   ~Timeout();
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -97,16 +97,17 @@ EXPORTS += [
     'nsINodeList.h',
     'nsIScriptContext.h',
     'nsIScriptElement.h',
     'nsIScriptGlobalObject.h',
     'nsIScriptNameSpaceManager.h',
     'nsIScriptObjectPrincipal.h',
     'nsIScriptTimeoutHandler.h',
     'nsIStyleSheetLinkingElement.h',
+    'nsITimeoutHandler.h',
     'nsJSEnvironment.h',
     'nsJSUtils.h',
     'nsLineBreaker.h',
     'nsMappedAttributeElement.h',
     'nsNameSpaceManager.h',
     'nsNodeInfoManager.h',
     'nsNodeUtils.h',
     'nsPIDOMWindow.h',
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -36,16 +36,17 @@
 #include "nsDOMWindowList.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptTimeoutHandler.h"
+#include "nsITimeoutHandler.h"
 #include "nsIController.h"
 #include "nsScriptNameSpaceManager.h"
 #include "nsISlowScriptDebug.h"
 #include "nsWindowMemoryReporter.h"
 #include "WindowNamedPropertiesHandler.h"
 #include "nsFrameSelection.h"
 #include "nsNetUtil.h"
 #include "nsVariant.h"
@@ -1994,20 +1995,17 @@ nsGlobalWindow::IsBlackForCC(bool aTraci
 
 void
 nsGlobalWindow::UnmarkGrayTimers()
 {
   for (Timeout* timeout = mTimeouts.getFirst();
        timeout;
        timeout = timeout->getNext()) {
     if (timeout->mScriptHandler) {
-      Function* f = timeout->mScriptHandler->GetCallback();
-      if (f) {
-        f->MarkForCC();
-      }
+      timeout->mScriptHandler->MarkForCC();
     }
   }
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIScriptGlobalObject
 //*****************************************************************************
 
@@ -12366,17 +12364,17 @@ nsGlobalWindow::SetInterval(JSContext* a
                             ErrorResult& aError)
 {
   int32_t timeout;
   bool isInterval = IsInterval(aTimeout, timeout);
   return SetTimeoutOrInterval(aCx, aHandler, timeout, isInterval, aError);
 }
 
 nsresult
-nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler* aHandler,
+nsGlobalWindow::SetTimeoutOrInterval(nsITimeoutHandler* aHandler,
                                      int32_t interval, bool aIsInterval,
                                      int32_t* aReturn)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   // If we don't have a document (we could have been unloaded since
   // the call to setTimeout was made), do nothing.
   if (!mDoc) {
@@ -12566,50 +12564,56 @@ nsGlobalWindow::RunTimeoutHandler(Timeou
   const char *reason;
   if (timeout->mIsInterval) {
     reason = "setInterval handler";
   } else {
     reason = "setTimeout handler";
   }
 
   bool abortIntervalHandler = false;
-  nsCOMPtr<nsIScriptTimeoutHandler> handler(timeout->mScriptHandler);
-  RefPtr<Function> callback = handler->GetCallback();
-
-  if (!callback) {
-    // Evaluate the timeout expression.
-    const nsAString& script = handler->GetHandlerText();
-
-    const char* filename = nullptr;
-    uint32_t lineNo = 0, dummyColumn = 0;
-    handler->GetLocation(&filename, &lineNo, &dummyColumn);
-
-    // New script entry point required, due to the "Create a script" sub-step of
-    // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
-    nsAutoMicroTask mt;
-    AutoEntryScript aes(this, reason, true);
-    JS::CompileOptions options(aes.cx());
-    options.setFileAndLine(filename, lineNo)
-           .setVersion(JSVERSION_DEFAULT);
-    JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
-    nsresult rv = nsJSUtils::EvaluateString(aes.cx(), script, global, options);
-    if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
-      abortIntervalHandler = true;
+  nsCOMPtr<nsITimeoutHandler> basicHandler(timeout->mScriptHandler);
+  nsCOMPtr<nsIScriptTimeoutHandler> handler(do_QueryInterface(basicHandler));
+  if (handler) {
+    RefPtr<Function> callback = handler->GetCallback();
+
+    if (!callback) {
+      // Evaluate the timeout expression.
+      const nsAString& script = handler->GetHandlerText();
+
+      const char* filename = nullptr;
+      uint32_t lineNo = 0, dummyColumn = 0;
+      handler->GetLocation(&filename, &lineNo, &dummyColumn);
+
+      // New script entry point required, due to the "Create a script" sub-step of
+      // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
+      nsAutoMicroTask mt;
+      AutoEntryScript aes(this, reason, true);
+      JS::CompileOptions options(aes.cx());
+      options.setFileAndLine(filename, lineNo).setVersion(JSVERSION_DEFAULT);
+      JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
+      nsresult rv =
+        nsJSUtils::EvaluateString(aes.cx(), script, global, options);
+      if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
+        abortIntervalHandler = true;
+      }
+    } else {
+      // Hold strong ref to ourselves while we call the callback.
+      nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow*>(this));
+      ErrorResult rv;
+      JS::Rooted<JS::Value> ignoredVal(RootingCx());
+      callback->Call(me, handler->GetArgs(), &ignoredVal, rv, reason);
+      if (rv.IsUncatchableException()) {
+        abortIntervalHandler = true;
+      }
+
+      rv.SuppressException();
     }
   } else {
-    // Hold strong ref to ourselves while we call the callback.
-    nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
-    ErrorResult rv;
-    JS::Rooted<JS::Value> ignoredVal(RootingCx());
-    callback->Call(me, handler->GetArgs(), &ignoredVal, rv, reason);
-    if (rv.IsUncatchableException()) {
-      abortIntervalHandler = true;
-    }
-
-    rv.SuppressException();
+    nsCOMPtr<nsISupports> kungFuDeathGrip(static_cast<nsIDOMWindow*>(this));
+    basicHandler->Call();
   }
 
   // If we received an uncatchable exception, do not schedule the timeout again.
   // This allows the slow script dialog to break easy DoS attacks like
   // setInterval(function() { while(1); }, 100);
   if (abortIntervalHandler) {
     // If it wasn't an interval timer to begin with, this does nothing.  If it
     // was, we'll treat it as a timeout that we just ran and discard it when
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -81,16 +81,17 @@ class nsIContent;
 class nsICSSDeclaration;
 class nsIDocShellTreeOwner;
 class nsIDOMOfflineResourceList;
 class nsIScrollableFrame;
 class nsIControllers;
 class nsIJSID;
 class nsIScriptContext;
 class nsIScriptTimeoutHandler;
+class nsITimeoutHandler;
 class nsIWebBrowserChrome;
 class mozIDOMWindowProxy;
 
 class nsDOMWindowList;
 class nsScreen;
 class nsHistory;
 class nsGlobalWindowObserver;
 class nsGlobalWindow;
@@ -1443,17 +1444,17 @@ private:
 
   void FreezeInternal();
   void ThawInternal();
 
 public:
   // Timeout Functions
   // Language agnostic timeout function (all args passed).
   // |interval| is in milliseconds.
-  nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler* aHandler,
+  nsresult SetTimeoutOrInterval(nsITimeoutHandler* aHandler,
                                 int32_t interval, bool aIsInterval,
                                 int32_t* aReturn);
   int32_t SetTimeoutOrInterval(JSContext* aCx,
                                mozilla::dom::Function& aFunction,
                                int32_t aTimeout,
                                const mozilla::dom::Sequence<JS::Value>& aArguments,
                                bool aIsInterval, mozilla::ErrorResult& aError);
   int32_t SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
--- a/dom/base/nsIScriptTimeoutHandler.h
+++ b/dom/base/nsIScriptTimeoutHandler.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsIScriptTimeoutHandler_h___
 #define nsIScriptTimeoutHandler_h___
 
+#include "nsITimeoutHandler.h"
 #include "nsTArray.h"
 #include "js/TypeDecls.h"
 #include "mozilla/Function.h"
 #include "mozilla/Maybe.h"
 
 namespace mozilla {
 namespace dom {
 class Function;
@@ -21,33 +22,31 @@ class Function;
 { 0x53c8e80e, 0xcc78, 0x48bc, \
  { 0xba, 0x63, 0x0c, 0xb9, 0xdb, 0xf7, 0x06, 0x34 } }
 
 /**
  * Abstraction of the script objects etc required to do timeouts in a
  * language agnostic way.
  */
 
-class nsIScriptTimeoutHandler : public nsISupports
+class nsIScriptTimeoutHandler : public nsITimeoutHandler
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTTIMEOUTHANDLER_IID)
 
   // Get the Function to call.  If this returns nullptr, GetHandlerText() will
   // be called to get the string.
   virtual mozilla::dom::Function* GetCallback() = 0;
 
   // Get the handler text of not a compiled object.
   virtual const nsAString& GetHandlerText() = 0;
 
   // Get the location of the script.
   // Note: The memory pointed to by aFileName is owned by the
   // nsIScriptTimeoutHandler and should not be freed by the caller.
-  virtual void GetLocation(const char **aFileName, uint32_t *aLineNo,
-                           uint32_t *aColumn) = 0;
 
   // If we have a Function, get the arguments for passing to it.
   virtual const nsTArray<JS::Value>& GetArgs() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptTimeoutHandler,
                               NS_ISCRIPTTIMEOUTHANDLER_IID)
 
new file mode 100644
--- /dev/null
+++ b/dom/base/nsITimeoutHandler.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsITimeoutHandler_h___
+#define nsITimeoutHandler_h___
+
+#include "nsISupports.h"
+
+#define NS_ITIMEOUTHANDLER_IID \
+{ 0xb071a1d3, 0xfd54, 0x40a8,  \
+ { 0x91, 0x9f, 0xc8, 0xf3, 0x3e, 0xb8, 0x3c, 0xfe } }
+
+class nsITimeoutHandler : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITIMEOUTHANDLER_IID)
+
+  virtual nsresult Call() = 0;
+
+  virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
+                           uint32_t* aColumn) = 0;
+
+  virtual void MarkForCC() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsITimeoutHandler,
+                              NS_ITIMEOUTHANDLER_IID)
+
+#endif // nsITimeoutHandler_h___
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -55,27 +55,39 @@ public:
 
   virtual const nsAString& GetHandlerText() override;
 
   virtual Function* GetCallback() override
   {
     return mFunction;
   }
 
+  virtual const nsTArray<JS::Value>& GetArgs() override
+  {
+    return mArgs;
+  }
+
+  virtual nsresult Call() override
+  {
+    return NS_OK;
+  }
+
   virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
                            uint32_t* aColumn) override
   {
     *aFileName = mFileName.get();
     *aLineNo = mLineNo;
     *aColumn = mColumn;
   }
 
-  virtual const nsTArray<JS::Value>& GetArgs() override
+  virtual void MarkForCC() override
   {
-    return mArgs;
+    if (mFunction) {
+      mFunction->MarkForCC();
+    }
   }
 
   void ReleaseJSObjects();
 
 private:
   ~nsJSScriptTimeoutHandler();
 
   void Init(JSContext* aCx,
@@ -146,16 +158,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
   for (uint32_t i = 0; i < tmp->mArgs.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgs[i])
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler)
   NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler)
+  NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler)
 
 static bool
 CheckCSPForEval(JSContext* aCx, nsGlobalWindow* aWindow, ErrorResult& aError)