From 8dd16259287f58f9273002717ec4d27e97127719 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:43:14 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- mobile/android/geckoview/api.txt | 31 +--- mobile/android/geckoview/build.gradle | 16 +- .../optional-permission-all-urls/manifest.json | 13 ++ .../geckoview/test/GeckoSessionTestRuleTest.kt | 3 - .../org/mozilla/geckoview/test/GeckoViewTest.kt | 3 +- .../org/mozilla/geckoview/test/ScreenshotTest.kt | 26 +++- .../org/mozilla/geckoview/test/TelemetryTest.kt | 131 ---------------- .../org/mozilla/geckoview/test/TranslationsTest.kt | 115 ++++++++++++-- .../org/mozilla/geckoview/test/WebExtensionTest.kt | 102 +++++++++++- .../geckoview/test/rule/GeckoSessionTestRule.java | 6 - .../geckoview/test/util/RuntimeCreator.java | 50 ------ .../java/org/mozilla/geckoview/GeckoDisplay.java | 6 +- .../java/org/mozilla/geckoview/GeckoEditable.java | 6 +- .../mozilla/geckoview/GeckoRuntimeSettings.java | 26 ---- .../java/org/mozilla/geckoview/GeckoSession.java | 4 + .../mozilla/geckoview/GeckoSessionSettings.java | 18 +-- .../main/java/org/mozilla/geckoview/GeckoView.java | 10 ++ .../org/mozilla/geckoview/RuntimeTelemetry.java | 171 --------------------- .../java/org/mozilla/geckoview/ScreenLength.java | 4 +- .../java/org/mozilla/geckoview/SessionFinder.java | 4 +- .../mozilla/geckoview/TranslationsController.java | 38 ++++- .../java/org/mozilla/geckoview/WebExtension.java | 2 +- .../mozilla/geckoview/WebExtensionController.java | 26 +++- .../org/mozilla/geckoview/doc-files/CHANGELOG.md | 13 +- 24 files changed, 355 insertions(+), 469 deletions(-) create mode 100644 mobile/android/geckoview/src/androidTest/assets/web_extensions/optional-permission-all-urls/manifest.json delete mode 100644 mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TelemetryTest.kt delete mode 100644 mobile/android/geckoview/src/main/java/org/mozilla/geckoview/RuntimeTelemetry.java (limited to 'mobile/android/geckoview') diff --git a/mobile/android/geckoview/api.txt b/mobile/android/geckoview/api.txt index 658677a509..57c4d18d56 100644 --- a/mobile/android/geckoview/api.txt +++ b/mobile/android/geckoview/api.txt @@ -102,7 +102,6 @@ import org.mozilla.geckoview.OverscrollEdgeEffect; import org.mozilla.geckoview.PanZoomController; import org.mozilla.geckoview.ProfilerController; import org.mozilla.geckoview.RuntimeSettings; -import org.mozilla.geckoview.RuntimeTelemetry; import org.mozilla.geckoview.ScreenLength; import org.mozilla.geckoview.SessionAccessibility; import org.mozilla.geckoview.SessionFinder; @@ -886,7 +885,6 @@ package org.mozilla.geckoview { method public boolean getRemoteDebuggingEnabled(); method @Nullable public GeckoRuntime getRuntime(); method @Nullable public Rect getScreenSizeOverride(); - method @Nullable public RuntimeTelemetry.Delegate getTelemetryDelegate(); method public boolean getTranslationsOfferPopup(); method @NonNull public String getTrustedRecursiveResolverUri(); method public int getTrustedRecusiveResolverMode(); @@ -969,7 +967,6 @@ package org.mozilla.geckoview { method @NonNull public GeckoRuntimeSettings.Builder preferredColorScheme(int); method @NonNull public GeckoRuntimeSettings.Builder remoteDebuggingEnabled(boolean); method @NonNull public GeckoRuntimeSettings.Builder screenSizeOverride(int, int); - method @Deprecated @DeprecationSchedule(id="geckoview-gvst",version=127) @NonNull public GeckoRuntimeSettings.Builder telemetryDelegate(@NonNull RuntimeTelemetry.Delegate); method @NonNull public GeckoRuntimeSettings.Builder translationsOfferPopup(boolean); method @NonNull public GeckoRuntimeSettings.Builder trustedRecursiveResolverMode(int); method @NonNull public GeckoRuntimeSettings.Builder trustedRecursiveResolverUri(@NonNull String); @@ -1081,6 +1078,7 @@ package org.mozilla.geckoview { field public static final int FINDER_DISPLAY_DRAW_LINK_OUTLINE = 4; field public static final int FINDER_DISPLAY_HIGHLIGHT_ALL = 1; field public static final int FINDER_FIND_BACKWARDS = 1; + field public static final int FINDER_FIND_FORWARD = 0; field public static final int FINDER_FIND_LINKS_ONLY = 8; field public static final int FINDER_FIND_MATCH_CASE = 2; field public static final int FINDER_FIND_WHOLE_WORD = 4; @@ -2168,28 +2166,6 @@ package org.mozilla.geckoview { method @AnyThread @NonNull protected abstract Settings newSettings(@Nullable Settings); } - public final class RuntimeTelemetry { - ctor protected RuntimeTelemetry(); - } - - public static interface RuntimeTelemetry.Delegate { - method @AnyThread default public void onBooleanScalar(@NonNull RuntimeTelemetry.Metric); - method @AnyThread default public void onHistogram(@NonNull RuntimeTelemetry.Histogram); - method @AnyThread default public void onLongScalar(@NonNull RuntimeTelemetry.Metric); - method @AnyThread default public void onStringScalar(@NonNull RuntimeTelemetry.Metric); - } - - public static class RuntimeTelemetry.Histogram extends RuntimeTelemetry.Metric { - ctor protected Histogram(); - field public final boolean isCategorical; - } - - public static class RuntimeTelemetry.Metric { - ctor protected Metric(); - field @NonNull public final String name; - field @NonNull public final T value; - } - public class ScreenLength { method @AnyThread @NonNull public static ScreenLength bottom(); method @AnyThread @NonNull public static ScreenLength fromPixels(double); @@ -2396,9 +2372,11 @@ package org.mozilla.geckoview { } public static class TranslationsController.SessionTranslation.TranslationState { - ctor public TranslationState(@Nullable TranslationsController.SessionTranslation.TranslationPair, @Nullable String, @Nullable TranslationsController.SessionTranslation.DetectedLanguages, @NonNull Boolean); + ctor @Deprecated @DeprecationSchedule(version=130,id="translation-state-deprecated-constructor") public TranslationState(@Nullable TranslationsController.SessionTranslation.TranslationPair, @Nullable String, @Nullable TranslationsController.SessionTranslation.DetectedLanguages, @NonNull Boolean); + ctor public TranslationState(@Nullable TranslationsController.SessionTranslation.TranslationPair, @Nullable String, @Nullable TranslationsController.SessionTranslation.DetectedLanguages, @NonNull Boolean, @NonNull Boolean); field @Nullable public final TranslationsController.SessionTranslation.DetectedLanguages detectedLanguages; field @Nullable public final String error; + field @NonNull public final Boolean hasVisibleChange; field @NonNull public final Boolean isEngineReady; field @Nullable public final TranslationsController.SessionTranslation.TranslationPair requestedTranslationPair; } @@ -2792,6 +2770,7 @@ package org.mozilla.geckoview { method @UiThread default public void onInstallationFailed(@Nullable WebExtension, @NonNull WebExtension.InstallException); method @UiThread default public void onInstalled(@NonNull WebExtension); method @UiThread default public void onInstalling(@NonNull WebExtension); + method @UiThread default public void onOptionalPermissionsChanged(@NonNull WebExtension); method @UiThread default public void onReady(@NonNull WebExtension); method @UiThread default public void onUninstalled(@NonNull WebExtension); method @UiThread default public void onUninstalling(@NonNull WebExtension); diff --git a/mobile/android/geckoview/build.gradle b/mobile/android/geckoview/build.gradle index 040cd2fea8..8a8ba698ec 100644 --- a/mobile/android/geckoview/build.gradle +++ b/mobile/android/geckoview/build.gradle @@ -229,6 +229,8 @@ configurations { dependencies { implementation ComponentsDependencies.androidx_annotation + implementation ComponentsDependencies.androidx_collection + implementation ComponentsDependencies.androidx_core implementation "com.google.android.gms:play-services-fido:20.0.1" implementation "org.yaml:snakeyaml:2.2" @@ -384,20 +386,6 @@ android.libraryVariants.all { variant -> } } -android.libraryVariants.all { variant -> - // At this point, the Android-Gradle plugin has created all the Android - // tasks and configurations. This is the time for us to declare additional - // Glean files to package into AAR files. This packs `metrics.yaml` in the - // root of the AAR, sibling to `AndroidManifest.xml` and `classes.jar`. By - // default, consumers of the AAR will ignore this file, but consumers that - // look for it can find it (provided GeckoView is a `module()` dependency - // and not a `project()` dependency.) Under the hood this uses that the - // task provided by `packageLibraryProvider` task is a Maven `Zip` task, - // and we can simply extend its inputs. See - // https://android.googlesource.com/platform/tools/base/+/0cbe8846f7d02c0bb6f07156b9f4fde16d96d329/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/BundleAar.kt#94. - variant.packageLibraryProvider.get().from("${topsrcdir}/toolkit/components/telemetry/geckoview/streaming/metrics.yaml") -} - apply plugin: 'maven-publish' version = getVersionNumber() diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/optional-permission-all-urls/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/optional-permission-all-urls/manifest.json new file mode 100644 index 0000000000..c1c8fd9caa --- /dev/null +++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/optional-permission-all-urls/manifest.json @@ -0,0 +1,13 @@ +{ + "manifest_version": 3, + "name": "optional-permission-all-urls", + "browser_specific_settings": { + "gecko": { + "id": "optional-permission-all-urls@example.com" + } + }, + "version": "1.0", + "description": "Request optional extension origins for all urls.", + "optional_permissions": ["geolocation", "activeTab"], + "host_permissions": ["http://*/", "https://*/", "file://*/*"] +} diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt index 2ec305f913..06b946ba3d 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt @@ -2029,9 +2029,6 @@ class GeckoSessionTestRuleTest : BaseSessionTest(noErrorCollector = true) { @IgnoreCrash @Test fun contentCrashIgnored() { - // TODO: Bug 1673953 - assumeThat(sessionRule.env.isFission, equalTo(false)) - // TODO: bug 1710940 assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false)) diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoViewTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoViewTest.kt index 82af2c6475..c2ecb652a2 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoViewTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoViewTest.kt @@ -11,7 +11,6 @@ import android.view.View import android.view.ViewStructure import android.view.autofill.AutofillId import android.view.autofill.AutofillValue -import androidx.core.view.ViewCompat import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest @@ -79,7 +78,7 @@ class GeckoViewTest : BaseSessionTest() { activityRule.scenario.onActivity { assertThat( "View should be attached", - ViewCompat.isAttachedToWindow(it.view), + it.view.isAttachedToWindow(), equalTo(true), ) it.view.session!!.acquireDisplay() diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ScreenshotTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ScreenshotTest.kt index f3141c661c..301995c95c 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ScreenshotTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ScreenshotTest.kt @@ -12,7 +12,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import androidx.test.platform.app.InstrumentationRegistry import org.hamcrest.Matchers.* // ktlint-disable no-wildcard-imports -import org.junit.Assert +import org.junit.Assert.* import org.junit.Assume.assumeThat import org.junit.Test import org.junit.runner.RunWith @@ -25,6 +25,7 @@ import org.mozilla.geckoview.GeckoSession.ContentDelegate import org.mozilla.geckoview.GeckoSession.ProgressDelegate import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay +import org.mozilla.geckoview.test.util.UiThreadUtils import java.lang.IllegalStateException import kotlin.math.absoluteValue import kotlin.math.max @@ -142,6 +143,27 @@ class ScreenshotTest : BaseSessionTest() { } } + @WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH) + @Test + fun capturePixelsFailsWhenCompositorNotReady() { + sessionRule.display?.let { display -> + mainSession.close() + var exceptionListenerCalled = false + val result = display.capturePixels() + result.exceptionally { error: Throwable -> + assertTrue(error is IllegalStateException) + exceptionListenerCalled = true + result + }.accept { + fail("screenshot shouldn't complete successfully after session is closed") + } + UiThreadUtils.waitForCondition( + { exceptionListenerCalled }, + sessionRule.env.defaultTimeoutMillis, + ) + } ?: run { fail("no display found") } + } + // This tests tries to catch problems like Bug 1644561. @WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH) @Test @@ -430,7 +452,7 @@ class ScreenshotTest : BaseSessionTest() { .capture() .exceptionally( OnExceptionListener { error: Throwable -> - Assert.assertTrue(error is OutOfMemoryError) + assertTrue(error is OutOfMemoryError) fromException(error) }, ) diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TelemetryTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TelemetryTest.kt deleted file mode 100644 index 42286c47a7..0000000000 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TelemetryTest.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -package org.mozilla.geckoview.test - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.MediumTest -import org.hamcrest.CoreMatchers.equalTo -import org.hamcrest.Matchers.* // ktlint-disable no-wildcard-imports -import org.junit.Test -import org.junit.runner.RunWith -import org.mozilla.geckoview.GeckoResult -import org.mozilla.geckoview.RuntimeTelemetry -import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled - -@RunWith(AndroidJUnit4::class) -@MediumTest -class TelemetryTest : BaseSessionTest() { - @Test - fun testOnTelemetryReceived() { - // Let's make sure we batch the telemetry calls. - sessionRule.setPrefsUntilTestEnd( - mapOf("toolkit.telemetry.geckoview.batchDurationMS" to 100000), - ) - - val expectedHistograms = listOf(401, 12, 1, 109, 2000) - val receivedHistograms = mutableListOf() - val histogram = GeckoResult() - val stringScalar = GeckoResult() - val booleanScalar = GeckoResult() - val longScalar = GeckoResult() - - sessionRule.addExternalDelegateUntilTestEnd( - RuntimeTelemetry.Delegate::class, - sessionRule::setTelemetryDelegate, - { sessionRule.setTelemetryDelegate(null) }, - object : RuntimeTelemetry.Delegate { - @AssertCalled - override fun onHistogram(metric: RuntimeTelemetry.Histogram) { - if (metric.name != "TELEMETRY_TEST_STREAMING") { - return - } - - assertThat( - "The histogram should not be categorical", - metric.isCategorical, - equalTo(false), - ) - - receivedHistograms.addAll(metric.value.toList()) - - if (receivedHistograms.size == expectedHistograms.size) { - histogram.complete(null) - } - } - - @AssertCalled - override fun onStringScalar(metric: RuntimeTelemetry.Metric) { - if (metric.name != "telemetry.test.string_kind") { - return - } - - assertThat( - "Metric value should match", - metric.value, - equalTo("test scalar"), - ) - - stringScalar.complete(null) - } - - @AssertCalled - override fun onBooleanScalar(metric: RuntimeTelemetry.Metric) { - if (metric.name != "telemetry.test.boolean_kind") { - return - } - - assertThat( - "Metric value should match", - metric.value, - equalTo(true), - ) - - booleanScalar.complete(null) - } - - @AssertCalled - override fun onLongScalar(metric: RuntimeTelemetry.Metric) { - if (metric.name != "telemetry.test.unsigned_int_kind") { - return - } - - assertThat( - "Metric value should match", - metric.value, - equalTo(1234L), - ) - - longScalar.complete(null) - } - }, - ) - - sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", expectedHistograms[0]) - sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", expectedHistograms[1]) - sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", expectedHistograms[2]) - sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", expectedHistograms[3]) - - sessionRule.setScalar("telemetry.test.boolean_kind", true) - sessionRule.setScalar("telemetry.test.unsigned_int_kind", 1234) - sessionRule.setScalar("telemetry.test.string_kind", "test scalar") - - // Forces flushing telemetry data at next histogram. - sessionRule.setPrefsUntilTestEnd( - mapOf("toolkit.telemetry.geckoview.batchDurationMS" to 0), - ) - sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", expectedHistograms[4]) - - sessionRule.waitForResult(histogram) - sessionRule.waitForResult(stringScalar) - sessionRule.waitForResult(booleanScalar) - sessionRule.waitForResult(longScalar) - - assertThat( - "Metric values should match", - receivedHistograms, - equalTo(expectedHistograms), - ) - } -} diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TranslationsTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TranslationsTest.kt index a2c4eede62..966eba73b6 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TranslationsTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TranslationsTest.kt @@ -6,6 +6,7 @@ package org.mozilla.geckoview.test import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest +import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertTrue import org.json.JSONObject import org.junit.After @@ -78,7 +79,7 @@ class TranslationsTest : BaseSessionTest() { handled.complete(null) } }) - var expectedTranslateEvent = JSONObject( + val expectedTranslateEvent = JSONObject( """ { "actor":{ @@ -89,6 +90,7 @@ class TranslationsTest : BaseSessionTest() { "docLangTag": "es" }, "requestedTranslationPair": null, + "hasVisibleChange": false, "error": null, "isEngineReady": false } @@ -153,28 +155,43 @@ class TranslationsTest : BaseSessionTest() { val translate = sessionRule.session.sessionTranslation!!.translate("en", "es", null) try { sessionRule.waitForResult(translate) - assertTrue("Translate should complete.", true) + // When testing from AS, this path is possible. + if (!sessionRule.env.isAutomation) { + assertTrue("Translate should complete.", true) + } } catch (e: Exception) { - assertTrue("Should not have an exception while translating.", false) + if (sessionRule.env.isAutomation) { + assertTrue("Expect an exception while translating in automation.", true) + } } // Options should work as expected - var options = TranslationOptions.Builder().downloadModel(true).build() + val options = TranslationOptions.Builder().downloadModel(true).build() val translateOptions = sessionRule.session.sessionTranslation!!.translate("en", "es", options) try { sessionRule.waitForResult(translateOptions) - assertTrue("Translate should complete with options.", true) + // When testing from AS, this path is possible. + if (!sessionRule.env.isAutomation) { + assertTrue("Translate should complete with options.", true) + } } catch (e: Exception) { - assertTrue("Should not have an exception while translating with options.", false) + if (sessionRule.env.isAutomation) { + assertTrue("Expect an exception while translating in automation.", true) + } } // Language tags should be fault tolerant of minor variations val longLanguageTag = sessionRule.session.sessionTranslation!!.translate("EN", "ES", null) try { sessionRule.waitForResult(longLanguageTag) - assertTrue("Translate should complete with longer language tag.", true) + // When testing from AS, this path is possible. + if (!sessionRule.env.isAutomation) { + assertTrue("Translate should complete with longer language tag.", true) + } } catch (e: Exception) { - assertTrue("Should not have an exception while translating with a longer language tag.", false) + if (sessionRule.env.isAutomation) { + assertTrue("Expect an exception while translating in automation.", true) + } } } @@ -244,9 +261,14 @@ class TranslationsTest : BaseSessionTest() { val translate = sessionRule.session.sessionTranslation!!.translate("es", "en", null) try { sessionRule.waitForResult(translate) - assertTrue("Should be able to translate.", true) + // When testing from AS, this path is possible. + if (!sessionRule.env.isAutomation) { + assertTrue("Should be able to translate.", true) + } } catch (e: Exception) { - assertTrue("Should not have an exception.", false) + if (sessionRule.env.isAutomation) { + assertTrue("Expect an exception while translating in automation.", true) + } } } @@ -443,10 +465,10 @@ class TranslationsTest : BaseSessionTest() { @Test fun testGetLanguageSettings() { // Note: Test endpoint is using a mocked response and doesn't reflect actual prefs - var languageSettings: Map = + val languageSettings: Map = sessionRule.waitForResult(TranslationsController.RuntimeTranslation.getLanguageSettings()) - var frLanguageSetting = sessionRule.waitForResult(TranslationsController.RuntimeTranslation.getLanguageSetting("fr")) + val frLanguageSetting = sessionRule.waitForResult(TranslationsController.RuntimeTranslation.getLanguageSetting("fr")) if (sessionRule.env.isAutomation) { assertTrue("FR was correctly set to ALWAYS via full query.", languageSettings["fr"] == ALWAYS) @@ -621,4 +643,73 @@ class TranslationsTest : BaseSessionTest() { } } } + + @Test + fun hasVisibleChangeTest() { + mainSession.loadTestPath(TRANSLATIONS_ES) + mainSession.waitForPageStop() + + val handled = GeckoResult() + var delegateCalled = 0 + sessionRule.delegateUntilTestEnd(object : Delegate { + @AssertCalled(count = 2) + override fun onTranslationStateChange( + session: GeckoSession, + translationState: TranslationState?, + ) { + delegateCalled++ + + if (delegateCalled == 1) { + assertFalse("Initially not visibly changed.", translationState!!.hasVisibleChange) + } + + if (delegateCalled == 2) { + assertTrue("After a translation, the DOM should be visibly changed.", translationState!!.hasVisibleChange) + handled.complete(null) + } + } + }) + val notTranslated = JSONObject( + """ + { + "actor":{ + "languageState":{ + "detectedLanguages": { + "userLangTag": "en", + "isDocLangTagSupported": true, + "docLangTag": "es" + }, + "requestedTranslationPair": null, + "hasVisibleChange": false, + "error": null, + "isEngineReady": false + } + } + } + """.trimIndent(), + ) + mainSession.triggerLanguageStateChange(notTranslated) + + val translated = JSONObject( + """ + { + "actor":{ + "languageState":{ + "detectedLanguages": { + "userLangTag": "en", + "isDocLangTagSupported": true, + "docLangTag": "es" + }, + "requestedTranslationPair": {"fromLanguage" : "es" , "toLanguage" : "en"}, + "hasVisibleChange": true, + "error": null, + "isEngineReady": true + } + } + } + """.trimIndent(), + ) + mainSession.triggerLanguageStateChange(translated) + sessionRule.waitForResult(handled) + } } diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExtensionTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExtensionTest.kt index fa5caa8693..413ca7e2f6 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExtensionTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExtensionTest.kt @@ -482,6 +482,101 @@ class WebExtensionTest : BaseSessionTest() { sessionRule.waitForResult(controller.uninstall(extension)) } + @Test + fun optionalOriginsNormalized() { + // For mv3 extensions the host_permissions are being granted automatically at install time + // but this test needs them to not be granted yet and so we explicitly opt-out in this test. + sessionRule.setPrefsUntilTestEnd( + mapOf( + "extensions.originControls.grantByDefault" to false, + ), + ) + + var extension = sessionRule.waitForResult( + controller.ensureBuiltIn( + "resource://android/assets/web_extensions/optional-permission-all-urls/", + "optional-permission-all-urls@example.com", + ), + ) + + assertEquals("optional-permission-all-urls@example.com", extension.id) + + var grantedOptionalOrigins = extension.metaData.grantedOptionalOrigins + assertArrayEquals( + "grantedOptionalPermissions must be initially empty", + arrayOf(), + grantedOptionalOrigins, + ) + + extension = sessionRule.waitForResult( + controller.addOptionalPermissions( + extension.id, + arrayOf(), + arrayOf("http://*/", "https://*/", "file://*/*"), + ), + ) + + grantedOptionalOrigins = extension.metaData.grantedOptionalOrigins + + assertArrayEquals( + "grantedOptionalPermissions must be [http://*/*, https://*/*, file://*/*]", + arrayOf("http://*/*", "https://*/*", "file://*/*"), + grantedOptionalOrigins, + ) + + sessionRule.waitForResult(controller.uninstall(extension)) + } + + @Test + fun onOptionalPermissionsChanged() { + var extension = sessionRule.waitForResult( + controller.ensureBuiltIn( + "resource://android/assets/web_extensions/optional-permission-request/", + "optional-permission-request@example.com", + ), + ) + + assertEquals("optional-permission-request@example.com", extension.id) + + var grantedOptionalPermissions = extension.metaData.grantedOptionalPermissions + var grantedOptionalOrigins = extension.metaData.grantedOptionalOrigins + + assertThat( + "grantedOptionalPermissions must be 0.", + grantedOptionalPermissions.size, + equalTo(0), + ) + assertThat("grantedOptionalOrigins must be 0.", grantedOptionalOrigins.size, equalTo(0)) + + sessionRule.delegateDuringNextWait(object : WebExtensionController.AddonManagerDelegate { + @AssertCalled(count = 1) + override fun onOptionalPermissionsChanged(updatedExtension: WebExtension) { + grantedOptionalPermissions = updatedExtension.metaData.grantedOptionalPermissions + grantedOptionalOrigins = updatedExtension.metaData.grantedOptionalOrigins + assertNull(updatedExtension) + assertArrayEquals( + "grantedOptionalPermissions must be [activeTab, geolocation].", + arrayOf("activeTab", "geolocation"), + grantedOptionalPermissions, + ) + assertArrayEquals( + "grantedOptionalPermissions must be [*://example.com/*].", + arrayOf("*://example.com/*"), + grantedOptionalOrigins, + ) + } + }) + + extension = sessionRule.waitForResult( + controller.addOptionalPermissions( + extension.id, + arrayOf("activeTab", "geolocation"), + arrayOf("*://example.com/*"), + ), + ) + sessionRule.waitForResult(controller.uninstall(extension)) + } + private fun assertBodyBorderEqualTo(expected: String) { val color = mainSession.evaluateJS("document.body.style.borderColor") assertThat( @@ -2763,7 +2858,7 @@ class WebExtensionTest : BaseSessionTest() { "extensions.install.requireBuiltInCerts" to false, "extensions.update.requireBuiltInCerts" to false, "extensions.getAddons.cache.enabled" to true, - "extensions.getAddons.cache.lastUpdate" to 0, + "extensions.getAddons.cache.lastUpdate" to 1, ), ) mainSession.loadUri("https://example.com") @@ -3721,11 +3816,6 @@ class WebExtensionTest : BaseSessionTest() { mainSession.evaluateJS("typeof navigator.mozAddonManager") as String, equalTo("object"), ) - assertThat( - "mozAddonManager.abuseReportPanelEnabled should be false", - mainSession.evaluateJS("navigator.mozAddonManager.abuseReportPanelEnabled") as Boolean, - equalTo(false), - ) // Install an add-on, then assert results got from `mozAddonManager.getAddonByID()`. var addonId = "" diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java index 727f403931..7c95233552 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java @@ -89,7 +89,6 @@ import org.mozilla.geckoview.GeckoSession.TextInputDelegate; import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.geckoview.MediaSession; import org.mozilla.geckoview.OrientationController; -import org.mozilla.geckoview.RuntimeTelemetry; import org.mozilla.geckoview.SessionTextInput; import org.mozilla.geckoview.TranslationsController; import org.mozilla.geckoview.WebExtension; @@ -988,10 +987,6 @@ public class GeckoSessionTestRule implements TestRule { return RuntimeCreator.getRuntime(); } - public void setTelemetryDelegate(final RuntimeTelemetry.Delegate delegate) { - RuntimeCreator.setTelemetryDelegate(delegate); - } - /** Sets an experiment delegate on the runtime creator. */ public void setExperimentDelegate(final ExperimentDelegate delegate) { RuntimeCreator.setExperimentDelegate(delegate); @@ -1463,7 +1458,6 @@ public class GeckoSessionTestRule implements TestRule { mLastWaitStart = 0; mLastWaitEnd = 0; mTimeoutMillis = 0; - RuntimeCreator.setTelemetryDelegate(null); RuntimeCreator.setExperimentDelegate(null); } diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/RuntimeCreator.java b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/RuntimeCreator.java index 7eda360459..db18c06a9d 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/RuntimeCreator.java +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/RuntimeCreator.java @@ -18,7 +18,6 @@ import org.mozilla.geckoview.ExperimentDelegate; import org.mozilla.geckoview.GeckoResult; import org.mozilla.geckoview.GeckoRuntime; import org.mozilla.geckoview.GeckoRuntimeSettings; -import org.mozilla.geckoview.RuntimeTelemetry; import org.mozilla.geckoview.WebExtension; import org.mozilla.geckoview.test.TestCrashHandler; @@ -34,40 +33,6 @@ public class RuntimeCreator { public static AtomicInteger sTestSupport = new AtomicInteger(0); public static WebExtension sTestSupportExtension; - // The RuntimeTelemetry.Delegate can only be set when creating the RuntimeCreator, to - // let tests set their own Delegate we need to create a proxy here. - public static class RuntimeTelemetryDelegate implements RuntimeTelemetry.Delegate { - public RuntimeTelemetry.Delegate delegate = null; - - @Override - public void onHistogram(@NonNull final RuntimeTelemetry.Histogram metric) { - if (delegate != null) { - delegate.onHistogram(metric); - } - } - - @Override - public void onBooleanScalar(@NonNull final RuntimeTelemetry.Metric metric) { - if (delegate != null) { - delegate.onBooleanScalar(metric); - } - } - - @Override - public void onStringScalar(@NonNull final RuntimeTelemetry.Metric metric) { - if (delegate != null) { - delegate.onStringScalar(metric); - } - } - - @Override - public void onLongScalar(@NonNull final RuntimeTelemetry.Metric metric) { - if (delegate != null) { - delegate.onLongScalar(metric); - } - } - } - /** * The ExperimentDelegate can only be set when starting the RuntimeCreator, so for testing we are * setting up a proxy here @@ -110,9 +75,6 @@ public class RuntimeCreator { } } - public static final RuntimeTelemetryDelegate sRuntimeTelemetryProxy = - new RuntimeTelemetryDelegate(); - public static RuntimeExperimentDelegate sRuntimeExperimentDelegateProxy = new RuntimeExperimentDelegate(); private static WebExtension.Port sBackgroundPort; @@ -162,17 +124,6 @@ public class RuntimeCreator { }); } - /** - * Set the {@link RuntimeTelemetry.Delegate} instance for this test. Application code can only - * register this delegate when the {@link GeckoRuntime} is created, so we need to proxy it for - * test code. - * - * @param delegate the {@link RuntimeTelemetry.Delegate} for this test run. - */ - public static void setTelemetryDelegate(final RuntimeTelemetry.Delegate delegate) { - sRuntimeTelemetryProxy.delegate = delegate; - } - /** * Set the {@link ExperimentDelegate} instance for this test. Application code can only register * this delegate when the {@link GeckoRuntime} is created, so we need to proxy it for test code. @@ -216,7 +167,6 @@ public class RuntimeCreator { .remoteDebuggingEnabled(true) .consoleOutput(true) .crashHandler(TestCrashHandler.class) - .telemetryDelegate(sRuntimeTelemetryProxy) .experimentDelegate(sRuntimeExperimentDelegateProxy) .build(); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoDisplay.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoDisplay.java index 1fc34cb8bb..e0c16d66cc 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoDisplay.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoDisplay.java @@ -453,13 +453,15 @@ public class GeckoDisplay { *

This function must be called on the UI thread. * * @return A {@link GeckoResult} that completes with a {@link Bitmap} containing the pixels and - * size information of the requested portion of the visible web page. + * size information of the requested portion of the visible web page, or returns a failure + * {@link GeckoResult} including the reason why in an {@link Exception} */ @UiThread public @NonNull GeckoResult capture() { ThreadUtils.assertOnUiThread(); if (!mSession.isCompositorReady()) { - throw new IllegalStateException("Compositor must be ready before pixels can be captured"); + return GeckoResult.fromException( + new IllegalStateException("Compositor must be ready before pixels can be captured")); } final GeckoResult result = new GeckoResult<>(); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java index 8750f344a8..ccd513f6bd 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java @@ -1893,7 +1893,7 @@ import org.mozilla.geckoview.SessionTextInput.EditableListener.IMEState; outAttrs.imeOptions = EditorInfo.IME_ACTION_GO; } else if (actionHint.equals("done")) { outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE; - } else if (actionHint.equals("next") || actionHint.equals("maybenext")) { + } else if (actionHint.equals("next")) { outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT; } else if (actionHint.equals("previous")) { outAttrs.imeOptions = EditorInfo.IME_ACTION_PREVIOUS; @@ -1901,6 +1901,9 @@ import org.mozilla.geckoview.SessionTextInput.EditableListener.IMEState; outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH; } else if (actionHint.equals("send")) { outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND; + } else if (actionHint.equals("maybenext")) { + // this should be low priority as "maybenext" is internal type + outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT; } else if (actionHint.length() > 0) { if (DEBUG) Log.w(LOGTAG, "Unexpected actionHint=\"" + actionHint + "\""); outAttrs.actionLabel = actionHint; @@ -2568,6 +2571,7 @@ import org.mozilla.geckoview.SessionTextInput.EditableListener.IMEState; switch (keyCode) { case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_BACK: + case KeyEvent.KEYCODE_FORWARD: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_SEARCH: diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java index 0a80b02b04..482fa32f39 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java @@ -456,21 +456,6 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { return this; } - /** - * Add a {@link RuntimeTelemetry.Delegate} instance to this GeckoRuntime. This delegate can be - * used by the app to receive streaming telemetry data from GeckoView. - * - * @param delegate the delegate that will handle telemetry - * @return The builder instance. - */ - @Deprecated - @DeprecationSchedule(id = "geckoview-gvst", version = 127) - public @NonNull Builder telemetryDelegate(final @NonNull RuntimeTelemetry.Delegate delegate) { - getSettings().mTelemetryProxy = new RuntimeTelemetry.Proxy(delegate); - getSettings().mTelemetryEnabled.set(true); - return this; - } - /** * Set the {@link ExperimentDelegate} instance on this runtime, if any. This delegate is used to * send and receive experiment information from Nimbus. @@ -663,7 +648,6 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { /* package */ int mScreenHeightOverride; /* package */ Class mCrashHandler; /* package */ String[] mRequestedLocales; - /* package */ RuntimeTelemetry.Proxy mTelemetryProxy; /* package */ ExperimentDelegate mExperimentDelegate; /** @@ -674,10 +658,6 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { /* package */ void attachTo(final @NonNull GeckoRuntime runtime) { mRuntime = runtime; commit(); - - if (mTelemetryProxy != null) { - mTelemetryProxy.attach(); - } } @Override // RuntimeSettings @@ -719,7 +699,6 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { mCrashHandler = settings.mCrashHandler; mRequestedLocales = settings.mRequestedLocales; mConfigFilePath = settings.mConfigFilePath; - mTelemetryProxy = settings.mTelemetryProxy; mExperimentDelegate = settings.mExperimentDelegate; } @@ -1368,11 +1347,6 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { return this; } - @SuppressWarnings("checkstyle:javadocmethod") - public @Nullable RuntimeTelemetry.Delegate getTelemetryDelegate() { - return mTelemetryProxy.getDelegate(); - } - /** * Get the {@link ExperimentDelegate} instance set on this runtime, if any, * diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java index 85b3abf9a9..ccf1e8520e 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java @@ -2448,6 +2448,7 @@ public class GeckoSession { @IntDef( flag = true, value = { + FINDER_FIND_FORWARD, FINDER_FIND_BACKWARDS, FINDER_FIND_LINKS_ONLY, FINDER_FIND_MATCH_CASE, @@ -2455,6 +2456,9 @@ public class GeckoSession { }) public @interface FinderFindFlags {} + /** Go forward when finding the next match. */ + public static final int FINDER_FIND_FORWARD = 0; + /** Go backwards when finding the next match. */ public static final int FINDER_FIND_BACKWARDS = 1; diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java index 14f6b14c47..4dc23bc519 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java @@ -331,11 +331,11 @@ public final class GeckoSessionSettings implements Parcelable { new Key("fullAccessibilityTree", /* initOnly */ false, /* values */ null); /** - * Key to specify if this GeckoSession is a Popup or not. Popup sessions can paint over other - * sessions and are not exposed to the tabs WebExtension API. + * Key to specify if this GeckoSession is a Extension Popup or not. Popup sessions can paint over + * other sessions and are not exposed to the tabs WebExtension API. */ - private static final Key IS_POPUP = - new Key("isPopup", /* initOnly */ false, /* values */ null); + private static final Key IS_EXTENSION_POPUP = + new Key("isExtensionPopup", /* initOnly */ false, /* values */ null); /** Internal Gecko key to specify the session context ID. Derived from `UNSAFE_CONTEXT_ID`. */ private static final Key CONTEXT_ID = @@ -375,7 +375,7 @@ public final class GeckoSessionSettings implements Parcelable { mBundle.putBoolean(SUSPEND_MEDIA_WHEN_INACTIVE.name, false); mBundle.putBoolean(ALLOW_JAVASCRIPT.name, true); mBundle.putBoolean(FULL_ACCESSIBILITY_TREE.name, false); - mBundle.putBoolean(IS_POPUP.name, false); + mBundle.putBoolean(IS_EXTENSION_POPUP.name, false); mBundle.putInt(USER_AGENT_MODE.name, USER_AGENT_MODE_MOBILE); mBundle.putString(USER_AGENT_OVERRIDE.name, null); mBundle.putInt(VIEWPORT_MODE.name, VIEWPORT_MODE_MOBILE); @@ -430,8 +430,8 @@ public final class GeckoSessionSettings implements Parcelable { setBoolean(FULL_ACCESSIBILITY_TREE, value); } - /* package */ void setIsPopup(final boolean value) { - setBoolean(IS_POPUP, value); + /* package */ void setIsExtensionPopup(final boolean value) { + setBoolean(IS_EXTENSION_POPUP, value); } private void setBoolean(final Key key, final boolean value) { @@ -498,8 +498,8 @@ public final class GeckoSessionSettings implements Parcelable { return getBoolean(FULL_ACCESSIBILITY_TREE); } - /* package */ boolean getIsPopup() { - return getBoolean(IS_POPUP); + /* package */ boolean getIsExtensionPopup() { + return getBoolean(IS_EXTENSION_POPUP); } private boolean getBoolean(final Key key) { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java index 2271ff71f7..8b31862524 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java @@ -22,6 +22,8 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.StateListDrawable; import android.os.Build; import android.os.Handler; import android.print.PrintDocumentAdapter; @@ -269,6 +271,14 @@ public class GeckoView extends FrameLayout implements GeckoDisplay.NewSurfacePro // descendants to affect the way LayerView retains its focus. setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS); + // When GeckoView.requestFocus() is called with hardware keyboard, the focused state color + // might be applied on this view. But we don't want to apply it as default. + final StateListDrawable drawable = new StateListDrawable(); + drawable.addState( + new int[] {android.R.attr.state_focused, -android.R.attr.state_focused}, + new ColorDrawable(Color.WHITE)); + setBackground(drawable); + // This will stop PropertyAnimator from creating a drawing cache (i.e. a // bitmap) from a SurfaceView, which is just not possible (the bitmap will be // transparent). diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/RuntimeTelemetry.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/RuntimeTelemetry.java deleted file mode 100644 index 1fad0cb17e..0000000000 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/RuntimeTelemetry.java +++ /dev/null @@ -1,171 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * vim: ts=4 sw=4 expandtab: - * 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 org.mozilla.geckoview; - -import androidx.annotation.AnyThread; -import androidx.annotation.NonNull; -import org.mozilla.gecko.GeckoThread; -import org.mozilla.gecko.annotation.WrapForJNI; -import org.mozilla.gecko.mozglue.JNIObject; - -/** The telemetry API gives access to telemetry data of the Gecko runtime. */ -public final class RuntimeTelemetry { - protected RuntimeTelemetry() {} - - /** - * The runtime telemetry metric object. - * - * @param type of the underlying metric sample - */ - public static class Metric { - /** The runtime metric name. */ - public final @NonNull String name; - - /** The metric values. */ - public final @NonNull T value; - - /* package */ Metric(final String name, final T value) { - this.name = name; - this.value = value; - } - - @Override - public String toString() { - return "name: " + name + ", value: " + value; - } - - // For testing - protected Metric() { - name = null; - value = null; - } - } - - /** The Histogram telemetry metric object. */ - public static class Histogram extends Metric { - /** Whether or not this is a Categorical Histogram. */ - public final boolean isCategorical; - - /* package */ Histogram(final boolean isCategorical, final String name, final long[] value) { - super(name, value); - this.isCategorical = isCategorical; - } - - // For testing - protected Histogram() { - super(null, null); - isCategorical = false; - } - } - - /** - * The runtime telemetry delegate. Implement this if you want to receive runtime (Gecko) telemetry - * and attach it via {@link GeckoRuntimeSettings.Builder#telemetryDelegate}. - */ - public interface Delegate { - /** - * A runtime telemetry histogram metric has been received. - * - * @param metric The runtime metric details. - */ - @AnyThread - default void onHistogram(final @NonNull Histogram metric) {} - - /** - * A runtime telemetry boolean scalar has been received. - * - * @param metric The runtime metric details. - */ - @AnyThread - default void onBooleanScalar(final @NonNull Metric metric) {} - - /** - * A runtime telemetry long scalar has been received. - * - * @param metric The runtime metric details. - */ - @AnyThread - default void onLongScalar(final @NonNull Metric metric) {} - - /** - * A runtime telemetry string scalar has been received. - * - * @param metric The runtime metric details. - */ - @AnyThread - default void onStringScalar(final @NonNull Metric metric) {} - } - - // The proxy connects to telemetry core and forwards telemetry events - // to the attached delegate. - /* package */ static final class Proxy extends JNIObject { - private final Delegate mDelegate; - - public Proxy(final @NonNull Delegate delegate) { - mDelegate = delegate; - } - - // Attach to current runtime. - // We might have different mechanics of attaching to specific runtimes - // in future, for which case we should split the delegate assignment in - // the setup phase from the attaching. - public void attach() { - if (GeckoThread.isRunning()) { - registerDelegateProxy(this); - } else { - GeckoThread.queueNativeCall(Proxy.class, "registerDelegateProxy", Proxy.class, this); - } - } - - public @NonNull Delegate getDelegate() { - return mDelegate; - } - - @WrapForJNI(dispatchTo = "gecko") - private static native void registerDelegateProxy(Proxy proxy); - - @WrapForJNI(calledFrom = "gecko") - /* package */ void dispatchHistogram( - final boolean isCategorical, final String name, final long[] values) { - if (mDelegate == null) { - // TODO throw? - return; - } - mDelegate.onHistogram(new Histogram(isCategorical, name, values)); - } - - @WrapForJNI(calledFrom = "gecko") - /* package */ void dispatchStringScalar(final String name, final String value) { - if (mDelegate == null) { - return; - } - mDelegate.onStringScalar(new Metric<>(name, value)); - } - - @WrapForJNI(calledFrom = "gecko") - /* package */ void dispatchBooleanScalar(final String name, final boolean value) { - if (mDelegate == null) { - return; - } - mDelegate.onBooleanScalar(new Metric<>(name, value)); - } - - @WrapForJNI(calledFrom = "gecko") - /* package */ void dispatchLongScalar(final String name, final long value) { - if (mDelegate == null) { - return; - } - mDelegate.onLongScalar(new Metric<>(name, value)); - } - - @Override // JNIObject - protected void disposeNative() { - // We don't hold native references. - throw new UnsupportedOperationException(); - } - } -} diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ScreenLength.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ScreenLength.java index 1ce4b41659..e88976a447 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ScreenLength.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ScreenLength.java @@ -1,4 +1,6 @@ -/* License, v. 2.0. If a copy of the MPL was not distributed with this +/* + * 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 org.mozilla.geckoview; diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionFinder.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionFinder.java index 2ed0b1a6c3..b245a39f1e 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionFinder.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionFinder.java @@ -27,6 +27,7 @@ public final class SessionFinder { private static final List> sFlagNames = Arrays.asList( + new Pair<>(GeckoSession.FINDER_FIND_FORWARD, "forward"), new Pair<>(GeckoSession.FINDER_FIND_BACKWARDS, "backwards"), new Pair<>(GeckoSession.FINDER_FIND_LINKS_ONLY, "linksOnly"), new Pair<>(GeckoSession.FINDER_FIND_MATCH_CASE, "matchCase"), @@ -70,7 +71,8 @@ public final class SessionFinder { * previous search string. * * @param searchString String to search, or null to find again using the previous string. - * @param flags Flags for performing the search; either 0 or a combination of {@link + * @param flags Flags for performing the search; either FINDER_FIND_FORWARD {@link + * GeckoSession#FINDER_FIND_FORWARD} or a combination of {@link * GeckoSession#FINDER_FIND_BACKWARDS FINDER_FIND_*} constants. * @return Result of the search operation as a {@link GeckoResult} object. * @see #clear diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TranslationsController.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TranslationsController.java index 37e5e7139a..256877532b 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TranslationsController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TranslationsController.java @@ -1081,14 +1081,20 @@ public class TranslationsController { /** If the translation engine is ready for use or will need to be loaded. */ public final @NonNull Boolean isEngineReady; + /** If the DOM has began visibly changing to the translated text. */ + public final @NonNull Boolean hasVisibleChange; + /** - * Translation State constructor. + * This constructor is deprecated, please use the [TranslationState] with [hasVisibleChange] + * parameter. This constructor will be removed in bug 1895275. Translation State constructor. * * @param requestedTranslationPair the language pair to translate * @param error if an error occurred * @param detectedLanguages detected language * @param isEngineReady if the engine is ready for translations */ + @Deprecated + @DeprecationSchedule(version = 130, id = "translation-state-deprecated-constructor") public TranslationState( final @Nullable TranslationPair requestedTranslationPair, final @Nullable String error, @@ -1098,6 +1104,29 @@ public class TranslationsController { this.error = error; this.detectedLanguages = detectedLanguages; this.isEngineReady = isEngineReady; + this.hasVisibleChange = false; + } + + /** + * Translation State constructor. + * + * @param requestedTranslationPair the language pair to translate + * @param error if an error occurred + * @param detectedLanguages detected language + * @param isEngineReady if the engine is ready for translations + * @param hasVisibleChange if the DOM has began to visibly change to translated text + */ + public TranslationState( + final @Nullable TranslationPair requestedTranslationPair, + final @Nullable String error, + final @Nullable DetectedLanguages detectedLanguages, + final @NonNull Boolean isEngineReady, + final @NonNull Boolean hasVisibleChange) { + this.requestedTranslationPair = requestedTranslationPair; + this.error = error; + this.detectedLanguages = detectedLanguages; + this.isEngineReady = isEngineReady; + this.hasVisibleChange = hasVisibleChange; } @Override @@ -1112,6 +1141,8 @@ public class TranslationsController { + detectedLanguages + ", isEngineReady=" + isEngineReady + + ", hasVisibleChange=" + + hasVisibleChange + '}'; } @@ -1130,7 +1161,8 @@ public class TranslationsController { TranslationPair.fromBundle(bundle.getBundle("requestedTranslationPair")), bundle.getString("error"), DetectedLanguages.fromBundle(bundle.getBundle("detectedLanguages")), - bundle.getBoolean("isEngineReady", false)); + bundle.getBoolean("isEngineReady", false), + bundle.getBoolean("hasVisibleChange", false)); } } @@ -1168,7 +1200,7 @@ public class TranslationsController { final GeckoBundle data = message.getBundle("data"); final TranslationState translationState = TranslationState.fromBundle(data); if (DEBUG) { - Log.d(LOGTAG, "received translation state: " + translationState); + Log.d(LOGTAG, "Received translation state: " + translationState); } delegate.onTranslationStateChange(mSession, translationState); if (translationState != null diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java index bf5d431cf1..53d57b126a 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java @@ -1442,7 +1442,7 @@ public class WebExtension { return; } - session.getSettings().setIsPopup(true); + session.getSettings().setIsExtensionPopup(true); session.loadUri(popupUri); }); } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java index 07e848b079..889cd91895 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java @@ -335,6 +335,14 @@ public class WebExtensionController { @UiThread default void onDisabling(@NonNull WebExtension extension) {} + /** + * Called whenever optional permissions of an extension have changed. + * + * @param extension The {@link WebExtension} that has optional permissions changed. + */ + @UiThread + default void onOptionalPermissionsChanged(@NonNull WebExtension extension) {} + /** * Called whenever an extension has been disabled. * @@ -489,6 +497,7 @@ public class WebExtensionController { EventDispatcher.getInstance() .unregisterUiThreadListener( mInternals, + "GeckoView:WebExtension:OnOptionalPermissionsChanged", "GeckoView:WebExtension:OnDisabling", "GeckoView:WebExtension:OnDisabled", "GeckoView:WebExtension:OnEnabling", @@ -503,6 +512,7 @@ public class WebExtensionController { EventDispatcher.getInstance() .registerUiThreadListener( mInternals, + "GeckoView:WebExtension:OnOptionalPermissionsChanged", "GeckoView:WebExtension:OnDisabling", "GeckoView:WebExtension:OnDisabled", "GeckoView:WebExtension:OnEnabling", @@ -1021,6 +1031,9 @@ public class WebExtensionController { } else if ("GeckoView:WebExtension:OnDisabling".equals(event)) { onDisabling(bundle); return; + } else if ("GeckoView:WebExtension:OnOptionalPermissionsChanged".equals(event)) { + onOptionalPermissionsChanged(bundle); + return; } else if ("GeckoView:WebExtension:OnDisabled".equals(event)) { onDisabled(bundle); return; @@ -1265,6 +1278,17 @@ public class WebExtensionController { mAddonManagerDelegate.onDisabling(extension); } + private void onOptionalPermissionsChanged(final GeckoBundle bundle) { + if (mAddonManagerDelegate == null) { + Log.e(LOGTAG, "no AddonManager delegate registered"); + return; + } + + final GeckoBundle extensionBundle = bundle.getBundle("extension"); + final WebExtension extension = new WebExtension(mDelegateControllerProvider, extensionBundle); + mAddonManagerDelegate.onOptionalPermissionsChanged(extension); + } + private void onDisabled(final GeckoBundle bundle) { if (mAddonManagerDelegate == null) { Log.e(LOGTAG, "no AddonManager delegate registered"); @@ -1525,7 +1549,7 @@ public class WebExtensionController { if (delegate != null) { result = delegate.onCloseTab(extension, message.session); } else { - result = GeckoResult.fromValue(AllowOrDeny.DENY); + result = GeckoResult.deny(); } message.callback.resolveTo( diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md index e2df6df21b..a2d30d25ef 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md @@ -13,6 +13,17 @@ exclude: true ⚠️ breaking change and deprecation notices +## v127 +- ⚠️ Removed deprecated [`RuntimeTelemetry`][125.5], [`GeckoRuntimeSettings.getTelemetryDelegate`][125.6] and [`GeckoRuntimeSettings.telemetryDelegate`][125.7]. +- Added [FINDER_FIND_FORWARD][127.1] +- Added [`WebExtensionController.AddonManagerDelegate.onOptionalPermissionsChanged`][127.2] ([bug 1892302]({{bugzilla}}1892302). +- Added a new [`TranslationState`][127.3] constructor to add `hasVisibleChange` and deprecated the prior [`TranslationsState`][127.4] constructor to be removed in v130. + +[127.1]: {{javadoc_uri}}/GeckoSession.html#FINDER_FIND_FORWARD +[127.2]: {{javadoc_uri}}/WebExtensionController.AddonManagerDelegate.html#onOptionalPermissionsChanged +[127.3]: {{javadoc_uri}}/TranslationsController.SessionTranslation.TranslationState.html#%3Cinit%3E(org.mozilla.geckoview.TranslationsController.SessionTranslation.TranslationPair,java.lang.String,org.mozilla.geckoview.TranslationsController.SessionTranslation.DetectedLanguages,java.lang.Boolean,java.lang.Boolean) +[127.4]: {{javadoc_uri}}/TranslationsController.SessionTranslation.TranslationState.html#(org.mozilla.geckoview.TranslationsController.SessionTranslation.TranslationPair,java.lang.String,org.mozilla.geckoview.TranslationsController.SessionTranslation.DetectedLanguages,java.lang.Boolean) + ## v125 - ⚠️ Deprecated [`GeckoSession.NavigationDelegate.onLocationChange`][125.1], to be removed in v127. ([bug 1837601]({{bugzilla}}1837601)) @@ -1547,4 +1558,4 @@ to allow adding gecko profiler markers. [65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport(android.content.Context,android.os.Bundle,java.lang.String) [65.25]: {{javadoc_uri}}/GeckoResult.html -[api-version]: 2c319e9f18adb4178ce09d71088a173b56d1a694 +[api-version]: 0b9d0f241805fab7e71d3d745170a237f6ac113d -- cgit v1.2.3