forked from mirrors/gecko-dev
Bug 1845834 - Expose toolkit attribution event API to GV r=geckoview-reviewers,owlish
Differential Revision: https://phabricator.services.mozilla.com/D191135
This commit is contained in:
parent
a8213d2010
commit
f25a8b0ffa
8 changed files with 170 additions and 14 deletions
|
|
@ -268,6 +268,13 @@ pref("formhelper.autozoom", true);
|
||||||
// Optionally send web console output to logcat (bug 1415318)
|
// Optionally send web console output to logcat (bug 1415318)
|
||||||
pref("geckoview.console.enabled", false);
|
pref("geckoview.console.enabled", false);
|
||||||
|
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
|
pref("geckoview.shopping.enabled", true);
|
||||||
|
// Testing flag for geckoview shopping product. When true, sendAttributionEvent
|
||||||
|
// will return "TEST_AID_RESPONSE" for products with "TEST_AID" id.
|
||||||
|
pref("geckoview.shopping.test_response", false);
|
||||||
|
#endif
|
||||||
|
|
||||||
pref("image.cache.size", 1048576); // bytes
|
pref("image.cache.size", 1048576); // bytes
|
||||||
|
|
||||||
// Inherit locale from the OS, used for multi-locale builds
|
// Inherit locale from the OS, used for multi-locale builds
|
||||||
|
|
|
||||||
|
|
@ -1010,6 +1010,8 @@ package org.mozilla.geckoview {
|
||||||
method @AnyThread @NonNull public GeckoResult<List<GeckoSession.Recommendation>> requestRecommendations(@NonNull String);
|
method @AnyThread @NonNull public GeckoResult<List<GeckoSession.Recommendation>> requestRecommendations(@NonNull String);
|
||||||
method @AnyThread public void restoreState(@NonNull GeckoSession.SessionState);
|
method @AnyThread public void restoreState(@NonNull GeckoSession.SessionState);
|
||||||
method @AnyThread @NonNull public GeckoResult<InputStream> saveAsPdf();
|
method @AnyThread @NonNull public GeckoResult<InputStream> saveAsPdf();
|
||||||
|
method @AnyThread @NonNull public GeckoResult<Boolean> sendClickAttributionEvent(@NonNull String);
|
||||||
|
method @AnyThread @NonNull public GeckoResult<Boolean> sendImpressionAttributionEvent(@NonNull String);
|
||||||
method @AnyThread public void setActive(boolean);
|
method @AnyThread public void setActive(boolean);
|
||||||
method @UiThread public void setAutofillDelegate(@Nullable Autofill.Delegate);
|
method @UiThread public void setAutofillDelegate(@Nullable Autofill.Delegate);
|
||||||
method @AnyThread public void setContentBlockingDelegate(@Nullable ContentBlocking.Delegate);
|
method @AnyThread public void setContentBlockingDelegate(@Nullable ContentBlocking.Delegate);
|
||||||
|
|
|
||||||
|
|
@ -823,4 +823,50 @@ class ContentDelegateTest : BaseSessionTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sendAttributionEvents() {
|
||||||
|
// TODO (bug 1861175): enable in automation
|
||||||
|
if (!sessionRule.env.isAutomation) {
|
||||||
|
assumeThat(sessionRule.env.isNightly, equalTo(true))
|
||||||
|
|
||||||
|
// Checks that the pref value is also consistent with the runtime settings
|
||||||
|
val originalPrefs = sessionRule.getPrefs(
|
||||||
|
"geckoview.shopping.test_response",
|
||||||
|
)
|
||||||
|
assertThat("Pref is correct", originalPrefs[0] as Boolean, equalTo(false))
|
||||||
|
|
||||||
|
val aid = "TEST_AID"
|
||||||
|
val invalidClickResult = mainSession.sendClickAttributionEvent(aid)
|
||||||
|
assertThat(
|
||||||
|
"Click event success result should be false",
|
||||||
|
sessionRule.waitForResult(invalidClickResult),
|
||||||
|
equalTo(false),
|
||||||
|
)
|
||||||
|
val invalidImpressionResult = mainSession.sendImpressionAttributionEvent(aid)
|
||||||
|
assertThat(
|
||||||
|
"Impression event result result should be false",
|
||||||
|
sessionRule.waitForResult(invalidImpressionResult),
|
||||||
|
equalTo(false),
|
||||||
|
)
|
||||||
|
|
||||||
|
sessionRule.setPrefsUntilTestEnd(
|
||||||
|
mapOf(
|
||||||
|
"geckoview.shopping.test_response" to true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val validClickResult = mainSession.sendClickAttributionEvent(aid)
|
||||||
|
assertThat(
|
||||||
|
"Click event success result should be true",
|
||||||
|
sessionRule.waitForResult(validClickResult),
|
||||||
|
equalTo(true),
|
||||||
|
)
|
||||||
|
val validImpressionResult = mainSession.sendImpressionAttributionEvent(aid)
|
||||||
|
assertThat(
|
||||||
|
"Impression event success result should be true",
|
||||||
|
sessionRule.waitForResult(validImpressionResult),
|
||||||
|
equalTo(true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3041,6 +3041,32 @@ public class GeckoSession {
|
||||||
return mEventDispatcher.queryString("GeckoView:PollForAnalysisCompleted", bundle);
|
return mEventDispatcher.queryString("GeckoView:PollForAnalysisCompleted", bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a click event to the Ad Attribution API.
|
||||||
|
*
|
||||||
|
* @param aid Ad id of the recommended product.
|
||||||
|
* @return a {@link GeckoResult} result of whether or not sending the event was successful.
|
||||||
|
*/
|
||||||
|
@AnyThread
|
||||||
|
public @NonNull GeckoResult<Boolean> sendClickAttributionEvent(@NonNull final String aid) {
|
||||||
|
final GeckoBundle bundle = new GeckoBundle(1);
|
||||||
|
bundle.putString("aid", aid);
|
||||||
|
return mEventDispatcher.queryBoolean("GeckoView:SendClickAttributionEvent", bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an impression event to the Ad Attribution API.
|
||||||
|
*
|
||||||
|
* @param aid Ad id of the recommended product.
|
||||||
|
* @return a {@link GeckoResult} result of whether or not sending the event was successful.
|
||||||
|
*/
|
||||||
|
@AnyThread
|
||||||
|
public @NonNull GeckoResult<Boolean> sendImpressionAttributionEvent(@NonNull final String aid) {
|
||||||
|
final GeckoBundle bundle = new GeckoBundle(1);
|
||||||
|
bundle.putString("aid", aid);
|
||||||
|
return mEventDispatcher.queryBoolean("GeckoView:SendImpressionAttributionEvent", bundle);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request product recommendations given a specific product url.
|
* Request product recommendations given a specific product url.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,14 @@ exclude: true
|
||||||
## v121
|
## v121
|
||||||
- Added runtime controller functions. [`RuntimeTranslation`][121.1] has options for retrieving translation languages and managing language models.
|
- Added runtime controller functions. [`RuntimeTranslation`][121.1] has options for retrieving translation languages and managing language models.
|
||||||
- Added support for controlling `cookiebanners.service.enableGlobalRules` and `cookiebanners.service.enableGlobalRules.subFrames` via [`GeckoSession.ContentDelegate.cookieBannerGlobalRulesEnabled`][121.2] and [`GeckoSession.ContentDelegate.cookieBannerGlobalRulesSubFramesEnabled`][121.3].
|
- Added support for controlling `cookiebanners.service.enableGlobalRules` and `cookiebanners.service.enableGlobalRules.subFrames` via [`GeckoSession.ContentDelegate.cookieBannerGlobalRulesEnabled`][121.2] and [`GeckoSession.ContentDelegate.cookieBannerGlobalRulesSubFramesEnabled`][121.3].
|
||||||
|
- Added [`GeckoSession.sendClickAttributionEvent`][121.4] for sending click attribution event for a given product recommendation.
|
||||||
|
- Added [`GeckoSession.sendImpressionAttributionEvent`][121.5] for sending impression attribution event for a given product recommendation.
|
||||||
|
|
||||||
[121.1]: {{javadoc_uri}}/TranslationsController.RuntimeTranslation.html
|
[121.1]: {{javadoc_uri}}/TranslationsController.RuntimeTranslation.html
|
||||||
[121.2]: {{javadoc_uri}}/ContentBlocking.Settings.Builder.html#cookieBannerGlobalRulesEnabled(boolean)
|
[121.2]: {{javadoc_uri}}/ContentBlocking.Settings.Builder.html#cookieBannerGlobalRulesEnabled(boolean)
|
||||||
[121.3]: {{javadoc_uri}}/ContentBlocking.Settings.Builder.html#cookieBannerGlobalRulesSubFramesEnabled(boolean)
|
[121.3]: {{javadoc_uri}}/ContentBlocking.Settings.Builder.html#cookieBannerGlobalRulesSubFramesEnabled(boolean)
|
||||||
|
[121.4]: {{javadoc_uri}}/GeckoSession.html#sendClickAttributionEvent(String)
|
||||||
|
[121.5: {{javadoc_uri}}/GeckoSession.html#sendImpressionAttributionEvent(String)
|
||||||
|
|
||||||
## v120
|
## v120
|
||||||
- Added [`disableExtensionProcessSpawning`][120.1] for disabling the extension process spawning. ([bug 1855405]({{bugzilla}}1855405))
|
- Added [`disableExtensionProcessSpawning`][120.1] for disabling the extension process spawning. ([bug 1855405]({{bugzilla}}1855405))
|
||||||
|
|
@ -1457,4 +1461,4 @@ to allow adding gecko profiler markers.
|
||||||
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport(android.content.Context,android.os.Bundle,java.lang.String)
|
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport(android.content.Context,android.os.Bundle,java.lang.String)
|
||||||
[65.25]: {{javadoc_uri}}/GeckoResult.html
|
[65.25]: {{javadoc_uri}}/GeckoResult.html
|
||||||
|
|
||||||
[api-version]: d303487577022d159dbb7a4b18c339b555a48a45
|
[api-version]: c7dae91b8e81ae247eeec1bcf419f3cab0b76e40
|
||||||
|
|
|
||||||
|
|
@ -1305,6 +1305,9 @@ public class GeckoViewActivity extends AppCompatActivity
|
||||||
case R.id.request_shopping_analysis:
|
case R.id.request_shopping_analysis:
|
||||||
requestAnalysis(session, mCurrentUri);
|
requestAnalysis(session, mCurrentUri);
|
||||||
break;
|
break;
|
||||||
|
case R.id.request_shopping_recommendations:
|
||||||
|
requestRecommendations(session, mCurrentUri);
|
||||||
|
break;
|
||||||
case R.id.create_shopping_analysis:
|
case R.id.create_shopping_analysis:
|
||||||
requestCreateAnalysis(session, mCurrentUri);
|
requestCreateAnalysis(session, mCurrentUri);
|
||||||
break;
|
break;
|
||||||
|
|
@ -2450,7 +2453,41 @@ public class GeckoViewActivity extends AppCompatActivity
|
||||||
|
|
||||||
public void requestRecommendations(
|
public void requestRecommendations(
|
||||||
@NonNull final GeckoSession session, @NonNull final String url) {
|
@NonNull final GeckoSession session, @NonNull final String url) {
|
||||||
session.requestRecommendations(url);
|
GeckoResult<List<GeckoSession.Recommendation>> result = session.requestRecommendations(url);
|
||||||
|
result.map(
|
||||||
|
recs -> {
|
||||||
|
List<String> aids = new ArrayList<>();
|
||||||
|
for (int i = 0; i < recs.size(); ++i) {
|
||||||
|
aids.add(recs.get(i).aid);
|
||||||
|
}
|
||||||
|
if (aids.size() >= 1) {
|
||||||
|
Log.d(LOGTAG, "Sending attribution events to first AID: " + aids.get(0));
|
||||||
|
session
|
||||||
|
.sendClickAttributionEvent(aids.get(0))
|
||||||
|
.then(
|
||||||
|
new GeckoResult.OnValueListener<Boolean, Void>() {
|
||||||
|
@Override
|
||||||
|
public GeckoResult<Void> onValue(final Boolean isSuccessful) {
|
||||||
|
Log.d(LOGTAG, "Success of click attribution event: " + isSuccessful);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
session
|
||||||
|
.sendImpressionAttributionEvent(aids.get(0))
|
||||||
|
.then(
|
||||||
|
new GeckoResult.OnValueListener<Boolean, Void>() {
|
||||||
|
@Override
|
||||||
|
public GeckoResult<Void> onValue(final Boolean isSuccessful) {
|
||||||
|
Log.d(LOGTAG, "Success of impression attribution event: " + isSuccessful);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Log.d(LOGTAG, "No shopping recommendations. No attribution events were sent.");
|
||||||
|
}
|
||||||
|
return recs;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ExampleNavigationDelegate implements GeckoSession.NavigationDelegate {
|
private class ExampleNavigationDelegate implements GeckoSession.NavigationDelegate {
|
||||||
|
|
@ -2465,7 +2502,6 @@ public class GeckoViewActivity extends AppCompatActivity
|
||||||
mTrackingProtectionPermission = getTrackingProtectionPermission(perms);
|
mTrackingProtectionPermission = getTrackingProtectionPermission(perms);
|
||||||
mCurrentUri = url;
|
mCurrentUri = url;
|
||||||
requestAnalysis(session, url);
|
requestAnalysis(session, url);
|
||||||
requestRecommendations(session, url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
<item android:title="@string/save_pdf" android:id="@+id/save_pdf"/>
|
<item android:title="@string/save_pdf" android:id="@+id/save_pdf"/>
|
||||||
<item android:title="@string/print_page" android:id="@+id/print_page"/>
|
<item android:title="@string/print_page" android:id="@+id/print_page"/>
|
||||||
<item android:title="Request Shopping Analysis" android:id="@+id/request_shopping_analysis"/>
|
<item android:title="Request Shopping Analysis" android:id="@+id/request_shopping_analysis"/>
|
||||||
|
<item android:title="Get Shopping Recommendations" android:id="@+id/request_shopping_recommendations"/>
|
||||||
<item android:title="Create Shopping Analysis" android:id="@+id/create_shopping_analysis"/>
|
<item android:title="Create Shopping Analysis" android:id="@+id/create_shopping_analysis"/>
|
||||||
<item android:title="Get Shopping Analysis Status" android:id="@+id/get_shopping_analysis_status"/>
|
<item android:title="Get Shopping Analysis Status" android:id="@+id/get_shopping_analysis_status"/>
|
||||||
<item android:title="Poll Until Analysis Completed" android:id="@+id/poll_shopping_analysis_status"/>
|
<item android:title="Poll Until Analysis Completed" android:id="@+id/poll_shopping_analysis_status"/>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import { GeckoViewModule } from "resource://gre/modules/GeckoViewModule.sys.mjs";
|
import { GeckoViewModule } from "resource://gre/modules/GeckoViewModule.sys.mjs";
|
||||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
|
||||||
|
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
ChromeUtils.defineESModuleGetters(lazy, {
|
ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
|
|
@ -24,6 +23,8 @@ export class GeckoViewContent extends GeckoViewModule {
|
||||||
"GeckoView:RequestCreateAnalysis",
|
"GeckoView:RequestCreateAnalysis",
|
||||||
"GeckoView:RequestAnalysisCreationStatus",
|
"GeckoView:RequestAnalysisCreationStatus",
|
||||||
"GeckoView:PollForAnalysisCompleted",
|
"GeckoView:PollForAnalysisCompleted",
|
||||||
|
"GeckoView:SendClickAttributionEvent",
|
||||||
|
"GeckoView:SendImpressionAttributionEvent",
|
||||||
"GeckoView:RequestAnalysis",
|
"GeckoView:RequestAnalysis",
|
||||||
"GeckoView:RequestRecommendations",
|
"GeckoView:RequestRecommendations",
|
||||||
"GeckoView:ScrollBy",
|
"GeckoView:ScrollBy",
|
||||||
|
|
@ -213,6 +214,12 @@ export class GeckoViewContent extends GeckoViewModule {
|
||||||
case "GeckoView:PollForAnalysisCompleted":
|
case "GeckoView:PollForAnalysisCompleted":
|
||||||
this._pollForAnalysisCompleted(aData, aCallback);
|
this._pollForAnalysisCompleted(aData, aCallback);
|
||||||
break;
|
break;
|
||||||
|
case "GeckoView:SendClickAttributionEvent":
|
||||||
|
this._sendAttributionEvent("click", aData, aCallback);
|
||||||
|
break;
|
||||||
|
case "GeckoView:SendImpressionAttributionEvent":
|
||||||
|
this._sendAttributionEvent("impression", aData, aCallback);
|
||||||
|
break;
|
||||||
case "GeckoView:RequestRecommendations":
|
case "GeckoView:RequestRecommendations":
|
||||||
this._requestRecommendations(aData, aCallback);
|
this._requestRecommendations(aData, aCallback);
|
||||||
break;
|
break;
|
||||||
|
|
@ -333,8 +340,8 @@ export class GeckoViewContent extends GeckoViewModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _requestAnalysis(aData, aCallback) {
|
async _requestAnalysis(aData, aCallback) {
|
||||||
if (!AppConstants.NIGHTLY_BUILD) {
|
if (!Services.prefs.getBoolPref("geckoview.shopping.enabled", false)) {
|
||||||
aCallback.onError(`This API enabled for Nightly builds only.`);
|
aCallback.onError(`This API enabled for Shopping only.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const url = Services.io.newURI(aData.url);
|
const url = Services.io.newURI(aData.url);
|
||||||
|
|
@ -352,8 +359,8 @@ export class GeckoViewContent extends GeckoViewModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _requestCreateAnalysis(aData, aCallback) {
|
async _requestCreateAnalysis(aData, aCallback) {
|
||||||
if (!AppConstants.NIGHTLY_BUILD) {
|
if (!Services.prefs.getBoolPref("geckoview.shopping.enabled", false)) {
|
||||||
aCallback.onError(`This API enabled for Nightly builds only.`);
|
aCallback.onError(`This API enabled for Shopping only.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const url = Services.io.newURI(aData.url);
|
const url = Services.io.newURI(aData.url);
|
||||||
|
|
@ -371,8 +378,8 @@ export class GeckoViewContent extends GeckoViewModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _requestAnalysisCreationStatus(aData, aCallback) {
|
async _requestAnalysisCreationStatus(aData, aCallback) {
|
||||||
if (!AppConstants.NIGHTLY_BUILD) {
|
if (!Services.prefs.getBoolPref("geckoview.shopping.enabled", false)) {
|
||||||
aCallback.onError(`This API enabled for Nightly builds only.`);
|
aCallback.onError(`This API enabled for Shopping only.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const url = Services.io.newURI(aData.url);
|
const url = Services.io.newURI(aData.url);
|
||||||
|
|
@ -394,8 +401,8 @@ export class GeckoViewContent extends GeckoViewModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _pollForAnalysisCompleted(aData, aCallback) {
|
async _pollForAnalysisCompleted(aData, aCallback) {
|
||||||
if (!AppConstants.NIGHTLY_BUILD) {
|
if (!Services.prefs.getBoolPref("geckoview.shopping.enabled", false)) {
|
||||||
aCallback.onError(`This API enabled for Nightly builds only.`);
|
aCallback.onError(`This API enabled for Shopping only.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const url = Services.io.newURI(aData.url);
|
const url = Services.io.newURI(aData.url);
|
||||||
|
|
@ -416,9 +423,36 @@ export class GeckoViewContent extends GeckoViewModule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _sendAttributionEvent(aEvent, aData, aCallback) {
|
||||||
|
if (!Services.prefs.getBoolPref("geckoview.shopping.enabled", false)) {
|
||||||
|
aCallback.onError(`This API enabled for Shopping only.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO (bug1859055): remove product object once sendAttributionEvent() is static
|
||||||
|
const product = new lazy.ShoppingProduct(
|
||||||
|
"http://example.com/dp/ABCDEFG123"
|
||||||
|
);
|
||||||
|
let result;
|
||||||
|
if (Services.prefs.getBoolPref("geckoview.shopping.test_response", true)) {
|
||||||
|
result = { TEST_AID: "TEST_AID_RESPONSE" };
|
||||||
|
} else {
|
||||||
|
// TODO (bug 1860897): source will be changed to geckoview_android
|
||||||
|
result = await product.sendAttributionEvent(
|
||||||
|
aEvent,
|
||||||
|
aData.aid,
|
||||||
|
"firefox_android"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!result || !(aData.aid in result) || !result[aData.aid]) {
|
||||||
|
aCallback.onSuccess(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aCallback.onSuccess(true);
|
||||||
|
}
|
||||||
|
|
||||||
async _requestRecommendations(aData, aCallback) {
|
async _requestRecommendations(aData, aCallback) {
|
||||||
if (!AppConstants.NIGHTLY_BUILD) {
|
if (!Services.prefs.getBoolPref("geckoview.shopping.enabled", false)) {
|
||||||
aCallback.onError(`This API enabled for Nightly builds only.`);
|
aCallback.onError(`This API enabled for Shopping only.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const url = Services.io.newURI(aData.url);
|
const url = Services.io.newURI(aData.url);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue