summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2021-02-16 12:13:16 +0000
committerMoonchild <moonchild@palemoon.org>2021-02-16 12:13:16 +0000
commit2103a2283b68d0bc147905259f5d7ac94bce6f60 (patch)
treefbf66b790e1af8580d54a22473759ca514587482
parent8f663e3bcd14cb2a6a25771f51620233f276ec05 (diff)
downloadUXP-2103a2283b68d0bc147905259f5d7ac94bce6f60.tar
UXP-2103a2283b68d0bc147905259f5d7ac94bce6f60.tar.gz
UXP-2103a2283b68d0bc147905259f5d7ac94bce6f60.tar.lz
UXP-2103a2283b68d0bc147905259f5d7ac94bce6f60.tar.xz
UXP-2103a2283b68d0bc147905259f5d7ac94bce6f60.zip
Issue #1688 - Add flood guard to state change logic.
-rw-r--r--docshell/base/nsDocShell.cpp41
-rw-r--r--docshell/base/nsDocShell.h13
-rw-r--r--dom/locales/en-US/chrome/dom/dom.properties2
3 files changed, 56 insertions, 0 deletions
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index e1f023b10..0ceb70a30 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 93a1ba68d..019a7e4ab 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;
diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties
index 6bd3aac94..1f43a4389 100644
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -316,3 +316,5 @@ LargeAllocationRelatedBrowsingContexts=A Large-Allocation header was ignored due
LargeAllocationInIFrame=A Large-Allocation header was ignored due to the load occuring within an iframe.
# LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
LargeAllocationNonE10S=A Large-Allocation header was ignored due to the document not being loaded out of process.
+# LOCALIZATION NOTE: Do not translate "pushState" and "replaceState"
+PushStateFloodingPrevented=Call to pushState or replaceState ignored due to excessive calls within a short timeframe.