Skip to content

Commit

Permalink
Bug 1840690 - Expose requestRecommendations to GV r=geckoview-reviewe…
Browse files Browse the repository at this point in the history
…rs,ohall,jonalmeida,calu

Differential Revision: https://phabricator.services.mozilla.com/D184768
  • Loading branch information
calumozilla committed Aug 3, 2023
1 parent 07912c4 commit bec83a8
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 26 deletions.
23 changes: 15 additions & 8 deletions mobile/android/geckoview/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,7 @@ package org.mozilla.geckoview {
method @AnyThread public void reload();
method @AnyThread public void reload(int);
method @NonNull @UiThread public GeckoResult<GeckoSession.ReviewAnalysis> requestAnalysis(@NonNull String);
method @NonNull @UiThread public GeckoResult<List<GeckoSession.Recommendation>> requestRecommendations(@NonNull String);
method @AnyThread public void restoreState(@NonNull GeckoSession.SessionState);
method @AnyThread @NonNull public GeckoResult<InputStream> saveAsPdf();
method @AnyThread public void setActive(boolean);
Expand Down Expand Up @@ -1594,16 +1595,25 @@ package org.mozilla.geckoview {
field @Nullable public final String message;
}

@AnyThread public static class GeckoSession.Recommendation {
ctor protected Recommendation();
field @Nullable public final Double adjustedRating;
field @Nullable public final String aid;
field @Nullable public final String analysisUrl;
field @Nullable public final String currency;
field @Nullable public final String grade;
field @Nullable public final String imageUrl;
field @Nullable public final String name;
field @Nullable public final String price;
field @Nullable public final Boolean sponsored;
field @Nullable public final String url;
}

@Retention(value=RetentionPolicy.SOURCE) public static interface GeckoSession.RestartReason {
}

@AnyThread public static class GeckoSession.ReviewAnalysis {
ctor protected ReviewAnalysis();
field public static final String GRADE_A = "A";
field public static final String GRADE_B = "B";
field public static final String GRADE_C = "C";
field public static final String GRADE_D = "D";
field public static final String GRADE_E = "E";
field @NonNull public final Double adjustedRating;
field @Nullable public final String analysisURL;
field public final boolean deletedProduct;
Expand All @@ -1615,9 +1625,6 @@ package org.mozilla.geckoview {
field @Nullable public final String productId;
}

@Retention(value=RetentionPolicy.SOURCE) public static interface GeckoSession.ReviewAnalysis.Grade {
}

public class GeckoSession.ReviewAnalysis.Highlight {
ctor protected Highlight();
field @Nullable public final String[] appearance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import org.mozilla.geckoview.GeckoSession.ContentDelegate
import org.mozilla.geckoview.GeckoSession.NavigationDelegate
import org.mozilla.geckoview.GeckoSession.NavigationDelegate.LoadRequest
import org.mozilla.geckoview.GeckoSession.ProgressDelegate
import org.mozilla.geckoview.GeckoSession.ReviewAnalysis
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.IgnoreCrash
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.NullDelegate
Expand Down Expand Up @@ -678,12 +677,26 @@ class ContentDelegateTest : BaseSessionTest() {
if (!sessionRule.env.isAutomation) {
val result = mainSession.requestAnalysis("https://www.amazon.com/Furmax-Electric-Adjustable-Standing-Computer/dp/B09TJGHL5F/")
sessionRule.waitForResult(result).let {
assertThat("Product grade should match", it.grade, equalTo(ReviewAnalysis.GRADE_A))
assertThat("Product grade should match", it.grade, equalTo("A"))
assertThat("Product id should match", it.productId, equalTo("B09TJGHL5F"))
assertThat("Product adjusted rating should match", it.adjustedRating, equalTo(4.4))
assertThat("Product should not be reported that it was deleted", it.deletedProductReported, equalTo(false))
assertThat("Not a deleted product", it.deletedProduct, equalTo(false))
}
}
}

@Test
fun requestRecommendations() {
// TODO: bug1845760 replace with static example.com product page
if (!sessionRule.env.isAutomation) {
val result = mainSession.requestRecommendations("https://www.amazon.com/Furmax-Electric-Adjustable-Standing-Computer/dp/B09TJGHL5F/")
sessionRule.waitForResult(result)
.let {
assertThat("First recommendation adjusted rating should match", it[0].adjustedRating, equalTo(4.6))
assertThat("Another recommendation adjusted rating should match", it[2].adjustedRating, equalTo(4.5))
assertThat("First recommendation sponsored field should match", it[0].sponsored, equalTo(true))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2917,6 +2917,30 @@ public void restoreState(final @NonNull SessionState state) {
.map(analysisBundle -> new ReviewAnalysis(analysisBundle.getBundle("analysis")));
}

/**
* Request product recommendations given a specific product url.
*
* @param url The URL of the product page.
* @return a {@link GeckoResult} result of product recommendations.
*/
@UiThread
public @NonNull GeckoResult<List<Recommendation>> requestRecommendations(
@NonNull final String url) {
final GeckoBundle bundle = new GeckoBundle(1);
bundle.putString("url", url);
return mEventDispatcher
.queryBundle("GeckoView:RequestRecommendations", bundle)
.map(
recommendationsBundle -> {
final GeckoBundle[] bundles = recommendationsBundle.getBundleArray("recommendations");
final ArrayList<Recommendation> recArray = new ArrayList<>(bundles.length);
for (final GeckoBundle b : bundles) {
recArray.add(new Recommendation(b));
}
return recArray;
});
}

// This is the GeckoDisplay acquired via acquireDisplay(), if any.
private GeckoDisplay mDisplay;

Expand Down Expand Up @@ -3413,24 +3437,14 @@ protected WebResponseInfo() {
/** Contains information about the analysis of a product's reviews. */
@AnyThread
public static class ReviewAnalysis {
@Retention(RetentionPolicy.SOURCE)
@StringDef({GRADE_A, GRADE_B, GRADE_C, GRADE_D, GRADE_E})
public @interface Grade {}

public static final String GRADE_A = "A";
public static final String GRADE_B = "B";
public static final String GRADE_C = "C";
public static final String GRADE_D = "D";
public static final String GRADE_E = "E";

/** Analysis URL. */
@Nullable public final String analysisURL;

/** Product identifier (ASIN/SKU). */
@Nullable public final String productId;

/** Reliability grade for the product's reviews. */
@Nullable public final @Grade String grade;
@Nullable public final String grade;

/** Product rating adjusted to exclude untrusted reviews. */
@NonNull public final Double adjustedRating;
Expand Down Expand Up @@ -3511,6 +3525,67 @@ protected Highlight() {
}
}

/** Contains information about a product recommendation. */
@AnyThread
public static class Recommendation {
/** Analysis URL. */
@Nullable public final String analysisUrl;

/** Adjusted rating. */
@Nullable public final Double adjustedRating;

/** Whether or not it is a sponsored recommendation. */
@Nullable public final Boolean sponsored;

/** Url of product recommendation image. */
@Nullable public final String imageUrl;

/** Unique identifier for the ad entity. */
@Nullable public final String aid;

/** Url of recommended product. */
@Nullable public final String url;

/** Name of recommended product. */
@Nullable public final String name;

/** Grade of recommended product. */
@Nullable public final String grade;

/** Price of recommended product. */
@Nullable public final String price;

/** Currency of recommended product. */
@Nullable public final String currency;

/* package */ Recommendation(@NonNull final GeckoBundle message) {
analysisUrl = message.getString("analysis_url");
adjustedRating = message.getDouble("adjusted_rating");
sponsored = message.getBoolean("sponsored");
imageUrl = message.getString("image_url");
aid = message.getString("aid");
url = message.getString("url");
name = message.getString("name");
grade = message.getString("grade");
price = message.getString("price");
currency = message.getString("currency");
}

/** Empty constructor for tests. */
protected Recommendation() {
analysisUrl = "";
adjustedRating = 0.0;
sponsored = false;
imageUrl = "";
aid = "";
url = "";
name = "";
grade = "";
price = "";
currency = "";
}
}

public interface ContentDelegate {
/**
* A page title was discovered in the content or updated after the content loaded.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ exclude: true
- Added [`ERROR_BLOCKLISTED`][118.2] to `WebExtension.InstallException.ErrorCodes`. ([bug 1845745]({{bugzilla}}1845745))
- Added [`ContentDelegate.onProductUrl`][118.3] to notify the app when on a supported product page.
- Added [`GeckoSession.requestAnalysis`][118.4] for requesting product review analysis.
- Added [`GeckoSession.requestRecommendations`][118.5] for requesting product recommendations given a specific product url.

[118.1]: {{javadoc_uri}}/ExperimentDelegate.html
[118.2]: {{javadoc_uri}}/WebExtension.InstallException.ErrorCodes.html#ERROR_BLOCKLISTED
[118.3]:{{javadoc_uri}}/GeckoSession.ContentDelegate.html#onProductUrl(org.mozilla.geckoview.GeckoSession)
[118.4]: {{javadoc_uri}}/GeckoSession.html#requestAnalysis(String)
[118.5]: {{javadoc_uri}}/GeckoSession.html#requestRecommendations(String)

## v116
- Added [`GeckoSession.didPrintPageContent`][116.1] to included extra print status for a standard print and new `GeckoPrintException.ERROR_NO_PRINT_DELEGATE`
Expand Down Expand Up @@ -1403,4 +1405,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]: e06024c20809d282b312123df6c5f78d3a3e07c4
[api-version]: 1c6f22e97656f5dd51cd4a1c786371b3253302d2
Original file line number Diff line number Diff line change
Expand Up @@ -2127,6 +2127,11 @@ public void requestAnalysis(@NonNull final GeckoSession session, @NonNull final
session.requestAnalysis(url);
}

public void requestRecommendations(
@NonNull final GeckoSession session, @NonNull final String url) {
session.requestRecommendations(url);
}

private class ExampleNavigationDelegate implements GeckoSession.NavigationDelegate {
@Override
public void onLocationChange(
Expand All @@ -2139,6 +2144,7 @@ public void onLocationChange(
mTrackingProtectionPermission = getTrackingProtectionPermission(perms);
mCurrentUri = url;
requestAnalysis(session, url);
requestRecommendations(session, url);
}

@Override
Expand Down
22 changes: 18 additions & 4 deletions mobile/android/modules/geckoview/GeckoViewContent.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class GeckoViewContent extends GeckoViewModule {
"GeckoView:RestoreState",
"GeckoView:ContainsFormData",
"GeckoView:RequestAnalysis",
"GeckoView:RequestRecommendations",
"GeckoView:ScrollBy",
"GeckoView:ScrollTo",
"GeckoView:SetActive",
Expand Down Expand Up @@ -194,6 +195,9 @@ export class GeckoViewContent extends GeckoViewModule {
case "GeckoView:RequestAnalysis":
this._requestAnalysis(aData, aCallback);
break;
case "GeckoView:RequestRecommendations":
this._requestRecommendations(aData, aCallback);
break;
case "GeckoView:IsPdfJs":
aCallback.onSuccess(this.isPdfJs);
break;
Expand Down Expand Up @@ -320,11 +324,21 @@ export class GeckoViewContent extends GeckoViewModule {
if (!analysis) {
aCallback.onError(`Product analysis returned null.`);
}
var sanitizedGrade = analysis.grade?.toUpperCase();
if (!["A", "B", "C", "D", "E"].includes(sanitizedGrade)) {
sanitizedGrade = null;
aCallback.onSuccess({ analysis });
}
}

async _requestRecommendations(aData, aCallback) {
const url = Services.io.newURI(aData.url);
if (!lazy.isProductURL(url)) {
aCallback.onError(`Cannot requestRecommendations on a non-product url.`);
} else {
const product = new lazy.ShoppingProduct(url);
const recommendations = await product.requestRecommendations();
if (!recommendations) {
aCallback.onError(`Product recommendations returned null.`);
}
aCallback.onSuccess({ analysis: { ...analysis, grade: sanitizedGrade } });
aCallback.onSuccess({ recommendations });
}
}

Expand Down

0 comments on commit bec83a8

Please sign in to comment.