Skip to content

Commit 88c70e7

Browse files
committed
Bug 1844665 - use about: page for shopping to ensure process mechanics make sense, r=jhirsch,nika
Differential Revision: https://phabricator.services.mozilla.com/D184744
1 parent 06d44f9 commit 88c70e7

12 files changed

+128
-65
lines changed

browser/components/BrowserGlue.sys.mjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -745,10 +745,12 @@ let JSWINDOWACTORS = {
745745
esModuleURI: "resource:///actors/ShoppingSidebarChild.sys.mjs",
746746
events: {
747747
ContentReady: { wantUntrusted: true },
748-
DisableShopping: { wantUntrusted: true },
748+
// This is added so the actor instantiates immediately and makes
749+
// methods available to the page js on load.
750+
DOMDocElementInserted: {},
749751
},
750752
},
751-
matches: ["chrome://browser/content/shopping/shopping.html"],
753+
matches: ["about:shoppingsidebar"],
752754
remoteTypes: ["privilegedabout"],
753755
},
754756

browser/components/about/AboutRedirector.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ static const RedirEntry kRedirMap[] = {
9595
{"sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
9696
nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT |
9797
nsIAboutModule::IS_SECURE_CHROME_UI},
98+
#ifdef NIGHTLY_BUILD
99+
{"shoppingsidebar", "chrome://browser/content/shopping/shopping.html",
100+
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
101+
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
102+
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
103+
nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT |
104+
nsIAboutModule::IS_SECURE_CHROME_UI},
105+
#endif
98106
{"tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
99107
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
100108
nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT},

browser/components/about/components.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ pages = [
3737
'welcomeback',
3838
]
3939

40+
if buildconfig.substs.get('NIGHTLY_BUILD'):
41+
pages += ['shoppingsidebar']
42+
4043
Classes = [
4144
{
4245
'cid': '{7e4bb6ad-2fc4-4dc6-89ef-23e8e5ccf980}',

browser/components/shopping/ShoppingSidebarChild.sys.mjs

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,54 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
6+
import { RemotePageChild } from "resource://gre/actors/RemotePageChild.sys.mjs";
67

7-
export class ShoppingSidebarChild extends JSWindowActorChild {
8+
import { ShoppingProduct } from "chrome://global/content/shopping/ShoppingProduct.mjs";
9+
10+
let lazy = {};
11+
12+
let gAllActors = new Set();
13+
14+
XPCOMUtils.defineLazyPreferenceGetter(
15+
lazy,
16+
"optedIn",
17+
"browser.shopping.experience2023.optedIn",
18+
null,
19+
function optedInStateChanged() {
20+
for (let actor of gAllActors) {
21+
actor.optedInStateChanged();
22+
}
23+
}
24+
);
25+
26+
export class ShoppingSidebarChild extends RemotePageChild {
827
constructor() {
928
super();
29+
}
1030

11-
XPCOMUtils.defineLazyPreferenceGetter(
12-
this,
13-
"optedIn",
14-
"browser.shopping.experience2023.optedIn",
15-
null,
16-
() => this.updateContent()
17-
);
31+
actorCreated() {
32+
super.actorCreated();
33+
gAllActors.add(this);
1834
}
1935

36+
actorDestroyed() {
37+
super.actorDestroyed();
38+
gAllActors.delete(this);
39+
this.#product?.uninit();
40+
}
41+
42+
#productURI = null;
43+
#product = null;
44+
2045
receiveMessage(message) {
2146
switch (message.name) {
2247
case "ShoppingSidebar:UpdateProductURL":
48+
let { url } = message.data;
49+
let uri = Services.io.newURI(url);
50+
if (this.#productURI?.equalsExceptRef(uri)) {
51+
return;
52+
}
53+
this.#productURI = uri;
2354
this.updateContent();
2455
break;
2556
}
@@ -30,18 +61,56 @@ export class ShoppingSidebarChild extends JSWindowActorChild {
3061
case "ContentReady":
3162
this.updateContent();
3263
break;
33-
case "DisableShopping":
34-
this.sendAsyncMessage("DisableShopping");
35-
break;
3664
}
3765
}
3866

67+
get canFetchAndShowData() {
68+
return lazy.optedIn === 1;
69+
}
70+
71+
optedInStateChanged() {
72+
// Force re-fetching things if needed by clearing the last product URI:
73+
this.#productURI = null;
74+
// Then let content know.
75+
this.updateContent();
76+
}
77+
3978
async updateContent() {
40-
let url = await this.sendQuery("GetProductURL");
79+
this.#product?.uninit();
80+
// We are called either because the URL has changed or because the opt-in
81+
// state has changed. In both cases, we want to clear out content
82+
// immediately, without waiting for potentially async operations like
83+
// obtaining product information.
4184
this.sendToContent("Update", {
42-
optedIn: this.optedIn,
43-
url,
85+
showOnboarding: !this.canFetchAndShowData,
86+
data: null,
4487
});
88+
if (this.canFetchAndShowData) {
89+
if (!this.#productURI) {
90+
let url = await this.sendQuery("GetProductURL");
91+
92+
// Bail out if we opted out in the meantime, or don't have a URI.
93+
if (lazy.optedIn !== 1 || !url) {
94+
return;
95+
}
96+
97+
this.#productURI = Services.io.newURI(url);
98+
}
99+
100+
let uri = this.#productURI;
101+
this.#product = new ShoppingProduct(uri);
102+
let data = await this.#product.requestAnalysis().catch(err => {
103+
console.error("Failed to fetch product analysis data", err);
104+
});
105+
// Check if the product URI or opt in changed while we waited.
106+
if (uri != this.#productURI || !this.canFetchAndShowData) {
107+
return;
108+
}
109+
this.sendToContent("Update", {
110+
showOnboarding: false,
111+
data,
112+
});
113+
}
45114
}
46115

47116
sendToContent(eventName, detail) {

browser/components/shopping/ShoppingSidebarParent.sys.mjs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
export class ShoppingSidebarParent extends JSWindowActorParent {
6-
updateProductURL() {
7-
this.sendAsyncMessage("ShoppingSidebar:UpdateProductURL");
6+
updateProductURL(uri) {
7+
this.sendAsyncMessage("ShoppingSidebar:UpdateProductURL", {
8+
url: uri?.spec ?? null,
9+
});
810
}
911

1012
async receiveMessage(message) {
11-
let win = this.browsingContext.top.embedderElement.ownerGlobal;
1213
switch (message.name) {
13-
case "DisableShopping":
14-
Services.prefs.setIntPref("browser.shopping.experience2023.optedIn", 2);
15-
break;
1614
case "GetProductURL":
17-
let url = win.gBrowser.selectedBrowser.currentURI.spec;
18-
return url;
15+
let sidebarBrowser = this.browsingContext.top.embedderElement;
16+
let panel = sidebarBrowser.closest(".browserSidebarContainer");
17+
let associatedTabbedBrowser = panel.querySelector(
18+
"browser[messagemanagergroup=browsers]"
19+
);
20+
return associatedTabbedBrowser.currentURI?.spec ?? null;
1921
}
2022
return null;
2123
}

browser/components/shopping/content/settings.mjs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* License, v. 2.0. If a copy of the MPL was not distributed with this
44
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
55

6+
/* eslint-env mozilla/remote-page */
7+
68
import { html } from "chrome://global/content/vendor/lit.all.mjs";
79

810
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
@@ -26,11 +28,7 @@ class ShoppingSettings extends MozLitElement {
2628
}
2729

2830
onDisableShopping() {
29-
let event = new CustomEvent("DisableShopping", {
30-
bubbles: true,
31-
composed: true,
32-
});
33-
this.dispatchEvent(event);
31+
RPMSetPref("browser.shopping.experience2023.optedIn", 2);
3432
}
3533

3634
render() {

browser/components/shopping/content/shopping-card.mjs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
import { html, ifDefined } from "chrome://global/content/vendor/lit.all.mjs";
77
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
88

9-
window.MozXULElement.insertFTLIfNeeded("preview/shopping.ftl");
10-
119
/**
1210
* A card container to be used in the shopping sidebar. There are three card types.
1311
* The default type where no type attribute is required and the card will have no extra functionality.

browser/components/shopping/content/shopping-container.mjs

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5-
import { ShoppingProduct } from "chrome://global/content/shopping/ShoppingProduct.mjs";
5+
/* eslint-env mozilla/remote-page */
6+
67
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
78
import { html } from "chrome://global/content/vendor/lit.all.mjs";
89

@@ -20,9 +21,6 @@ import "chrome://browser/content/shopping/analysis-explainer.mjs";
2021
import "chrome://browser/content/shopping/shopping-message-bar.mjs";
2122

2223
export class ShoppingContainer extends MozLitElement {
23-
#optedIn;
24-
#product;
25-
2624
static properties = {
2725
data: { type: Object },
2826
showOnboarding: { type: Boolean },
@@ -54,33 +52,12 @@ export class ShoppingContainer extends MozLitElement {
5452
);
5553
}
5654

57-
async _update({ url, optedIn }) {
58-
this.#product?.uninit();
59-
this.#optedIn = optedIn;
60-
61-
if (this.#optedIn !== 1) {
62-
this.showOnboarding = true;
63-
// In case the user just opted out, clear out any product data too.
64-
this.data = null;
65-
return;
66-
}
67-
this.showOnboarding = false;
68-
69-
// `url` is null for non-product pages; clear out any sidebar content while
70-
// the chrome code closes the sidebar.
71-
if (!url) {
72-
this.data = null;
73-
return;
74-
}
75-
76-
let product = (this.#product = new ShoppingProduct(new URL(url)));
77-
let data = await product.requestAnalysis();
78-
// Double-check that we haven't opted out or re-entered this function
79-
// while we were `await`-ing.
80-
if (this.#optedIn !== 1 || product !== this.#product) {
81-
return;
82-
}
55+
async _update({ data, showOnboarding }) {
56+
// If we're not opted in or there's no shopping URL in the main browser,
57+
// the actor will pass `null`, which means this will clear out any existing
58+
// content in the sidebar.
8359
this.data = data;
60+
this.showOnboarding = showOnboarding;
8461
}
8562

8663
handleEvent(event) {

browser/components/shopping/content/shopping-sidebar.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
flex="1"
2222
message="true"
2323
remoteType="privilegedabout"
24+
maychangeremoteness="true"
2425
remote="true"
25-
selectmenulist="contentselectdropdown"
26-
src="chrome://browser/content/shopping/shopping.html"
26+
src="about:shoppingsidebar"
2727
type="content"
2828
/>
2929
`;

browser/components/shopping/content/shopping.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<meta charset="utf-8" />
88
<meta
99
http-equiv="Content-Security-Policy"
10-
content="default-src resource: chrome: https://staging.trustwerty.com https://staging-affiliates.fakespot.io/v1/fx/sp_search; object-src 'none'; img-src chrome:;"
10+
content="default-src resource: chrome:; object-src 'none'; img-src chrome:;"
1111
/>
1212
<meta name="color-scheme" content="light dark" />
1313
<link rel="localization" href="branding/brand.ftl" />
@@ -19,7 +19,7 @@
1919

2020
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css" />
2121

22-
<script src="chrome://global/content/customElements.js"></script>
22+
<script src="chrome://global/content/elements/message-bar.js"></script>
2323
<script
2424
type="module"
2525
src="chrome://browser/content/shopping/shopping-container.mjs"

toolkit/modules/AsyncPrefs.sys.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const kAllowedPrefs = new Set([
1515
"browser.contentblocking.report.hide_vpn_banner",
1616
"browser.contentblocking.report.show_mobile_app",
1717

18+
"browser.shopping.experience2023.optedIn",
19+
1820
"narrate.rate",
1921
"narrate.voice",
2022

toolkit/modules/RemotePageAccessManager.sys.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ export let RemotePageAccessManager = {
236236
],
237237
RPMRecordTelemetryEvent: ["*"],
238238
},
239+
"about:shoppingsidebar": {
240+
RPMSetPref: ["browser.shopping.experience2023.optedIn"],
241+
RPMGetFormatURLPref: ["app.support.baseURL"],
242+
},
239243
"about:tabcrashed": {
240244
RPMSendAsyncMessage: ["Load", "closeTab", "restoreTab", "restoreAll"],
241245
RPMAddMessageListener: ["*"],

0 commit comments

Comments
 (0)