Files
tubestation/browser/components/shopping/ReviewCheckerParent.sys.mjs
Fred Chasen f43d5bc2ba Bug 1953534 - Clean up Review Checker location change handlers. r=shopping-reviewers,kpatenio,tabbrowser-reviewers
Now that the ReviewChecker sidebar can auto-open and close, the logic for finding distinct navigations needs to be updated.
This allow us to detect the first time new products URLs are requested to shown in the sidebar and to check if a tab should auto-open the sidebar after it was closed.

- `isDistinctProductPageVisit` now works the same as it did in the ShoppingSidebar, indicating that there was a product navigation while a tab was in the background.
- `reviewCheckerWasClosed` now prevents the sidebar from re-opening on tab changes for a location it was closed on.
- `isSimulated` is set to false in the location change handlers on tab changes where there was an unhandled background product navigation.
- Moves the `isSameProduct` check to `ShoppingUtils` and use that to test previous URIs passed to `hasLocationChanged`.
- Manager will only handle product page specific parts of location changes when the sidebar is not open to handle them.

Differential Revision: https://phabricator.services.mozilla.com/D241446
2025-03-28 00:07:40 +00:00

191 lines
5.8 KiB
JavaScript

/* 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/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
ShoppingUtils: "resource:///modules/ShoppingUtils.sys.mjs",
});
const ABOUT_SHOPPING_SIDEBAR = "about:shoppingsidebar";
const ABOUT_BLANK = "about:blank";
/**
* When a Review Checker sidebar panel is open ReviewCheckerParent
* handles listening for location changes and determining if the
* location or the current URI is a product or not.
*
* The ReviewCheckerChild will use that info to update the sidebar UI.
*
* This is a simplified version of the `ShoppingSidebarParent` for
* using the shopping components in the main sidebar instead of the
* custom shopping sidebar.
*/
export class ReviewCheckerParent extends JSWindowActorParent {
static SHOPPING_OPTED_IN_PREF = "browser.shopping.experience2023.optedIn";
static CLOSE_SIDEBAR = "CloseReviewCheckerSidebar";
static REVERSE_SIDEBAR = "ReverseSidebarPositionFromReviewChecker";
static SHOW_SIDEBAR_SETTINGS = "ShowSidebarSettingsFromReviewChecker";
static DISPATCH_NEW_POSITION_CARD_IF_ELIGIBLE =
"DispatchNewPositionCardIfEligible";
actorCreated() {
this.topBrowserWindow = this.browsingContext.topChromeWindow;
this.topBrowserWindow.gBrowser.addProgressListener(this);
/* The manager class that tracks auto-open behaviour is likely to be instantiated before ReviewCheckerParent.
* Once the parent actor is created, dispatch an event to the manager to see if we
* can render the notification for the RC's new sidebar position. Also be sure to set up an
* eventListener so that we can hear back from the manager. */
this.showNewPositionCard = this.showNewPositionCard.bind(this);
this.topBrowserWindow.addEventListener(
"ReviewCheckerManager:ShowNewPositionCard",
this.showNewPositionCard
);
this.dispatchNewPositionCardIfEligible();
}
didDestroy() {
if (!this.topBrowserWindow) {
return;
}
this.topBrowserWindow.removeEventListener(
"ReviewCheckerManager:ShowNewPositionCard",
this.showNewPositionCard
);
this.topBrowserWindow.gBrowser.removeProgressListener(this);
this.topBrowserWindow = undefined;
}
/**
* Returns true if a URL is ignored by ReviewChecker.
*
* @param {string} url the url that we want to validate.
*/
static isIgnoredURL(url) {
// about:shoppingsidebar is only used for testing with fake data.
return url == ABOUT_SHOPPING_SIDEBAR || url == ABOUT_BLANK;
}
updateCurrentURL(uri, flags, isSimulated) {
if (ReviewCheckerParent.isIgnoredURL(uri.spec)) {
uri = null;
}
this.sendAsyncMessage("ReviewChecker:UpdateCurrentURL", {
url: uri?.spec,
isReload: !!(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_RELOAD),
isSimulated,
});
}
getCurrentURL() {
let { selectedBrowser } = this.topBrowserWindow.gBrowser;
let uri = selectedBrowser.currentURI;
// Remove the unhandled navigation flag if present, as the
// product will be handled now.
lazy.ShoppingUtils.clearIsDistinctProductPageVisitFlag(selectedBrowser);
if (ReviewCheckerParent.isIgnoredURL(uri.spec)) {
return null;
}
return uri?.spec;
}
showNewPositionCard() {
this.sendAsyncMessage("ReviewChecker:ShowNewPositionCard");
}
async receiveMessage(message) {
if (this.browsingContext.usePrivateBrowsing) {
throw new Error("We should never be invoked in PBM.");
}
switch (message.name) {
case "GetCurrentURL":
return this.getCurrentURL();
case "DisableShopping":
Services.prefs.setIntPref(
ReviewCheckerParent.SHOPPING_OPTED_IN_PREF,
2
);
this.closeSidebarPanel();
break;
case "CloseShoppingSidebar":
this.closeSidebarPanel();
break;
case "ReverseSidebarPosition":
this.reverseSidebarPosition();
break;
case "ShowSidebarSettings":
this.showSidebarSettings();
break;
}
return null;
}
/**
* Called by TabsProgressListener whenever any browser navigates from one
* URL to another.
* Note that this includes hash changes / pushState navigations, because
* those can be significant for us.
*/
onLocationChange(
aWebProgress,
_aRequest,
aLocationURI,
aFlags,
aIsSimulated
) {
if (aWebProgress && !aWebProgress.isTopLevel) {
return;
}
let isPBM = lazy.PrivateBrowsingUtils.isWindowPrivate(
this.topBrowserWindow
);
if (isPBM) {
return;
}
// If this tab changed to a new location in the background
// it will have an `isDistinctProductPageVisit` flag.
// If that is present we need to handle the navigation as
// an un-simulated location change.
let { selectedBrowser } = this.topBrowserWindow.gBrowser;
if (selectedBrowser.isDistinctProductPageVisit) {
aIsSimulated = false;
lazy.ShoppingUtils.clearIsDistinctProductPageVisitFlag(selectedBrowser);
}
this.updateCurrentURL(aLocationURI, aFlags, aIsSimulated);
}
dispatchEvent(eventName) {
let event = new CustomEvent(eventName, {
bubbles: true,
composed: true,
});
this.topBrowserWindow.dispatchEvent(event);
}
closeSidebarPanel() {
this.dispatchEvent(ReviewCheckerParent.CLOSE_SIDEBAR);
}
dispatchNewPositionCardIfEligible() {
this.dispatchEvent(
ReviewCheckerParent.DISPATCH_NEW_POSITION_CARD_IF_ELIGIBLE
);
}
reverseSidebarPosition() {
this.dispatchEvent(ReviewCheckerParent.REVERSE_SIDEBAR);
}
showSidebarSettings() {
this.dispatchEvent(ReviewCheckerParent.SHOW_SIDEBAR_SETTINGS);
}
}