Bug 1356334: Part 3 - Enforce a stricter slow script timeout for extension content scripts. r?billm draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 16 Apr 2017 13:41:45 -0700
changeset 563393 07f920d5b5fed22cdb5b7e8033b7c1c63b88696d
parent 563392 436bbaf99bd141258459c67a575aafe93e04341d
child 563394 2867085761edc4b55b23c96c402f55647ac90836
push id54281
push usermaglione.k@gmail.com
push dateSun, 16 Apr 2017 21:11:25 +0000
reviewersbillm
bugs1356334
milestone55.0a1
Bug 1356334: Part 3 - Enforce a stricter slow script timeout for extension content scripts. r?billm MozReview-Commit-ID: LLvPQn1x1Xj
js/xpconnect/src/XPCJSContext.cpp
modules/libpref/init/all.js
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -55,16 +55,17 @@
 #include "nsGlobalWindow.h"
 #include "nsAboutProtocolUtils.h"
 
 #include "GeckoProfiler.h"
 #include "nsIInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsJSPrincipals.h"
 #include "ExpandedPrincipal.h"
+#include "SystemPrincipal.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 #if defined(MOZ_JEMALLOC4)
 #include "mozmemory.h"
 #endif
@@ -1101,16 +1102,17 @@ class Watchdog
     bool mHibernating;
     bool mInitialized;
     bool mShuttingDown;
     mozilla::Atomic<int32_t> mMinScriptRunTimeSeconds;
 };
 
 #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
 #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
+#define PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT "dom.max_ext_content_script_run_time"
 
 class WatchdogManager : public nsIObserver
 {
   public:
 
     NS_DECL_ISUPPORTS
     explicit WatchdogManager(XPCJSContext* aContext) : mContext(aContext)
                                                      , mContextState(CONTEXT_INACTIVE)
@@ -1121,29 +1123,31 @@ class WatchdogManager : public nsIObserv
 
         // Enable the watchdog, if appropriate.
         RefreshWatchdog();
 
         // Register ourselves as an observer to get updates on the pref.
         mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog");
         mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
         mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
+        mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT);
     }
 
   protected:
 
     virtual ~WatchdogManager()
     {
         // Shutting down the watchdog requires context-switching to the watchdog
         // thread, which isn't great to do in a destructor. So we require
         // consumers to shut it down manually before releasing it.
         MOZ_ASSERT(!mWatchdog);
         mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog");
         mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
         mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
+        mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT);
     }
 
   public:
 
     NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
                        const char16_t* aData) override
     {
         RefreshWatchdog();
@@ -1211,17 +1215,20 @@ class WatchdogManager : public nsIObserv
 
         if (mWatchdog) {
             int32_t contentTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CONTENT, 10);
             if (contentTime <= 0)
                 contentTime = INT32_MAX;
             int32_t chromeTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CHROME, 20);
             if (chromeTime <= 0)
                 chromeTime = INT32_MAX;
-            mWatchdog->SetMinScriptRunTimeSeconds(std::min(contentTime, chromeTime));
+            int32_t extTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT, 5);
+            if (extTime <= 0)
+                extTime = INT32_MAX;
+            mWatchdog->SetMinScriptRunTimeSeconds(std::min(std::min(contentTime, chromeTime), extTime));
         }
     }
 
     void StartWatchdog()
     {
         MOZ_ASSERT(!mWatchdog);
         mWatchdog = new Watchdog(this);
         mWatchdog->Init();
@@ -1362,20 +1369,33 @@ XPCJSContext::InterruptCallback(JSContex
     // has finished bootstrapping. Avoid crashing in nsContentUtils below.
     if (!nsContentUtils::IsInitialized())
         return true;
 
     // This is at least the second interrupt callback we've received since
     // returning to the event loop. See how long it's been, and what the limit
     // is.
     TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint;
-    bool chrome = nsContentUtils::IsSystemCaller(cx);
-    const char* prefName = chrome ? PREF_MAX_SCRIPT_RUN_TIME_CHROME
-                                  : PREF_MAX_SCRIPT_RUN_TIME_CONTENT;
-    int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10);
+    int32_t limit;
+
+    nsString addonId;
+    const char* prefName;
+
+    auto principal = BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(cx));
+    bool chrome = principal->Is<SystemPrincipal>();
+    if (chrome) {
+        prefName = PREF_MAX_SCRIPT_RUN_TIME_CHROME;
+        limit = Preferences::GetInt(prefName, 20);
+    } else if (IsExtensionContentScript(principal, addonId)) {
+        prefName = PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT;
+        limit = Preferences::GetInt(prefName, 5);
+    } else {
+        prefName = PREF_MAX_SCRIPT_RUN_TIME_CONTENT;
+        limit = Preferences::GetInt(prefName, 10);
+    }
 
     // If there's no limit, or we're within the limit, let it go.
     if (limit == 0 || duration.ToSeconds() < limit / 2.0)
         return true;
 
     self->mSlowScriptActualWait += duration;
 
     // In order to guard against time changes or laptops going to sleep, we
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2971,16 +2971,17 @@ pref("viewmanager.do_doublebuffering", t
 pref("gestures.enable_single_finger_input", true);
 
 pref("editor.resizing.preserve_ratio",       true);
 pref("editor.positioning.offset",            0);
 
 pref("dom.use_watchdog", true);
 pref("dom.max_chrome_script_run_time", 20);
 pref("dom.max_script_run_time", 10);
+pref("dom.max_ext_content_script_run_time", 5);
 
 // Stop all scripts in a compartment when the "stop script" dialog is used.
 pref("dom.global_stop_script", true);
 
 // Time (milliseconds) between throttled idle callbacks.
 pref("dom.idle_period.throttled_length", 10000);
 
 // The amount of idle time (milliseconds) reserved for a long idle period