diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 5b89df5116..d2e1af36b3 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -822,6 +822,8 @@ nsDocShell::nsDocShell() , mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE) , mStateFloodGuardCount(0) , mStateFloodGuardReported(false) + , mReloadFloodGuardCount(0) + , mReloadFloodGuardReported(false) { AssertOriginAttributesMatchPrivateBrowsing(); mHistoryID = ++gDocshellIDCounter; @@ -5329,6 +5331,25 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL, nullptr); } +bool +nsDocShell::IsReloadFlooding() +{ + if (mReloadFloodGuardCount > kReloadLimit) { + TimeStamp now = TimeStamp::Now(); + + if (now - mReloadFloodGuardUpdated > TimeDuration::FromSeconds(kReloadTimeSecs)) { + mReloadFloodGuardCount = 0; + mReloadFloodGuardUpdated = now; + mReloadFloodGuardReported = false; + return false; + } + return true; + } + + mReloadFloodGuardCount++; + return false; +} + NS_IMETHODIMP nsDocShell::Reload(uint32_t aReloadFlags) { @@ -5354,6 +5375,26 @@ nsDocShell::Reload(uint32_t aReloadFlags) shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload); } + // If we're being flooded with reload requests, we should abort early + // from the reload logic. + if (IsReloadFlooding()) { + // Report a warning to the console to tell developers why their reload + // failed. + // Do this only if not yet marked reported so we only report it once per + // flood interval. + if (!mReloadFloodGuardReported) { +#if 0 + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("Reload"), + GetDocument(), + nsContentUtils::eDOM_PROPERTIES, + "ReloadFloodingPrevented"); +#endif + mReloadFloodGuardReported = true; + } + return NS_OK; + } + if (!canReload) { return NS_OK; } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 019a7e4ab6..396996ea74 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -1058,6 +1058,15 @@ private: const int32_t kStateUpdateLimit = 50; const double kRefreshTimeSecs = 10.0; + // Keep track how how many history state changes we're getting, to catch & + // prevent flooding. + int32_t mReloadFloodGuardCount; + mozilla::TimeStamp mReloadFloodGuardUpdated; + bool mReloadFloodGuardReported; + // We have a limit of reloading 50 times every 10 seconds. + const int32_t kReloadLimit = 50; + const double kReloadTimeSecs = 10.0; + // Separate function to do the actual name (i.e. not _top, _self etc.) // searching for FindItemWithName. nsresult DoFindItemWithName(const nsAString& aName, @@ -1077,6 +1086,9 @@ private: // ReplaceState. bool IsStateChangeFlooding(); + // Helper method for Reload which checks for excessive calls to Reload. + bool IsReloadFlooding(); + #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 1f43a4389f..60104a63ad 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -318,3 +318,5 @@ LargeAllocationInIFrame=A Large-Allocation header was ignored due to the load oc 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. +# LOCALIZATION NOTE: Do not translate "Reload" +ReloadFloodingPrevented=Call to Reload ignored due to excessive calls within a short timeframe.