Commit 390fbc7a authored by Roger Yang's avatar Roger Yang
Browse files

Closes #7710: Stop intercepting redirect with subframe load request without user action

parent ed00160a
......@@ -377,7 +377,7 @@ class GeckoEngineSession(
initialLoad = true
}
return if (maybeInterceptRequest(request) != null) {
return if (maybeInterceptRequest(request, false) != null) {
GeckoResult.fromValue(AllowOrDeny.DENY)
} else {
notifyObservers {
......@@ -400,7 +400,7 @@ class GeckoEngineSession(
return GeckoResult.fromValue(AllowOrDeny.ALLOW)
}
return if (maybeInterceptRequest(request) != null) {
return if (maybeInterceptRequest(request, true) != null) {
GeckoResult.fromValue(AllowOrDeny.DENY)
} else {
// Not notifying session observer because of performance concern and currently there
......@@ -450,7 +450,8 @@ class GeckoEngineSession(
}
private fun maybeInterceptRequest(
request: NavigationDelegate.LoadRequest
request: NavigationDelegate.LoadRequest,
isSubframeRequest: Boolean
): InterceptionResponse? {
val interceptor = settings.requestInterceptor
return if (
......@@ -464,7 +465,8 @@ class GeckoEngineSession(
request.hasUserGesture,
isSameDomain,
request.isRedirect,
request.isDirectNavigation
request.isDirectNavigation,
isSubframeRequest
)?.apply {
when (this) {
is InterceptionResponse.Content -> loadData(data, mimeType, encoding)
......
......@@ -639,7 +639,7 @@ class GeckoEngineSessionTest {
fun `keeps track of current url via onLocationChange events`() {
val mockedContentBlockingController = mock<ContentBlockingController>()
val engineSession = GeckoEngineSession(runtime, geckoSessionProvider = geckoSessionProvider)
var geckoResult = GeckoResult<Boolean?>()
val geckoResult = GeckoResult<Boolean?>()
whenever(runtime.contentBlockingController).thenReturn(mockedContentBlockingController)
whenever(mockedContentBlockingController.checkException(any())).thenReturn(geckoResult)
......@@ -660,7 +660,7 @@ class GeckoEngineSessionTest {
val engineSession = GeckoEngineSession(runtime, geckoSessionProvider = geckoSessionProvider,
context = coroutineContext)
val historyTrackingDelegate: HistoryTrackingDelegate = mock()
var geckoResult = GeckoResult<Boolean?>()
val geckoResult = GeckoResult<Boolean?>()
whenever(runtime.contentBlockingController).thenReturn(mockedContentBlockingController)
whenever(mockedContentBlockingController.checkException(any())).thenReturn(geckoResult)
......@@ -1270,7 +1270,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return RequestInterceptor.InterceptionResponse.Content("<h1>Hello World</h1>")
......@@ -1300,7 +1301,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return RequestInterceptor.InterceptionResponse.Url("https://mozilla.org")
......@@ -1335,7 +1337,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalled = true
return RequestInterceptor.InterceptionResponse.Url("https://mozilla.org")
......@@ -1380,7 +1383,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return null
......@@ -2047,7 +2051,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
......@@ -2088,6 +2093,7 @@ class GeckoEngineSessionTest {
var observedUrl: String? = null
var observedIntent: Intent? = null
var observedIsSubframe = false
engineSession.settings.requestInterceptor = object : RequestInterceptor {
override fun interceptsAppInitiatedRequests() = true
......@@ -2098,8 +2104,10 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
observedIsSubframe = isSubframeRequest
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
else -> null
......@@ -2122,12 +2130,14 @@ class GeckoEngineSessionTest {
assertNotNull(observedIntent)
assertEquals("result", observedUrl)
assertEquals(true, observedIsSubframe)
navigationDelegate.value.onSubframeLoadRequest(
mock(), mockLoadRequest("sample:about", triggeredByRedirect = false))
assertNotNull(observedIntent)
assertEquals("result", observedUrl)
assertEquals(true, observedIsSubframe)
}
@Test
......@@ -2152,7 +2162,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.Url("result")
......@@ -2275,7 +2286,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
fakeUrl -> null
......
......@@ -374,7 +374,7 @@ class GeckoEngineSession(
}
return when {
maybeInterceptRequest(request) != null ->
maybeInterceptRequest(request, false) != null ->
GeckoResult.fromValue(AllowOrDeny.DENY)
request.target == NavigationDelegate.TARGET_WINDOW_NEW ->
GeckoResult.fromValue(AllowOrDeny.ALLOW)
......@@ -400,7 +400,7 @@ class GeckoEngineSession(
return GeckoResult.fromValue(AllowOrDeny.ALLOW)
}
return if (maybeInterceptRequest(request) != null) {
return if (maybeInterceptRequest(request, true) != null) {
GeckoResult.fromValue(AllowOrDeny.DENY)
} else {
// Not notifying session observer because of performance concern and currently there
......@@ -450,7 +450,8 @@ class GeckoEngineSession(
}
private fun maybeInterceptRequest(
request: NavigationDelegate.LoadRequest
request: NavigationDelegate.LoadRequest,
isSubframeRequest: Boolean
): InterceptionResponse? {
val interceptor = settings.requestInterceptor
return if (
......@@ -464,7 +465,8 @@ class GeckoEngineSession(
request.hasUserGesture,
isSameDomain,
request.isRedirect,
request.isDirectNavigation
request.isDirectNavigation,
isSubframeRequest
)?.apply {
when (this) {
is InterceptionResponse.Content -> loadData(data, mimeType, encoding)
......
......@@ -1270,7 +1270,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return RequestInterceptor.InterceptionResponse.Content("<h1>Hello World</h1>")
......@@ -1300,7 +1301,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return RequestInterceptor.InterceptionResponse.Url("https://mozilla.org")
......@@ -1335,7 +1337,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalled = true
return RequestInterceptor.InterceptionResponse.Url("https://mozilla.org")
......@@ -1380,7 +1383,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return null
......@@ -1935,7 +1939,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
......@@ -2008,7 +2013,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
......@@ -2078,7 +2084,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
......@@ -2228,7 +2235,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
......@@ -2269,6 +2277,7 @@ class GeckoEngineSessionTest {
var observedUrl: String? = null
var observedIntent: Intent? = null
var observedIsSubframe = false
engineSession.settings.requestInterceptor = object : RequestInterceptor {
override fun interceptsAppInitiatedRequests() = true
......@@ -2279,8 +2288,10 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
observedIsSubframe = isSubframeRequest
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
else -> null
......@@ -2303,12 +2314,14 @@ class GeckoEngineSessionTest {
assertNotNull(observedIntent)
assertEquals("result", observedUrl)
assertEquals(true, observedIsSubframe)
navigationDelegate.value.onSubframeLoadRequest(
mock(), mockLoadRequest("sample:about", triggeredByRedirect = false))
assertNotNull(observedIntent)
assertEquals("result", observedUrl)
assertEquals(true, observedIsSubframe)
}
@Test
......@@ -2333,7 +2346,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.Url("result")
......@@ -2456,7 +2470,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
fakeUrl -> null
......
......@@ -377,7 +377,7 @@ class GeckoEngineSession(
initialLoad = true
}
return if (maybeInterceptRequest(request) != null) {
return if (maybeInterceptRequest(request, false) != null) {
GeckoResult.fromValue(AllowOrDeny.DENY)
} else {
notifyObservers {
......@@ -400,7 +400,7 @@ class GeckoEngineSession(
return GeckoResult.fromValue(AllowOrDeny.ALLOW)
}
return if (maybeInterceptRequest(request) != null) {
return if (maybeInterceptRequest(request, true) != null) {
GeckoResult.fromValue(AllowOrDeny.DENY)
} else {
// Not notifying session observer because of performance concern and currently there
......@@ -450,7 +450,8 @@ class GeckoEngineSession(
}
private fun maybeInterceptRequest(
request: NavigationDelegate.LoadRequest
request: NavigationDelegate.LoadRequest,
isSubframeRequest: Boolean
): InterceptionResponse? {
val interceptor = settings.requestInterceptor
return if (
......@@ -464,7 +465,8 @@ class GeckoEngineSession(
request.hasUserGesture,
isSameDomain,
request.isRedirect,
request.isDirectNavigation
request.isDirectNavigation,
isSubframeRequest
)?.apply {
when (this) {
is InterceptionResponse.Content -> loadData(data, mimeType, encoding)
......
......@@ -639,7 +639,7 @@ class GeckoEngineSessionTest {
fun `keeps track of current url via onLocationChange events`() {
val mockedContentBlockingController = mock<ContentBlockingController>()
val engineSession = GeckoEngineSession(runtime, geckoSessionProvider = geckoSessionProvider)
var geckoResult = GeckoResult<Boolean?>()
val geckoResult = GeckoResult<Boolean?>()
whenever(runtime.contentBlockingController).thenReturn(mockedContentBlockingController)
whenever(mockedContentBlockingController.checkException(any())).thenReturn(geckoResult)
......@@ -660,7 +660,7 @@ class GeckoEngineSessionTest {
val engineSession = GeckoEngineSession(runtime, geckoSessionProvider = geckoSessionProvider,
context = coroutineContext)
val historyTrackingDelegate: HistoryTrackingDelegate = mock()
var geckoResult = GeckoResult<Boolean?>()
val geckoResult = GeckoResult<Boolean?>()
whenever(runtime.contentBlockingController).thenReturn(mockedContentBlockingController)
whenever(mockedContentBlockingController.checkException(any())).thenReturn(geckoResult)
......@@ -1270,7 +1270,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return RequestInterceptor.InterceptionResponse.Content("<h1>Hello World</h1>")
......@@ -1300,7 +1301,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return RequestInterceptor.InterceptionResponse.Url("https://mozilla.org")
......@@ -1335,7 +1337,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalled = true
return RequestInterceptor.InterceptionResponse.Url("https://mozilla.org")
......@@ -1380,7 +1383,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return null
......@@ -2047,7 +2051,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
......@@ -2088,6 +2093,7 @@ class GeckoEngineSessionTest {
var observedUrl: String? = null
var observedIntent: Intent? = null
var observedIsSubframe = false
engineSession.settings.requestInterceptor = object : RequestInterceptor {
override fun interceptsAppInitiatedRequests() = true
......@@ -2098,8 +2104,10 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
observedIsSubframe = isSubframeRequest
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
else -> null
......@@ -2122,12 +2130,14 @@ class GeckoEngineSessionTest {
assertNotNull(observedIntent)
assertEquals("result", observedUrl)
assertEquals(true, observedIsSubframe)
navigationDelegate.value.onSubframeLoadRequest(
mock(), mockLoadRequest("sample:about", triggeredByRedirect = false))
assertNotNull(observedIntent)
assertEquals("result", observedUrl)
assertEquals(true, observedIsSubframe)
}
@Test
......@@ -2152,7 +2162,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.Url("result")
......@@ -2275,7 +2286,8 @@ class GeckoEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
fakeUrl -> null
......
......@@ -251,7 +251,8 @@ class SystemEngineView @JvmOverloads constructor(
request.hasGesture(),
session.currentUrl.tryGetHostFromUrl() == request.url.host,
isRedirect,
false
false,
request.isForMainFrame
)?.apply {
return when (this) {
is InterceptionResponse.Content ->
......
......@@ -460,7 +460,8 @@ class SystemEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return RequestInterceptor.InterceptionResponse.Content("<h1>Hello World</h1>")
......@@ -534,7 +535,8 @@ class SystemEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return RequestInterceptor.InterceptionResponse.Content("<h1>Hello World</h1>")
}
......@@ -568,7 +570,8 @@ class SystemEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return RequestInterceptor.InterceptionResponse.Url("https://mozilla.org")
......@@ -624,7 +627,8 @@ class SystemEngineSessionTest {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
interceptorCalledWithUri = uri
return null
......
......@@ -63,6 +63,7 @@ interface RequestInterceptor {
* @param isSameDomain If the request is the same domain as the current URL then true, else false.
* @param isRedirect If the request is due to redirect then true, else false.
* @param isDirectNavigation If the request is due to a direct navigation then true, else false.
* @param isSubframeRequest If the request is coming from a subframe then true, else false.
* @return An [InterceptionResponse] object containing alternative content
* or an alternative URL. Null if the original request should continue to
* be loaded.
......@@ -74,7 +75,8 @@ interface RequestInterceptor {
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): InterceptionResponse? = null
/**
......
......@@ -41,7 +41,7 @@ class RequestInterceptorTest {
fun `interceptor has default methods`() {
val engineSession = mock(EngineSession::class.java)
val interceptor = object : RequestInterceptor { }
interceptor.onLoadRequest(engineSession, "url", false, false, false, false)
interceptor.onLoadRequest(engineSession, "url", false, false, false, false, false)