Commit 4d4ddb03 authored by Sebastian Kaspari's avatar Sebastian Kaspari
Browse files

(Merge day) browser-engine-gecko-beta (80) -> browser-engine-gecko (80)

parent da914b23
......@@ -16,7 +16,7 @@ internal object GeckoVersions {
/**
* GeckoView Release Version.
*/
const val release_version = "79.0.20200813192915"
const val release_version = "80.0.20200818235255"
}
@Suppress("Unused", "MaxLineLength")
......
......@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package mozilla.components.lib.fetch.geckoview
package mozilla.components.browser.engine.gecko.fetch.geckoview
import androidx.test.annotation.UiThreadTest
import androidx.test.core.app.ApplicationProvider
......@@ -10,7 +10,6 @@ import androidx.test.filters.MediumTest
import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
import mozilla.components.concept.fetch.Client
import org.junit.Assert.assertTrue
import org.junit.Ignore
import org.junit.Test
@MediumTest
......@@ -110,7 +109,6 @@ class GeckoViewFetchTestCases : mozilla.components.tooling.fetch.tests.FetchTest
@Test
@UiThreadTest
@Ignore("https://bugzilla.mozilla.org/show_bug.cgi?id=1626335")
override fun get200WithCookiePolicy() {
super.get200WithCookiePolicy()
}
......
......@@ -7,9 +7,13 @@ package mozilla.components.browser.engine.gecko
import android.content.Context
import android.util.AttributeSet
import androidx.annotation.VisibleForTesting
import mozilla.components.browser.engine.gecko.ext.getAntiTrackingPolicy
import mozilla.components.browser.engine.gecko.ext.getEtpLevel
import mozilla.components.browser.engine.gecko.ext.getStrictSocialTrackingProtection
import mozilla.components.browser.engine.gecko.integration.LocaleSettingUpdater
import mozilla.components.browser.engine.gecko.mediaquery.from
import mozilla.components.browser.engine.gecko.mediaquery.toGeckoValue
import mozilla.components.browser.engine.gecko.profiler.Profiler
import mozilla.components.browser.engine.gecko.webextension.GeckoWebExtension
import mozilla.components.browser.engine.gecko.webnotifications.GeckoWebNotificationDelegate
import mozilla.components.browser.engine.gecko.webpush.GeckoWebPushDelegate
......@@ -27,7 +31,6 @@ import mozilla.components.concept.engine.content.blocking.TrackerLog
import mozilla.components.concept.engine.content.blocking.TrackingProtectionExceptionStorage
import mozilla.components.concept.engine.history.HistoryTrackingDelegate
import mozilla.components.concept.engine.mediaquery.PreferredColorScheme
import mozilla.components.concept.engine.profiler.Profiler
import mozilla.components.concept.engine.utils.EngineVersion
import mozilla.components.concept.engine.webextension.Action
import mozilla.components.concept.engine.webextension.ActionHandler
......@@ -478,7 +481,7 @@ class GeckoEngine(
/**
* See [Engine.profiler].
*/
override val profiler: Profiler? = null
override val profiler: Profiler? = Profiler(runtime)
override fun name(): String = "Gecko"
......@@ -527,42 +530,29 @@ class GeckoEngine(
override var trackingProtectionPolicy: TrackingProtectionPolicy? = null
set(value) {
value?.let { policy ->
val activateStrictSocialTracking =
policy.strictSocialTrackingProtection ?: policy.trackingCategories.contains(
TrackingCategory.STRICT
)
val etpLevel =
when {
policy.trackingCategories.contains(TrackingCategory.NONE) ->
ContentBlocking.EtpLevel.NONE
else -> ContentBlocking.EtpLevel.STRICT
with(runtime.settings.contentBlocking) {
if (enhancedTrackingProtectionLevel != value.getEtpLevel()) {
enhancedTrackingProtectionLevel = value.getEtpLevel()
}
runtime.settings.contentBlocking.setEnhancedTrackingProtectionLevel(etpLevel)
runtime.settings.contentBlocking.setStrictSocialTrackingProtection(
activateStrictSocialTracking
)
runtime.settings.contentBlocking.setAntiTracking(policy.getAntiTrackingPolicy())
runtime.settings.contentBlocking.setCookieBehavior(policy.cookiePolicy.id)
if (strictSocialTrackingProtection != value.getStrictSocialTrackingProtection()) {
strictSocialTrackingProtection = policy.getStrictSocialTrackingProtection()
}
if (antiTrackingCategories != value.getAntiTrackingPolicy()) {
setAntiTracking(policy.getAntiTrackingPolicy())
}
if (cookieBehavior != value.cookiePolicy.id) {
cookieBehavior = value.cookiePolicy.id
}
}
defaultSettings?.trackingProtectionPolicy = value
field = value
}
}
private fun TrackingProtectionPolicy.getAntiTrackingPolicy(): Int {
/**
* The [TrackingProtectionPolicy.TrackingCategory.SCRIPTS_AND_SUB_RESOURCES] is an
* artificial category, created with the sole purpose of going around this bug
* https://bugzilla.mozilla.org/show_bug.cgi?id=1579264, for this reason we have to
* remove its value from the valid anti tracking categories, when is present.
*/
val total = trackingCategories.sumBy { it.id }
return if (contains(TrackingCategory.SCRIPTS_AND_SUB_RESOURCES)) {
total - TrackingProtectionPolicy.TrackingCategory.SCRIPTS_AND_SUB_RESOURCES.id
} else {
total
}
}
override var remoteDebuggingEnabled: Boolean
get() = runtime.settings.remoteDebuggingEnabled
set(value) { runtime.settings.remoteDebuggingEnabled = value }
......
......@@ -104,6 +104,24 @@ class GeckoEngineSession(
createGeckoSession(shouldOpen = openGeckoSession)
}
/**
* Represents a request to load a [url].
*
* @param url the url to load.
* @param parent the parent (referring) [EngineSession] i.e. the session that
* triggered creating this one.
* @param flags the [LoadUrlFlags] to use when loading the provided url.
* @param additionalHeaders the extra headers to use when loading the provided url.
**/
data class LoadRequest(
val url: String,
val parent: EngineSession?,
val flags: LoadUrlFlags,
val additionalHeaders: Map<String, String>?
)
@VisibleForTesting
internal var initialLoadRequest: LoadRequest? = null
/**
* See [EngineSession.loadUrl]
*/
......@@ -113,6 +131,9 @@ class GeckoEngineSession(
flags: LoadUrlFlags,
additionalHeaders: Map<String, String>?
) {
if (initialLoad) {
initialLoadRequest = LoadRequest(url, parent, flags, additionalHeaders)
}
geckoSession.loadUri(url, (parent as? GeckoEngineSession)?.geckoSession, flags.value, additionalHeaders)
}
......@@ -137,7 +158,12 @@ class GeckoEngineSession(
* See [EngineSession.reload]
*/
override fun reload(flags: LoadUrlFlags) {
geckoSession.reload(flags.value)
initialLoadRequest?.let {
// We have a pending initial load request, which means we never
// successfully loaded a page. Calling reload now would just reload
// about:blank. To prevent that we trigger the initial load again.
loadUrl(it.url, it.parent, it.flags, it.additionalHeaders)
} ?: geckoSession.reload(flags.value)
}
/**
......@@ -388,6 +414,8 @@ class GeckoEngineSession(
currentUrl = url
initialLoad = false
initialLoadRequest = null
isIgnoredForTrackingProtection { ignored ->
notifyObservers {
onExcludedOnTrackingProtectionChange(ignored)
......@@ -400,10 +428,6 @@ class GeckoEngineSession(
session: GeckoSession,
request: NavigationDelegate.LoadRequest
): GeckoResult<AllowOrDeny> {
if (request.target == NavigationDelegate.TARGET_WINDOW_NEW) {
return GeckoResult.fromValue(AllowOrDeny.ALLOW)
}
// The process switch involved when loading extension pages will
// trigger an initial load of about:blank which we want to
// avoid:
......@@ -413,18 +437,22 @@ class GeckoEngineSession(
initialLoad = true
}
return if (maybeInterceptRequest(request, false) != null) {
GeckoResult.fromValue(AllowOrDeny.DENY)
} else {
notifyObservers {
onLoadRequest(
url = request.uri,
triggeredByRedirect = request.isRedirect,
triggeredByWebContent = request.hasUserGesture
)
}
return when {
maybeInterceptRequest(request, false) != null ->
GeckoResult.fromValue(AllowOrDeny.DENY)
request.target == NavigationDelegate.TARGET_WINDOW_NEW ->
GeckoResult.fromValue(AllowOrDeny.ALLOW)
else -> {
notifyObservers {
onLoadRequest(
url = request.uri,
triggeredByRedirect = request.isRedirect,
triggeredByWebContent = request.hasUserGesture
)
}
GeckoResult.fromValue(AllowOrDeny.ALLOW)
GeckoResult.fromValue(AllowOrDeny.ALLOW)
}
}
}
......
......@@ -38,9 +38,14 @@ class GeckoEngineView @JvmOverloads constructor(
} catch (e: IllegalStateException) {
// This is to debug "display already acquired" crashes
val otherActivityClassName =
this.session?.accessibility?.view?.context?.javaClass?.simpleName
this.session?.accessibility?.view?.context?.javaClass?.simpleName
val otherActivityClassHashcode =
this.session?.accessibility?.view?.context?.hashCode()
val activityClassName = context.javaClass.simpleName
val msg = "ATTACH VIEW: Current activity: $activityClassName Other activity: $otherActivityClassName"
val activityClassHashCode = context.hashCode()
val msg = "ATTACH VIEW: Current activity: $activityClassName hashcode " +
"$activityClassHashCode Other activity: $otherActivityClassName " +
"hashcode $otherActivityClassHashcode"
throw IllegalStateException(msg, e)
}
}
......@@ -118,9 +123,13 @@ class GeckoEngineView @JvmOverloads constructor(
// This is to debug "display already acquired" crashes
val otherActivityClassName =
internalSession.geckoSession.accessibility.view?.context?.javaClass?.simpleName
val otherActivityClassHashcode =
internalSession.geckoSession.accessibility.view?.context?.hashCode()
val activityClassName = context.javaClass.simpleName
val msg =
"SET SESSION: Current activity: $activityClassName Other activity: $otherActivityClassName"
val activityClassHashCode = context.hashCode()
val msg = "SET SESSION: Current activity: $activityClassName hashcode " +
"$activityClassHashCode Other activity: $otherActivityClassName " +
"hashcode $otherActivityClassHashcode"
throw IllegalStateException(msg, e)
}
}
......
......@@ -20,10 +20,16 @@ fun TrackingProtectionPolicy.toContentBlockingSetting(
antiTracking(getAntiTrackingPolicy())
cookieBehavior(cookiePolicy.id)
safeBrowsing(safeBrowsingPolicy.sumBy { it.id })
// This will be fixed on merge day when strictSocialTrackingProtection will be available on beta
// strictSocialTrackingProtection(getStrictSocialTrackingProtection())
strictSocialTrackingProtection(getStrictSocialTrackingProtection())
}.build()
/**
* Returns whether [TrackingCategory.STRICT] is enabled in the [TrackingProtectionPolicy].
*/
internal fun TrackingProtectionPolicy.getStrictSocialTrackingProtection(): Boolean {
return strictSocialTrackingProtection ?: trackingCategories.contains(TrackingCategory.STRICT)
}
/**
* Returns the [TrackingProtectionPolicy] categories as an Enhanced Tracking Protection level for GeckoView.
*/
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package mozilla.components.browser.engine.gecko.profiler
import mozilla.components.concept.engine.profiler.Profiler
import org.mozilla.geckoview.GeckoRuntime
/**
* Gecko-based implementation of [Profiler], wrapping the
* ProfilerController object provided by GeckoView.
*/
class Profiler(
private val runtime: GeckoRuntime
) : Profiler {
/**
* See [Profiler.isProfilerActive].
*/
override fun isProfilerActive(): Boolean {
return runtime.profilerController.isProfilerActive
}
/**
* See [Profiler.getProfilerTime].
*/
override fun getProfilerTime(): Double? {
return runtime.profilerController.profilerTime
}
/**
* See [Profiler.addMarker].
*/
override fun addMarker(markerName: String, startTime: Double?, endTime: Double?, text: String?) {
runtime.profilerController.addMarker(markerName, startTime, endTime, text)
}
/**
* See [Profiler.addMarker].
*/
override fun addMarker(markerName: String, startTime: Double?, text: String?) {
runtime.profilerController.addMarker(markerName, startTime, text)
}
/**
* See [Profiler.addMarker].
*/
override fun addMarker(markerName: String, startTime: Double?) {
runtime.profilerController.addMarker(markerName, startTime)
}
/**
* See [Profiler.addMarker].
*/
override fun addMarker(markerName: String, text: String?) {
runtime.profilerController.addMarker(markerName, text)
}
/**
* See [Profiler.addMarker].
*/
override fun addMarker(markerName: String) {
runtime.profilerController.addMarker(markerName)
}
}
......@@ -44,7 +44,7 @@ open class GeckoSelectionActionDelegate(
val selectedText = mSelection?.text
val customActionIsAvailable = !selectedText.isNullOrEmpty() &&
customDelegate.isActionAvailable(id, selectedText)
customDelegate.isActionAvailable(id, selectedText)
return customActionIsAvailable ||
super.isActionAvailable(id)
......
......@@ -15,10 +15,10 @@ import mozilla.components.browser.errorpages.ErrorType
import mozilla.components.concept.engine.DefaultSettings
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineSession.LoadUrlFlags
import mozilla.components.concept.engine.EngineSession.SafeBrowsingPolicy
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.CookiePolicy
import mozilla.components.concept.engine.EngineSession.SafeBrowsingPolicy
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory
import mozilla.components.concept.engine.HitResult
import mozilla.components.concept.engine.UnsupportedSettingException
import mozilla.components.concept.engine.content.blocking.Tracker
......@@ -462,23 +462,27 @@ class GeckoEngineSessionTest {
@Test
fun reload() {
val engineSession = GeckoEngineSession(mock(),
geckoSessionProvider = geckoSessionProvider)
val engineSession = GeckoEngineSession(mock(), geckoSessionProvider = geckoSessionProvider)
engineSession.loadUrl("http://mozilla.org")
// Initial load is still in progress so reload should not be called.
// Instead we should have called loadUrl again to prevent reloading
// about:blank.
engineSession.reload()
verify(geckoSession, never()).reload(GeckoSession.LOAD_FLAGS_BYPASS_CACHE)
verify(geckoSession, times(2)).loadUri(
"http://mozilla.org",
null as GeckoSession?,
GeckoSession.LOAD_FLAGS_NONE,
null
)
// Subsequent reloads should simply call reload on the gecko session.
engineSession.initialLoadRequest = null
engineSession.reload()
verify(geckoSession).reload(GeckoSession.LOAD_FLAGS_NONE)
}
@Test
fun reloadBypassingCache() {
val engineSession = GeckoEngineSession(mock(),
geckoSessionProvider = geckoSessionProvider)
engineSession.loadUrl("http://mozilla.org")
engineSession.reload(flags = LoadUrlFlags.select(LoadUrlFlags.BYPASS_CACHE))
verify(geckoSession).reload(GeckoSession.LOAD_FLAGS_BYPASS_CACHE)
}
......@@ -640,7 +644,7 @@ class GeckoEngineSessionTest {
fun `keeps track of current url via onLocationChange events`() {
val mockedContentBlockingController = mock<ContentBlockingController>()
val engineSession = GeckoEngineSession(runtime, geckoSessionProvider = geckoSessionProvider)
val geckoResult = GeckoResult<Boolean?>()
var geckoResult = GeckoResult<Boolean?>()
whenever(runtime.contentBlockingController).thenReturn(mockedContentBlockingController)
whenever(mockedContentBlockingController.checkException(any())).thenReturn(geckoResult)
......@@ -661,7 +665,7 @@ class GeckoEngineSessionTest {
val engineSession = GeckoEngineSession(runtime, geckoSessionProvider = geckoSessionProvider,
context = coroutineContext)
val historyTrackingDelegate: HistoryTrackingDelegate = mock()
val geckoResult = GeckoResult<Boolean?>()
var geckoResult = GeckoResult<Boolean?>()
whenever(runtime.contentBlockingController).thenReturn(mockedContentBlockingController)
whenever(mockedContentBlockingController.checkException(any())).thenReturn(geckoResult)
......@@ -1954,15 +1958,202 @@ class GeckoEngineSessionTest {
}
@Test
fun `Handle new window load requests`() {
GeckoEngineSession(mock(), geckoSessionProvider = geckoSessionProvider)
fun `onLoadRequest will try to intercept new window load requests`() {
val engineSession = GeckoEngineSession(mock(),
geckoSessionProvider = geckoSessionProvider)
captureDelegates()
val result = navigationDelegate.value.onLoadRequest(geckoSession,
mockLoadRequest("sample:about", null, GeckoSession.NavigationDelegate.TARGET_WINDOW_NEW))
var observedUrl: String? = null
var observedIntent: Intent? = null
var observedLoadUrl: String? = null
var observedTriggeredByRedirect: Boolean? = null
var observedTriggeredByWebContent: Boolean? = null
engineSession.settings.requestInterceptor = object : RequestInterceptor {
override fun interceptsAppInitiatedRequests() = true
override fun onLoadRequest(
engineSession: EngineSession,
uri: String,
lastUri: String?,
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
else -> null
}
}
}
engineSession.register(object : EngineSession.Observer {
override fun onLaunchIntentRequest(
url: String,
appIntent: Intent?
) {
observedUrl = url
observedIntent = appIntent
}
override fun onLoadRequest(url: String, triggeredByRedirect: Boolean, triggeredByWebContent: Boolean) {
observedLoadUrl = url
observedTriggeredByRedirect = triggeredByRedirect
observedTriggeredByWebContent = triggeredByWebContent
}
})
var result = navigationDelegate.value.onLoadRequest(
mock(), mockLoadRequest("sample:about", null,
GeckoSession.NavigationDelegate.TARGET_WINDOW_NEW, triggeredByRedirect = true)
)
assertEquals(result!!.poll(0), AllowOrDeny.DENY)
assertNotNull(observedIntent)
assertEquals("result", observedUrl)
assertNull(observedLoadUrl)
assertNull(observedTriggeredByRedirect)
assertNull(observedTriggeredByWebContent)
result = navigationDelegate.value.onLoadRequest(
mock(), mockLoadRequest("sample:about", null,
GeckoSession.NavigationDelegate.TARGET_WINDOW_NEW, triggeredByRedirect = false)
)
assertEquals(result!!.poll(0), AllowOrDeny.DENY)
assertNotNull(observedIntent)
assertEquals("result", observedUrl)
assertNull(observedLoadUrl)
assertNull(observedTriggeredByRedirect)
assertNull(observedTriggeredByWebContent)
}
@Test
fun `onLoadRequest allows new window requests if not intercepted`() {
val engineSession = GeckoEngineSession(mock(),
geckoSessionProvider = geckoSessionProvider)
captureDelegates()
var observedUrl: String? = null
var observedIntent: Intent? = null
var observedLoadUrl: String? = null
var observedTriggeredByRedirect: Boolean? = null
var observedTriggeredByWebContent: Boolean? = null
engineSession.settings.requestInterceptor = object : RequestInterceptor {
override fun interceptsAppInitiatedRequests() = true
override fun onLoadRequest(
engineSession: EngineSession,
uri: String,
lastUri: String?,
hasUserGesture: Boolean,
isSameDomain: Boolean,
isRedirect: Boolean,
isDirectNavigation: Boolean,
isSubframeRequest: Boolean
): RequestInterceptor.InterceptionResponse? {
return when (uri) {
"sample:about" -> RequestInterceptor.InterceptionResponse.AppIntent(mock(), "result")
else -> null
}
}
}
engineSession.register(object : EngineSession.Observer {
override fun onLaunchIntentRequest(
url: String,
appIntent: Intent?
) {
observedUrl = url
observedIntent = appIntent
}
override fun onLoadRequest(url: String, triggeredByRedirect: Boolean, triggeredByWebContent: Boolean) {
observedLoadUrl = url
observedTriggeredByRedirect = triggeredByRedirect
observedTriggeredByWebContent = triggeredByWebContent
}
})