Bug 1356334: Part 3 - Enforce a stricter slow script timeout for extension content scripts. r?billm
MozReview-Commit-ID: LLvPQn1x1Xj
--- 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