forked from mirrors/gecko-dev
Bug 1913000 - Add more rate limiting to History interface. r=emilio a=RyanVM
Original Revision: https://phabricator.services.mozilla.com/D221255 Differential Revision: https://phabricator.services.mozilla.com/D224233
This commit is contained in:
parent
fd7c0e3c40
commit
73a06167f9
14 changed files with 48 additions and 35 deletions
|
|
@ -3772,7 +3772,7 @@ bool BrowsingContext::ShouldUpdateSessionHistory(uint32_t aLoadType) {
|
|||
(IsForceReloadType(aLoadType) && IsSubframe()));
|
||||
}
|
||||
|
||||
nsresult BrowsingContext::CheckLocationChangeRateLimit(CallerType aCallerType) {
|
||||
nsresult BrowsingContext::CheckNavigationRateLimit(CallerType aCallerType) {
|
||||
// We only rate limit non system callers
|
||||
if (aCallerType == CallerType::System) {
|
||||
return NS_OK;
|
||||
|
|
@ -3780,9 +3780,9 @@ nsresult BrowsingContext::CheckLocationChangeRateLimit(CallerType aCallerType) {
|
|||
|
||||
// Fetch rate limiting preferences
|
||||
uint32_t limitCount =
|
||||
StaticPrefs::dom_navigation_locationChangeRateLimit_count();
|
||||
StaticPrefs::dom_navigation_navigationRateLimit_count();
|
||||
uint32_t timeSpanSeconds =
|
||||
StaticPrefs::dom_navigation_locationChangeRateLimit_timespan();
|
||||
StaticPrefs::dom_navigation_navigationRateLimit_timespan();
|
||||
|
||||
// Disable throttling if either of the preferences is set to 0.
|
||||
if (limitCount == 0 || timeSpanSeconds == 0) {
|
||||
|
|
@ -3791,15 +3791,15 @@ nsresult BrowsingContext::CheckLocationChangeRateLimit(CallerType aCallerType) {
|
|||
|
||||
TimeDuration throttleSpan = TimeDuration::FromSeconds(timeSpanSeconds);
|
||||
|
||||
if (mLocationChangeRateLimitSpanStart.IsNull() ||
|
||||
((TimeStamp::Now() - mLocationChangeRateLimitSpanStart) > throttleSpan)) {
|
||||
if (mNavigationRateLimitSpanStart.IsNull() ||
|
||||
((TimeStamp::Now() - mNavigationRateLimitSpanStart) > throttleSpan)) {
|
||||
// Initial call or timespan exceeded, reset counter and timespan.
|
||||
mLocationChangeRateLimitSpanStart = TimeStamp::Now();
|
||||
mLocationChangeRateLimitCount = 1;
|
||||
mNavigationRateLimitSpanStart = TimeStamp::Now();
|
||||
mNavigationRateLimitCount = 1;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mLocationChangeRateLimitCount >= limitCount) {
|
||||
if (mNavigationRateLimitCount >= limitCount) {
|
||||
// Rate limit reached
|
||||
|
||||
Document* doc = GetDocument();
|
||||
|
|
@ -3812,14 +3812,14 @@ nsresult BrowsingContext::CheckLocationChangeRateLimit(CallerType aCallerType) {
|
|||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
mLocationChangeRateLimitCount++;
|
||||
mNavigationRateLimitCount++;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void BrowsingContext::ResetLocationChangeRateLimit() {
|
||||
void BrowsingContext::ResetNavigationRateLimit() {
|
||||
// Resetting the timestamp object will cause the check function to
|
||||
// init again and reset the rate limit.
|
||||
mLocationChangeRateLimitSpanStart = TimeStamp();
|
||||
mNavigationRateLimitSpanStart = TimeStamp();
|
||||
}
|
||||
|
||||
void BrowsingContext::LocationCreated(dom::Location* aLocation) {
|
||||
|
|
|
|||
|
|
@ -898,13 +898,13 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
|
|||
|
||||
// Checks if we reached the rate limit for calls to Location and History API.
|
||||
// The rate limit is controlled by the
|
||||
// "dom.navigation.locationChangeRateLimit" prefs.
|
||||
// "dom.navigation.navigationRateLimit" prefs.
|
||||
// Rate limit applies per BrowsingContext.
|
||||
// Returns NS_OK if we are below the rate limit and increments the counter.
|
||||
// Returns NS_ERROR_DOM_SECURITY_ERR if limit is reached.
|
||||
nsresult CheckLocationChangeRateLimit(CallerType aCallerType);
|
||||
nsresult CheckNavigationRateLimit(CallerType aCallerType);
|
||||
|
||||
void ResetLocationChangeRateLimit();
|
||||
void ResetNavigationRateLimit();
|
||||
|
||||
mozilla::dom::DisplayMode DisplayMode() { return Top()->GetDisplayMode(); }
|
||||
|
||||
|
|
@ -1432,9 +1432,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
|
|||
nsTArray<std::function<void(uint64_t)>> mDiscardListeners;
|
||||
|
||||
// Counter and time span for rate limiting Location and History API calls.
|
||||
// Used by CheckLocationChangeRateLimit. Do not apply cross-process.
|
||||
uint32_t mLocationChangeRateLimitCount;
|
||||
mozilla::TimeStamp mLocationChangeRateLimitSpanStart;
|
||||
// Used by CheckNavigationRateLimit. Do not apply cross-process.
|
||||
uint32_t mNavigationRateLimitCount;
|
||||
mozilla::TimeStamp mNavigationRateLimitSpanStart;
|
||||
|
||||
mozilla::LinkedList<dom::Location> mLocations;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ void ChildSHistory::AsyncGo(int32_t aOffset, bool aRequireUserInteraction,
|
|||
MOZ_LOG(gSHLog, LogLevel::Debug,
|
||||
("ChildSHistory::AsyncGo(%d), current index = %d", aOffset,
|
||||
index.value()));
|
||||
nsresult rv = mBrowsingContext->CheckLocationChangeRateLimit(aCallerType);
|
||||
nsresult rv = mBrowsingContext->CheckNavigationRateLimit(aCallerType);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gSHLog, LogLevel::Debug, ("Rejected"));
|
||||
aRv.Throw(rv);
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1314912
|
|||
|
||||
async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
["dom.navigation.locationChangeRateLimit.count", RATE_LIMIT_COUNT],
|
||||
["dom.navigation.locationChangeRateLimit.timespan", RATE_LIMIT_TIME_SPAN]]});
|
||||
["dom.navigation.navigationRateLimit.count", RATE_LIMIT_COUNT],
|
||||
["dom.navigation.navigationRateLimit.timespan", RATE_LIMIT_TIME_SPAN]]});
|
||||
}
|
||||
|
||||
let inc = 0;
|
||||
|
|
@ -26,6 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1314912
|
|||
const rateLimitedFunctions = (win) => ({
|
||||
"history.replaceState": () => win.history.replaceState(null, "test", `${win.location.href}#${inc++}`),
|
||||
"history.pushState": () => win.history.pushState(null, "test", `${win.location.href}#${inc++}`),
|
||||
"history.SetScrollRestoration": () => win.history.scrollRestoration = "auto",
|
||||
"history.back": () => win.history.back(),
|
||||
"history.forward": () => win.history.forward(),
|
||||
"history.go": () => win.history.go(-1),
|
||||
|
|
@ -53,7 +54,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1314912
|
|||
Object.entries(rateLimitedFunctions(win)).forEach(([name, fn]) => {
|
||||
// Reset the rate limit for the next run.
|
||||
info("Reset rate limit.");
|
||||
SpecialPowers.wrap(win).browsingContext.resetLocationChangeRateLimit();
|
||||
SpecialPowers.wrap(win).browsingContext.resetNavigationRateLimit();
|
||||
|
||||
info(`Calling ${name} ${RATE_LIMIT_COUNT} times to reach the rate limit.`);
|
||||
for(let i = 0; i< RATE_LIMIT_COUNT; i++) {
|
||||
|
|
@ -83,7 +84,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1314912
|
|||
|
||||
// Cleanup
|
||||
win.close();
|
||||
SpecialPowers.wrap(win).browsingContext.resetLocationChangeRateLimit();
|
||||
SpecialPowers.wrap(win).browsingContext.resetNavigationRateLimit();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -562,7 +562,7 @@ void Location::Reload(bool aForceget, nsIPrincipal& aSubjectPrincipal,
|
|||
? CallerType::System
|
||||
: CallerType::NonSystem;
|
||||
|
||||
nsresult rv = bc->CheckLocationChangeRateLimit(callerType);
|
||||
nsresult rv = bc->CheckNavigationRateLimit(callerType);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ void LocationBase::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
|
|||
? CallerType::System
|
||||
: CallerType::NonSystem;
|
||||
|
||||
nsresult rv = bc->CheckLocationChangeRateLimit(callerType);
|
||||
nsresult rv = bc->CheckNavigationRateLimit(callerType);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ uint32_t nsHistory::GetLength(ErrorResult& aRv) const {
|
|||
return len >= 0 ? len : 0;
|
||||
}
|
||||
|
||||
ScrollRestoration nsHistory::GetScrollRestoration(mozilla::ErrorResult& aRv) {
|
||||
ScrollRestoration nsHistory::GetScrollRestoration(mozilla::dom::CallerType aCallerType, mozilla::ErrorResult& aRv) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
|
||||
if (!win || !win->HasActiveDocument() || !win->GetDocShell()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
|
|
@ -88,6 +88,7 @@ ScrollRestoration nsHistory::GetScrollRestoration(mozilla::ErrorResult& aRv) {
|
|||
}
|
||||
|
||||
void nsHistory::SetScrollRestoration(mozilla::dom::ScrollRestoration aMode,
|
||||
mozilla::dom::CallerType aCallerType,
|
||||
mozilla::ErrorResult& aRv) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
|
||||
if (!win || !win->HasActiveDocument() || !win->GetDocShell()) {
|
||||
|
|
@ -95,6 +96,15 @@ void nsHistory::SetScrollRestoration(mozilla::dom::ScrollRestoration aMode,
|
|||
return;
|
||||
}
|
||||
|
||||
BrowsingContext* bc = win->GetBrowsingContext();
|
||||
if (bc) {
|
||||
nsresult rv = bc->CheckNavigationRateLimit(aCallerType);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
win->GetDocShell()->SetCurrentScrollRestorationIsManual(
|
||||
aMode == mozilla::dom::ScrollRestoration::Manual);
|
||||
}
|
||||
|
|
@ -152,7 +162,7 @@ void nsHistory::Go(int32_t aDelta, nsIPrincipal& aSubjectPrincipal,
|
|||
? CallerType::System
|
||||
: CallerType::NonSystem;
|
||||
|
||||
// AsyncGo throws if we hit the location change rate limit.
|
||||
// AsyncGo throws if we hit the navigation rate limit.
|
||||
session_history->AsyncGo(aDelta, /* aRequireUserInteraction = */ false,
|
||||
userActivation, callerType, aRv);
|
||||
}
|
||||
|
|
@ -237,7 +247,7 @@ void nsHistory::PushOrReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData,
|
|||
|
||||
BrowsingContext* bc = win->GetBrowsingContext();
|
||||
if (bc) {
|
||||
nsresult rv = bc->CheckLocationChangeRateLimit(aCallerType);
|
||||
nsresult rv = bc->CheckNavigationRateLimit(aCallerType);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -40,8 +40,10 @@ class nsHistory final : public nsISupports, public nsWrapperCache {
|
|||
|
||||
uint32_t GetLength(mozilla::ErrorResult& aRv) const;
|
||||
mozilla::dom::ScrollRestoration GetScrollRestoration(
|
||||
mozilla::dom::CallerType aCallerType,
|
||||
mozilla::ErrorResult& aRv);
|
||||
void SetScrollRestoration(mozilla::dom::ScrollRestoration aMode,
|
||||
mozilla::dom::CallerType aCallerType,
|
||||
mozilla::ErrorResult& aRv);
|
||||
void GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
|
||||
mozilla::ErrorResult& aRv) const;
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ interface BrowsingContext {
|
|||
readonly attribute ChildSHistory? childSessionHistory;
|
||||
|
||||
// Resets the location change rate limit. Used for testing.
|
||||
undefined resetLocationChangeRateLimit();
|
||||
undefined resetNavigationRateLimit();
|
||||
|
||||
readonly attribute long childOffset;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ enum ScrollRestoration { "auto", "manual" };
|
|||
interface History {
|
||||
[Throws]
|
||||
readonly attribute unsigned long length;
|
||||
[Throws]
|
||||
[Throws, NeedsCallerType]
|
||||
attribute ScrollRestoration scrollRestoration;
|
||||
[Throws]
|
||||
readonly attribute any state;
|
||||
|
|
|
|||
|
|
@ -3165,13 +3165,13 @@
|
|||
|
||||
# Limit of location change caused by content scripts in a time span per
|
||||
# BrowsingContext. This includes calls to History and Location APIs.
|
||||
- name: dom.navigation.locationChangeRateLimit.count
|
||||
- name: dom.navigation.navigationRateLimit.count
|
||||
type: uint32_t
|
||||
value: 200
|
||||
mirror: always
|
||||
|
||||
# Time span in seconds for location change rate limit.
|
||||
- name: dom.navigation.locationChangeRateLimit.timespan
|
||||
- name: dom.navigation.navigationRateLimit.timespan
|
||||
type: uint32_t
|
||||
value: 10
|
||||
mirror: always
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ const COMMON_PREFERENCES = new Map([
|
|||
["dom.max_script_run_time", 0],
|
||||
|
||||
// Disable location change rate limitation
|
||||
["dom.navigation.locationChangeRateLimit.count", 0],
|
||||
["dom.navigation.navigationRateLimit.count", 0],
|
||||
|
||||
// DOM Push
|
||||
["dom.push.connection.enabled", false],
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ class GeckoInstance(object):
|
|||
# No slow script dialogs
|
||||
"dom.max_chrome_script_run_time": 0,
|
||||
"dom.max_script_run_time": 0,
|
||||
# Disable location change rate limitation
|
||||
"dom.navigation.locationChangeRateLimit.count": 0,
|
||||
# Disable navigation change rate limitation
|
||||
"dom.navigation.navigationRateLimit.count": 0,
|
||||
# DOM Push
|
||||
"dom.push.connection.enabled": False,
|
||||
# Screen Orientation API
|
||||
|
|
|
|||
2
tools/@types/lib.gecko.dom.d.ts
vendored
2
tools/@types/lib.gecko.dom.d.ts
vendored
|
|
@ -5168,7 +5168,7 @@ interface BrowsingContext extends LoadContextMixin {
|
|||
watchedByDevTools: boolean;
|
||||
readonly window: WindowProxy | null;
|
||||
getAllBrowsingContextsInSubtree(): BrowsingContext[];
|
||||
resetLocationChangeRateLimit(): void;
|
||||
resetNavigationRateLimit(): void;
|
||||
setRDMPaneMaxTouchPoints(maxTouchPoints: number): void;
|
||||
setRDMPaneOrientation(type: OrientationType, rotationAngle: number): void;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue