summaryrefslogtreecommitdiff
path: root/docshell
diff options
context:
space:
mode:
Diffstat (limited to 'docshell')
-rw-r--r--docshell/base/nsDocShell.cpp41
-rw-r--r--docshell/base/nsDocShell.h13
2 files changed, 54 insertions, 0 deletions
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index e1f023b105..0ceb70a304 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -820,6 +820,8 @@ nsDocShell::nsDocShell()
, mParentCharsetSource(0)
, mJSRunToCompletionDepth(0)
, mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE)
+ , mStateFloodGuardCount(0)
+ , mStateFloodGuardReported(false)
{
AssertOriginAttributesMatchPrivateBrowsing();
mHistoryID = ++gDocshellIDCounter;
@@ -11833,6 +11835,27 @@ nsDocShell::SetReferrerPolicy(uint32_t aReferrerPolicy)
// nsDocShell: Session History
//*****************************************************************************
+bool
+nsDocShell::IsStateChangeFlooding()
+{
+ // Issue #1688: Let's copy Firefox's strategy for state flooding here, so
+ // that our implementations are interoperable.
+ if (mStateFloodGuardCount > kStateUpdateLimit) {
+ TimeStamp now = TimeStamp::Now();
+
+ if (now - mStateFloodGuardUpdated > TimeDuration::FromSeconds(kRefreshTimeSecs)) {
+ mStateFloodGuardCount = 0;
+ mStateFloodGuardUpdated = now;
+ mStateFloodGuardReported = false;
+ return false;
+ }
+ return true;
+ }
+
+ mStateFloodGuardCount++;
+ return false;
+}
+
NS_IMETHODIMP
nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
const nsAString& aURL, bool aReplace, JSContext* aCx)
@@ -11897,6 +11920,24 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
nsCOMPtr<nsIDocument> document = GetDocument();
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+ // If we're being flooded with state change requests, we should abort early
+ // from the state change logic.
+ if (IsStateChangeFlooding()) {
+ // Report a warning to the console to tell developers why their navigations
+ // failed.
+ // Do this only if not yet marked reported so we only report it once per
+ // flood interval.
+ if (!mStateFloodGuardReported) {
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("PushState"),
+ document,
+ nsContentUtils::eDOM_PROPERTIES,
+ "PushStateFloodingPrevented");
+ mStateFloodGuardReported = true;
+ }
+ return NS_OK;
+ }
+
// Step A: Serialize aData using structured clone.
// https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
// step 5.
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index 93a1ba68df..019a7e4ab6 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -1049,6 +1049,15 @@ private:
// as constants in the nsIDocShell.idl file.
uint32_t mTouchEventsOverride;
+ // Keep track how how many history state changes we're getting, to catch &
+ // prevent flooding.
+ int32_t mStateFloodGuardCount;
+ mozilla::TimeStamp mStateFloodGuardUpdated;
+ bool mStateFloodGuardReported;
+ // We have a limit of pushing 50 states to history every 10 seconds.
+ const int32_t kStateUpdateLimit = 50;
+ const double kRefreshTimeSecs = 10.0;
+
// Separate function to do the actual name (i.e. not _top, _self etc.)
// searching for FindItemWithName.
nsresult DoFindItemWithName(const nsAString& aName,
@@ -1064,6 +1073,10 @@ private:
void MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
const nsString& aKeyword);
+ // Helper method for AddState which checks for excessive calls to PushState or
+ // ReplaceState.
+ bool IsStateChangeFlooding();
+
#ifdef DEBUG
// We're counting the number of |nsDocShells| to help find leaks
static unsigned long gNumberOfDocShells;