mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1845824 - ExperimentDelegate GeckoSessionHandler r=geckoview-reviewers,tthibaud,jonalmeida,owlish
This patch adds a an ExperimentDelegate GeckoSessionHandler, a JS Experiment Delegate Parent/Child, and a way to get/set the Experiment Delegate on the session. Differential Revision: https://phabricator.services.mozilla.com/D184841
This commit is contained in:
parent
e56c50d203
commit
713fd4f612
@ -0,0 +1,69 @@
|
||||
/* 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/. */
|
||||
|
||||
import { GeckoViewActorParent } from "resource://gre/modules/GeckoViewActorParent.sys.mjs";
|
||||
|
||||
export class GeckoViewExperimentDelegateParent extends GeckoViewActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets experiment information on a given feature.
|
||||
*
|
||||
* @param feature the experiment item to retrieve information on
|
||||
* @returns a promise of success with a JSON message or failure
|
||||
*/
|
||||
async getExperimentFeature(feature) {
|
||||
return this.eventDispatcher.sendRequestForResult({
|
||||
type: "GeckoView:GetExperimentFeature",
|
||||
feature,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Records an exposure event, that the experiment area was encountered, on a given feature.
|
||||
*
|
||||
* @param feature the experiment item to record an exposure event of
|
||||
* @returns a promise of success or failure
|
||||
*/
|
||||
async recordExposure(feature) {
|
||||
return this.eventDispatcher.sendRequestForResult({
|
||||
type: "GeckoView:RecordExposure",
|
||||
feature,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Records an exposure event on a specific experiment feature and element.
|
||||
*
|
||||
* Note: Use recordExposure, if the slug is not known.
|
||||
*
|
||||
* @param feature the experiment item to record an exposure event of
|
||||
* @param slug a specific experiment element
|
||||
* @returns a promise of success or failure
|
||||
*/
|
||||
async recordExperimentExposure(feature, slug) {
|
||||
return this.eventDispatcher.sendRequestForResult({
|
||||
type: "GeckoView:RecordExperimentExposure",
|
||||
feature,
|
||||
slug,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* For recording malformed configuration.
|
||||
*
|
||||
* @param feature the experiment item to record an exposure event of
|
||||
* @param part malformed information to send
|
||||
* @returns a promise of success or failure
|
||||
*/
|
||||
async recordExperimentMalformedConfig(feature, part) {
|
||||
return this.eventDispatcher.sendRequestForResult({
|
||||
type: "GeckoView:RecordMalformedConfig",
|
||||
feature,
|
||||
part,
|
||||
});
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ FINAL_TARGET_FILES.actors += [
|
||||
"GeckoViewClipboardPermissionParent.sys.mjs",
|
||||
"GeckoViewContentChild.sys.mjs",
|
||||
"GeckoViewContentParent.sys.mjs",
|
||||
"GeckoViewExperimentDelegateParent.sys.mjs",
|
||||
"GeckoViewFormValidationChild.sys.mjs",
|
||||
"GeckoViewPermissionChild.sys.mjs",
|
||||
"GeckoViewPermissionParent.sys.mjs",
|
||||
|
@ -5,4 +5,7 @@ prefs =
|
||||
dom.enable_window_print=true
|
||||
skip-if =
|
||||
os != 'android'
|
||||
[test_geckoview_actor_telemetry.html]
|
||||
[test_geckoview_actor_telemetry.html]
|
||||
skip-if =
|
||||
os != 'android'
|
||||
[test_geckoview_experiment_delegate.html]
|
@ -0,0 +1,108 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1845824
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test Experiment Delegate</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="head.js" type="application/javascript"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
// Note: TestRunnerActivity provides a pseudo Experiment Delegate for this test.
|
||||
async function requestExperiment(message) {
|
||||
const chromeScript = SpecialPowers.loadChromeScript(_ => {
|
||||
/* eslint-env mozilla/chrome-script */
|
||||
addMessageListener("experiment", (msg) => {
|
||||
var result;
|
||||
const navigator = Services.wm.getMostRecentWindow("navigator:geckoview");
|
||||
const experimentActor = navigator.window.moduleManager.getActor("GeckoViewExperimentDelegate");
|
||||
switch (msg.endpoint) {
|
||||
case 'getExperimentFeature':
|
||||
result = experimentActor.getExperimentFeature(msg.feature);
|
||||
break;
|
||||
case 'recordExposure':
|
||||
result = experimentActor.recordExposure(msg.feature);
|
||||
break
|
||||
case 'recordExperimentExposure':
|
||||
result = experimentActor.recordExperimentExposure(msg.feature, msg.slug);
|
||||
break;
|
||||
case 'recordExperimentMalformedConfig':
|
||||
result = experimentActor.recordExperimentMalformedConfig(msg.feature, msg.part);
|
||||
break;
|
||||
default:
|
||||
result = null;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
const result = await chromeScript.sendQuery("experiment", message);
|
||||
chromeScript.destroy();
|
||||
return result;
|
||||
}
|
||||
|
||||
add_task(async function test_getExperimentFeature() {
|
||||
const success = await requestExperiment({endpoint: "getExperimentFeature", feature: "test"});
|
||||
is(success["item-one"], true, "Retrieved TestRunnerActivity experiment feature 'test' for 'item-one'.");
|
||||
is(success["item-two"], 5, "Retrieved TestRunnerActivity experiment feature 'test' for 'item-two'.");
|
||||
var didErrorOccur = false;
|
||||
try {
|
||||
await requestExperiment({endpoint: "getExperimentFeature", feature: "no-feature"});
|
||||
} catch (error) {
|
||||
is(error, "An error occurred while retrieving feature data.", "Correctly failed when the feature did not exist.");
|
||||
didErrorOccur = true;
|
||||
}
|
||||
is(didErrorOccur, true, "Error was caught when no feature existed.");
|
||||
});
|
||||
|
||||
add_task(async function test_recordExposure() {
|
||||
const success = await requestExperiment({endpoint: "recordExposure", feature: "test"});
|
||||
is(success, true, "Recorded exposure for the feature.");
|
||||
var didErrorOccur = false;
|
||||
try {
|
||||
await requestExperiment({endpoint: "recordExposure", feature: "no-feature"});
|
||||
} catch (error) {
|
||||
is(error, "An error occurred while recording feature.", "Correctly failed when the feature did not exist.");
|
||||
didErrorOccur = true;
|
||||
}
|
||||
is(didErrorOccur, true, "Error was caught when no feature existed.");
|
||||
});
|
||||
|
||||
|
||||
add_task(async function test_recordExperimentExposure() {
|
||||
const success = await requestExperiment({endpoint: "recordExperimentExposure", feature: "test", slug: "test"});
|
||||
is(success, true, "Recorded experiment exposure for the feature.");
|
||||
var didErrorOccur = false;
|
||||
try {
|
||||
await requestExperiment({endpoint: "recordExperimentExposure", feature: "no-feature", slug: "no-slug"});
|
||||
} catch (error) {
|
||||
is(error, "An error occurred while recording experiment feature.", "Correctly failed when the feature did not exist.");
|
||||
didErrorOccur = true;
|
||||
}
|
||||
is(didErrorOccur, true, "Error was caught when no feature existed.");
|
||||
});
|
||||
|
||||
|
||||
add_task(async function test_recordExperimentMalformedConfig() {
|
||||
const success = await requestExperiment({endpoint: "recordExperimentMalformedConfig", feature: "test", part: "test"});
|
||||
is(success, true, "Recorded exposure for the feature.");
|
||||
var didErrorOccur = false;
|
||||
try {
|
||||
await requestExperiment({endpoint: "recordExperimentMalformedConfig", feature: "no-feature", part:"no-part"});
|
||||
} catch (error) {
|
||||
is(error, "An error occurred while recording malformed feature config.", "Correctly failed when the feature did not exist.");
|
||||
didErrorOccur = true;
|
||||
}
|
||||
is(didErrorOccur, true, "Error was caught when no feature existed.");
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -842,6 +842,20 @@ function startup() {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GeckoViewExperimentDelegate",
|
||||
onInit: {
|
||||
actors: {
|
||||
GeckoViewExperimentDelegate: {
|
||||
parent: {
|
||||
esModuleURI:
|
||||
"resource:///actors/GeckoViewExperimentDelegateParent.sys.mjs",
|
||||
},
|
||||
allFrames: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
if (!Services.appinfo.sessionHistoryInParent) {
|
||||
|
@ -49,6 +49,7 @@ import java.io.InputStream;
|
||||
import java.lang.Boolean;
|
||||
import java.lang.CharSequence;
|
||||
import java.lang.Class;
|
||||
import java.lang.Deprecated;
|
||||
import java.lang.Double;
|
||||
import java.lang.Exception;
|
||||
import java.lang.Float;
|
||||
@ -83,6 +84,7 @@ import org.mozilla.geckoview.CompositorController;
|
||||
import org.mozilla.geckoview.ContentBlocking;
|
||||
import org.mozilla.geckoview.ContentBlockingController;
|
||||
import org.mozilla.geckoview.CrashHandler;
|
||||
import org.mozilla.geckoview.DeprecationSchedule;
|
||||
import org.mozilla.geckoview.ExperimentDelegate;
|
||||
import org.mozilla.geckoview.GeckoDisplay;
|
||||
import org.mozilla.geckoview.GeckoResult;
|
||||
@ -660,6 +662,7 @@ package org.mozilla.geckoview {
|
||||
|
||||
public static class ExperimentDelegate.ExperimentException extends Exception {
|
||||
ctor public ExperimentException(int);
|
||||
field public static final int ERROR_EXPERIMENT_DELEGATE_NOT_IMPLEMENTED = -4;
|
||||
field public static final int ERROR_EXPERIMENT_SLUG_NOT_FOUND = -3;
|
||||
field public static final int ERROR_FEATURE_NOT_FOUND = -2;
|
||||
field public static final int ERROR_UNKNOWN = -1;
|
||||
@ -942,6 +945,7 @@ package org.mozilla.geckoview {
|
||||
method @AnyThread @Nullable public ContentBlocking.Delegate getContentBlockingDelegate();
|
||||
method @Nullable @UiThread public GeckoSession.ContentDelegate getContentDelegate();
|
||||
method @AnyThread @NonNull public static String getDefaultUserAgent();
|
||||
method @AnyThread @Nullable public ExperimentDelegate getExperimentDelegate();
|
||||
method @AnyThread @NonNull public SessionFinder getFinder();
|
||||
method @AnyThread @Nullable public GeckoSession.HistoryDelegate getHistoryDelegate();
|
||||
method @AnyThread @Nullable public GeckoSession.MediaDelegate getMediaDelegate();
|
||||
@ -987,6 +991,7 @@ package org.mozilla.geckoview {
|
||||
method @UiThread public void setAutofillDelegate(@Nullable Autofill.Delegate);
|
||||
method @AnyThread public void setContentBlockingDelegate(@Nullable ContentBlocking.Delegate);
|
||||
method @UiThread public void setContentDelegate(@Nullable GeckoSession.ContentDelegate);
|
||||
method @AnyThread public void setExperimentDelegate(@Nullable ExperimentDelegate);
|
||||
method @AnyThread public void setFocused(boolean);
|
||||
method @AnyThread public void setHistoryDelegate(@Nullable GeckoSession.HistoryDelegate);
|
||||
method @AnyThread public void setMediaDelegate(@Nullable GeckoSession.MediaDelegate);
|
||||
@ -1038,7 +1043,7 @@ package org.mozilla.geckoview {
|
||||
method @UiThread default public void onFirstContentfulPaint(@NonNull GeckoSession);
|
||||
method @UiThread default public void onFocusRequest(@NonNull GeckoSession);
|
||||
method @UiThread default public void onFullScreen(@NonNull GeckoSession, boolean);
|
||||
method @AnyThread @Nullable default public JSONObject onGetNimbusFeature(@NonNull GeckoSession, @NonNull String);
|
||||
method @AnyThread @Deprecated @DeprecationSchedule(version=122,id="session-nimbus") @Nullable default public JSONObject onGetNimbusFeature(@NonNull GeckoSession, @NonNull String);
|
||||
method @UiThread default public void onKill(@NonNull GeckoSession);
|
||||
method @UiThread default public void onMetaViewportFitChange(@NonNull GeckoSession, @NonNull String);
|
||||
method @UiThread default public void onPaintStatusReset(@NonNull GeckoSession);
|
||||
|
@ -29,6 +29,7 @@ class ExperimentDelegateTest : BaseSessionTest() {
|
||||
mainSession.loadTestPath(TRACEMONKEY_PDF_PATH)
|
||||
|
||||
sessionRule.waitUntilCalled(object : ContentDelegate {
|
||||
@Deprecated("Changing to Experiment Delegate in Bug 1840658.")
|
||||
override fun onGetNimbusFeature(session: GeckoSession, featureId: String): JSONObject? {
|
||||
assertThat(
|
||||
"Feature id should match",
|
||||
|
@ -80,7 +80,7 @@ public class RuntimeCreator {
|
||||
if (delegate != null) {
|
||||
return delegate.onGetExperimentFeature(feature);
|
||||
}
|
||||
return null;
|
||||
return ExperimentDelegate.super.onGetExperimentFeature(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -88,7 +88,7 @@ public class RuntimeCreator {
|
||||
if (delegate != null) {
|
||||
return delegate.onRecordExposureEvent(feature);
|
||||
}
|
||||
return null;
|
||||
return ExperimentDelegate.super.onRecordExposureEvent(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -97,7 +97,7 @@ public class RuntimeCreator {
|
||||
if (delegate != null) {
|
||||
return delegate.onRecordExperimentExposureEvent(feature, slug);
|
||||
}
|
||||
return null;
|
||||
return ExperimentDelegate.super.onRecordExperimentExposureEvent(feature, slug);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -106,7 +106,7 @@ public class RuntimeCreator {
|
||||
if (delegate != null) {
|
||||
return delegate.onRecordMalformedConfigurationEvent(feature, part);
|
||||
}
|
||||
return null;
|
||||
return ExperimentDelegate.super.onRecordMalformedConfigurationEvent(feature, part);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
package org.mozilla.geckoview;
|
||||
|
||||
import static org.mozilla.geckoview.ExperimentDelegate.ExperimentException.ERROR_EXPERIMENT_DELEGATE_NOT_IMPLEMENTED;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
@ -42,7 +44,10 @@ public interface ExperimentDelegate {
|
||||
*/
|
||||
@AnyThread
|
||||
default @NonNull GeckoResult<JSONObject> onGetExperimentFeature(@NonNull String feature) {
|
||||
return null;
|
||||
final GeckoResult<JSONObject> result = new GeckoResult<>();
|
||||
result.completeExceptionally(
|
||||
new ExperimentException(ERROR_EXPERIMENT_DELEGATE_NOT_IMPLEMENTED));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,7 +69,10 @@ public interface ExperimentDelegate {
|
||||
*/
|
||||
@AnyThread
|
||||
default @NonNull GeckoResult<Void> onRecordExposureEvent(@NonNull String feature) {
|
||||
return null;
|
||||
final GeckoResult<Void> result = new GeckoResult<>();
|
||||
result.completeExceptionally(
|
||||
new ExperimentException(ERROR_EXPERIMENT_DELEGATE_NOT_IMPLEMENTED));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,7 +94,10 @@ public interface ExperimentDelegate {
|
||||
@AnyThread
|
||||
default @NonNull GeckoResult<Void> onRecordExperimentExposureEvent(
|
||||
@NonNull String feature, @NonNull String slug) {
|
||||
return null;
|
||||
final GeckoResult<Void> result = new GeckoResult<>();
|
||||
result.completeExceptionally(
|
||||
new ExperimentException(ERROR_EXPERIMENT_DELEGATE_NOT_IMPLEMENTED));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,7 +113,10 @@ public interface ExperimentDelegate {
|
||||
@AnyThread
|
||||
default @NonNull GeckoResult<Void> onRecordMalformedConfigurationEvent(
|
||||
@NonNull String feature, @NonNull String part) {
|
||||
return null;
|
||||
final GeckoResult<Void> result = new GeckoResult<>();
|
||||
result.completeExceptionally(
|
||||
new ExperimentException(ERROR_EXPERIMENT_DELEGATE_NOT_IMPLEMENTED));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,9 +143,18 @@ public interface ExperimentDelegate {
|
||||
/** The experiment slug was not available. */
|
||||
public static final int ERROR_EXPERIMENT_SLUG_NOT_FOUND = -3;
|
||||
|
||||
/** The experiment delegate is not implemented. */
|
||||
public static final int ERROR_EXPERIMENT_DELEGATE_NOT_IMPLEMENTED = -4;
|
||||
|
||||
/** Experiment exception error codes. */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(value = {ERROR_UNKNOWN, ERROR_FEATURE_NOT_FOUND, ERROR_EXPERIMENT_SLUG_NOT_FOUND})
|
||||
@IntDef(
|
||||
value = {
|
||||
ERROR_UNKNOWN,
|
||||
ERROR_FEATURE_NOT_FOUND,
|
||||
ERROR_EXPERIMENT_SLUG_NOT_FOUND,
|
||||
ERROR_EXPERIMENT_DELEGATE_NOT_IMPLEMENTED
|
||||
})
|
||||
public @interface Codes {}
|
||||
|
||||
/** One of {@link Codes} that provides more information about this exception. */
|
||||
|
@ -839,6 +839,95 @@ public class GeckoSession {
|
||||
}
|
||||
};
|
||||
|
||||
private final GeckoSessionHandler<ExperimentDelegate> mExperimentHandler =
|
||||
new GeckoSessionHandler<ExperimentDelegate>(
|
||||
"GeckoViewExperiment",
|
||||
this,
|
||||
new String[] {
|
||||
"GeckoView:GetExperimentFeature",
|
||||
"GeckoView:RecordExposure",
|
||||
"GeckoView:RecordExperimentExposure",
|
||||
"GeckoView:RecordMalformedConfig"
|
||||
}) {
|
||||
@Override
|
||||
public void handleMessage(
|
||||
final ExperimentDelegate delegate,
|
||||
final String event,
|
||||
final GeckoBundle message,
|
||||
final EventCallback callback) {
|
||||
|
||||
if (delegate == null) {
|
||||
if (callback != null) {
|
||||
callback.sendError("No experiment delegate registered.");
|
||||
}
|
||||
Log.w(LOGTAG, "No experiment delegate registered.");
|
||||
return;
|
||||
}
|
||||
final String feature = message.getString("feature", "");
|
||||
if ("GeckoView:GetExperimentFeature".equals(event) && callback != null) {
|
||||
final GeckoResult<JSONObject> result = delegate.onGetExperimentFeature(feature);
|
||||
result
|
||||
.accept(
|
||||
json -> {
|
||||
try {
|
||||
callback.sendSuccess(GeckoBundle.fromJSONObject(json));
|
||||
} catch (final JSONException e) {
|
||||
callback.sendError("An error occured when serializing the feature data.");
|
||||
}
|
||||
})
|
||||
.exceptionally(
|
||||
e -> {
|
||||
callback.sendError("An error occurred while retrieving feature data.");
|
||||
return null;
|
||||
});
|
||||
|
||||
} else if ("GeckoView:RecordExposure".equals(event) && callback != null) {
|
||||
final GeckoResult<Void> result = delegate.onRecordExposureEvent(feature);
|
||||
result
|
||||
.accept(
|
||||
a -> {
|
||||
callback.sendSuccess(true);
|
||||
})
|
||||
.exceptionally(
|
||||
e -> {
|
||||
callback.sendError("An error occurred while recording feature.");
|
||||
return null;
|
||||
});
|
||||
|
||||
} else if ("GeckoView:RecordExperimentExposure".equals(event) && callback != null) {
|
||||
final String slug = message.getString("slug", "");
|
||||
final GeckoResult<Void> result =
|
||||
delegate.onRecordExperimentExposureEvent(feature, slug);
|
||||
result
|
||||
.accept(
|
||||
a -> {
|
||||
callback.sendSuccess(true);
|
||||
})
|
||||
.exceptionally(
|
||||
e -> {
|
||||
callback.sendError("An error occurred while recording experiment feature.");
|
||||
return null;
|
||||
});
|
||||
|
||||
} else if ("GeckoView:RecordMalformedConfig".equals(event) && callback != null) {
|
||||
final String part = message.getString("part", "");
|
||||
final GeckoResult<Void> result =
|
||||
delegate.onRecordMalformedConfigurationEvent(feature, part);
|
||||
result
|
||||
.accept(
|
||||
a -> {
|
||||
callback.sendSuccess(true);
|
||||
})
|
||||
.exceptionally(
|
||||
e -> {
|
||||
callback.sendError(
|
||||
"An error occurred while recording malformed feature config.");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final GeckoSessionHandler<ContentDelegate> mProcessHangHandler =
|
||||
new GeckoSessionHandler<ContentDelegate>(
|
||||
"GeckoViewProcessHangMonitor", this, new String[] {"GeckoView:HangReport"}) {
|
||||
@ -1146,7 +1235,8 @@ public class GeckoSession {
|
||||
mScrollHandler,
|
||||
mSelectionActionDelegate,
|
||||
mContentBlockingHandler,
|
||||
mMediaSessionHandler
|
||||
mMediaSessionHandler,
|
||||
mExperimentHandler
|
||||
};
|
||||
|
||||
private static class PermissionCallback
|
||||
@ -1650,6 +1740,7 @@ public class GeckoSession {
|
||||
mId = id;
|
||||
mWindow = new Window(runtime, this, mNativeQueue);
|
||||
mWebExtensionController.setRuntime(runtime);
|
||||
mExperimentHandler.setDelegate(getRuntimeExperimentDelegate(), this);
|
||||
|
||||
onWindowChanged(WINDOW_OPEN, /* inProgress */ true);
|
||||
|
||||
@ -3908,13 +3999,18 @@ public class GeckoSession {
|
||||
default void onCookieBannerHandled(@NonNull final GeckoSession session) {}
|
||||
|
||||
/**
|
||||
* This method is called when GeckoView is requesting a specific Nimbus feature in using message
|
||||
* `GeckoView:GetNimbusFeature`.
|
||||
* This method is scheduled for deprecation, see Bug 1846074 for details. Please switch to the
|
||||
* [ExperimentDelegate.onGetExperimentFeature] for the same functionality.
|
||||
*
|
||||
* <p>This method is called when GeckoView is requesting a specific Nimbus feature in using
|
||||
* message `GeckoView:GetNimbusFeature`.
|
||||
*
|
||||
* @param session GeckoSession that initiated the callback.
|
||||
* @param featureId Nimbus feature id of the collected data.
|
||||
* @return A {@link JSONObject} with the feature.
|
||||
*/
|
||||
@Deprecated
|
||||
@DeprecationSchedule(version = 122, id = "session-nimbus")
|
||||
@AnyThread
|
||||
default @Nullable JSONObject onGetNimbusFeature(
|
||||
@NonNull final GeckoSession session, @NonNull final String featureId) {
|
||||
@ -7679,6 +7775,45 @@ public class GeckoSession {
|
||||
mPrintHandler.setDelegate(delegate, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the experiment delegate for this session.
|
||||
*
|
||||
* @return The current {@link ExperimentDelegate} for this session, if any.
|
||||
*/
|
||||
@AnyThread
|
||||
public @Nullable ExperimentDelegate getExperimentDelegate() {
|
||||
return mExperimentHandler.getDelegate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the experiment delegate from the runtime.
|
||||
*
|
||||
* @return The current {@link ExperimentDelegate} for the runtime or null.
|
||||
*/
|
||||
@AnyThread
|
||||
private @Nullable ExperimentDelegate getRuntimeExperimentDelegate() {
|
||||
final GeckoRuntime runtime = this.getRuntime();
|
||||
if (runtime != null) {
|
||||
final GeckoRuntimeSettings runtimeSettings = runtime.getSettings();
|
||||
if (runtimeSettings != null) {
|
||||
return runtimeSettings.getExperimentDelegate();
|
||||
}
|
||||
}
|
||||
Log.w(LOGTAG, "Could not retrieve experiment delegate from runtime.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the experiment delegate for this session. Default is set to the runtime experiment
|
||||
* delegate.
|
||||
*
|
||||
* @param delegate An instance of {@link ExperimentDelegate}.
|
||||
*/
|
||||
@AnyThread
|
||||
public void setExperimentDelegate(final @Nullable ExperimentDelegate delegate) {
|
||||
mExperimentHandler.setDelegate(delegate, this);
|
||||
}
|
||||
|
||||
/** Thrown when failure occurs when printing from a website. */
|
||||
@WrapForJNI
|
||||
public static class GeckoPrintException extends Exception {
|
||||
|
@ -22,6 +22,8 @@ exclude: true
|
||||
- Added [`ERROR_INCOMPATIBLE`][118.6] to `WebExtension.InstallException.ErrorCodes`. ([bug 1845749]({{bugzilla}}1845749))
|
||||
- Added [`GeckoRuntimeSettings.Builder.extensionsWebAPIEnabled`][118.7]. ([bug 1847173]({{bugzilla}}1847173))
|
||||
- Changed [`GeckoSession.AccountSelectorPrompt`][118.8]: added the Provider to which the Account belongs ([bug 1847059]({{bugzilla}}1847059))
|
||||
- Added [`getExperimentDelegate`][118.9] and [`setExperimentDelegate`][118.10] to the GeckoSession allow GeckoView to get and set the experiment delegate for the session. Default is to use the runtime delegate.
|
||||
- ⚠️ Deprecated [`onGetNimbusFeature`][115.5] by 122, please use `ExperimentDelegate.onGetExperimentFeature` instead.
|
||||
|
||||
[118.1]: {{javadoc_uri}}/ExperimentDelegate.html
|
||||
[118.2]: {{javadoc_uri}}/WebExtension.InstallException.ErrorCodes.html#ERROR_BLOCKLISTED
|
||||
@ -31,6 +33,8 @@ exclude: true
|
||||
[118.6]: {{javadoc_uri}}/WebExtension.InstallException.ErrorCodes.html#ERROR_INCOMPATIBLE
|
||||
[118.7]: {{javadoc_uri}}/GeckoRuntimeSettings.Builder.html#extensionsWebAPIEnabled(boolean)
|
||||
[118.8]: {{javadoc_uri}}/GeckoSession.html#AccountSelectorPrompt
|
||||
[118.9]: {{javadoc_uri}}/GeckoSession.html#getExperimentDelegate()
|
||||
[118.10]: {{javadoc_uri}}/GeckoSession.html#setExperimentDelegate(org.mozilla.geckoview.ExperimentDelegate)
|
||||
|
||||
## v116
|
||||
- Added [`GeckoSession.didPrintPageContent`][116.1] to included extra print status for a standard print and new `GeckoPrintException.ERROR_NO_PRINT_DELEGATE`
|
||||
@ -1411,4 +1415,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]: d735eedb188b91b0495d3c2667807f29f6915263
|
||||
[api-version]: 4a2f7cc3f89c0baa015d23f9d17fbea7e69e4aff
|
||||
|
@ -3,6 +3,10 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
package org.mozilla.geckoview.test_runner;
|
||||
|
||||
import static org.mozilla.geckoview.ExperimentDelegate.ExperimentException.ERROR_EXPERIMENT_SLUG_NOT_FOUND;
|
||||
import static org.mozilla.geckoview.ExperimentDelegate.ExperimentException.ERROR_FEATURE_NOT_FOUND;
|
||||
import static org.mozilla.geckoview.ExperimentDelegate.ExperimentException.ERROR_UNKNOWN;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@ -16,8 +20,11 @@ import java.util.ArrayDeque;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.geckoview.AllowOrDeny;
|
||||
import org.mozilla.geckoview.ContentBlocking;
|
||||
import org.mozilla.geckoview.ExperimentDelegate;
|
||||
import org.mozilla.geckoview.GeckoDisplay;
|
||||
import org.mozilla.geckoview.GeckoResult;
|
||||
import org.mozilla.geckoview.GeckoRuntime;
|
||||
@ -415,7 +422,8 @@ public class TestRunnerActivity extends Activity {
|
||||
.arguments(new String[] {"-purgecaches"})
|
||||
.displayDpiOverride(160)
|
||||
.displayDensityOverride(1.0f)
|
||||
.remoteDebuggingEnabled(true);
|
||||
.remoteDebuggingEnabled(true)
|
||||
.experimentDelegate(new TestRunnerExperimentDelegate());
|
||||
|
||||
final Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
@ -650,4 +658,58 @@ public class TestRunnerActivity extends Activity {
|
||||
public GeckoSession getGeckoSession() {
|
||||
return mSession;
|
||||
}
|
||||
|
||||
class TestRunnerExperimentDelegate implements ExperimentDelegate {
|
||||
@Override
|
||||
public GeckoResult<JSONObject> onGetExperimentFeature(@NonNull String feature) {
|
||||
GeckoResult<JSONObject> result = new GeckoResult<>();
|
||||
if (feature.equals("test")) {
|
||||
try {
|
||||
result.complete(new JSONObject().put("item-one", true).put("item-two", 5));
|
||||
} catch (JSONException e) {
|
||||
result.completeExceptionally(new ExperimentException(ERROR_UNKNOWN));
|
||||
}
|
||||
} else {
|
||||
result.completeExceptionally(new ExperimentException(ERROR_FEATURE_NOT_FOUND));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeckoResult<Void> onRecordExposureEvent(@NonNull String feature) {
|
||||
GeckoResult<Void> result = new GeckoResult<>();
|
||||
if (feature.equals("test")) {
|
||||
result.complete(null);
|
||||
} else {
|
||||
result.completeExceptionally(new ExperimentException(ERROR_FEATURE_NOT_FOUND));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeckoResult<Void> onRecordExperimentExposureEvent(
|
||||
@NonNull String feature, @NonNull String slug) {
|
||||
GeckoResult<Void> result = new GeckoResult<>();
|
||||
if (feature.equals("test") && slug.equals("test")) {
|
||||
result.complete(null);
|
||||
} else if (!slug.equals("test") && feature.equals("test")) {
|
||||
result.completeExceptionally(new ExperimentException(ERROR_EXPERIMENT_SLUG_NOT_FOUND));
|
||||
} else {
|
||||
result.completeExceptionally(new ExperimentException(ERROR_FEATURE_NOT_FOUND));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeckoResult<Void> onRecordMalformedConfigurationEvent(
|
||||
@NonNull String feature, @NonNull String part) {
|
||||
GeckoResult<Void> result = new GeckoResult<>();
|
||||
if (feature.equals("test")) {
|
||||
result.complete(null);
|
||||
} else {
|
||||
result.completeExceptionally(new ExperimentException(ERROR_FEATURE_NOT_FOUND));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user