summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/UrlbarView.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/UrlbarView.sys.mjs')
-rw-r--r--browser/components/urlbar/UrlbarView.sys.mjs115
1 files changed, 82 insertions, 33 deletions
diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs
index 440c06d4a1..b5fe1e1955 100644
--- a/browser/components/urlbar/UrlbarView.sys.mjs
+++ b/browser/components/urlbar/UrlbarView.sys.mjs
@@ -463,15 +463,18 @@ export class UrlbarView {
this.#setRowSelectable(row, false);
// Replace the row with a dismissal acknowledgment tip.
- let tip = new lazy.UrlbarResult(
- lazy.UrlbarUtils.RESULT_TYPE.TIP,
- lazy.UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
- {
- type: "dismissalAcknowledgment",
- titleL10n,
- buttons: [{ l10n: { id: "urlbar-search-tips-confirm-short" } }],
- icon: "chrome://branding/content/icon32.png",
- }
+ let tip = Object.assign(
+ new lazy.UrlbarResult(
+ lazy.UrlbarUtils.RESULT_TYPE.TIP,
+ lazy.UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ {
+ type: "dismissalAcknowledgment",
+ titleL10n,
+ buttons: [{ l10n: { id: "urlbar-search-tips-confirm-short" } }],
+ icon: "chrome://branding/content/icon32.png",
+ }
+ ),
+ { rowLabel: this.#rowLabel(row) }
);
this.#updateRow(row, tip);
this.#updateIndices();
@@ -693,7 +696,7 @@ export class UrlbarView {
this.#cacheL10nStrings();
}
- onQueryCancelled(queryContext) {
+ onQueryCancelled() {
this.#queryWasCancelled = true;
this.#cancelRemoveStaleRowsTimer();
}
@@ -1130,6 +1133,8 @@ export class UrlbarView {
*/
#rowCanUpdateToResult(rowIndex, result, seenSearchSuggestion) {
// The heuristic result must always be current, thus it's always compatible.
+ // Note that the `updateResults` code, when updating the selection, relies
+ // on the fact the heuristic is the first selectable row.
if (result.heuristic) {
return true;
}
@@ -1176,6 +1181,10 @@ export class UrlbarView {
// future we should make it support any type of result. Or, even better,
// results should be grouped, thus we can directly update groups.
+ // Discard tentative exposures. This is analogous to marking the
+ // hypothetical hidden rows of hidden-exposure results as stale.
+ this.controller.engagementEvent.discardTentativeExposures();
+
// Walk rows and find an insertion index for results. To avoid flicker, we
// skip rows until we find one compatible with the result we want to apply.
// If we couldn't find a compatible range, we'll just update.
@@ -1212,7 +1221,7 @@ export class UrlbarView {
) {
// We can replace the row's current result with the new one.
if (result.exposureResultHidden) {
- this.#addExposure(result);
+ this.controller.engagementEvent.addExposure(result);
} else {
this.#updateRow(row, result);
}
@@ -1282,7 +1291,13 @@ export class UrlbarView {
newSpanCount <= this.#queryContext.maxResults && !seenMisplacedResult;
if (result.exposureResultHidden) {
if (canBeVisible) {
- this.#addExposure(result);
+ this.controller.engagementEvent.addExposure(result);
+ } else {
+ // Add a tentative exposure: The hypothetical row for this
+ // hidden-exposure result can't be visible now, but as long as it were
+ // not marked stale in a later update, it would be shown when stale
+ // rows are removed.
+ this.controller.engagementEvent.addTentativeExposure(result);
}
continue;
}
@@ -1323,10 +1338,17 @@ export class UrlbarView {
// on the row itself so that screen readers ignore it.
item.setAttribute("role", "presentation");
+ // These are used to cleanup result specific entities when row contents are
+ // cleared to reuse the row for a different result.
+ item._sharedAttributes = new Set(
+ [...item.attributes].map(v => v.name).concat(["stale", "id"])
+ );
+ item._sharedClassList = new Set(item.classList);
+
return item;
}
- #createRowContent(item, result) {
+ #createRowContent(item) {
// The url is the only element that can wrap, thus all the other elements
// are child of noWrap.
let noWrap = this.#createElement("span");
@@ -1505,7 +1527,7 @@ export class UrlbarView {
return classes;
}
- #createRowContentForRichSuggestion(item, result) {
+ #createRowContentForRichSuggestion(item) {
item._content.toggleAttribute("selectable", true);
let favicon = this.#createElement("img");
@@ -1615,7 +1637,7 @@ export class UrlbarView {
// eslint-disable-next-line complexity
#updateRow(item, result) {
let oldResult = item.result;
- let oldResultType = item.result && item.result.type;
+ let oldResultType = item.result?.type;
let provider = lazy.UrlbarProvidersManager.getProvider(result.providerName);
item.result = result;
item.removeAttribute("stale");
@@ -1638,6 +1660,18 @@ export class UrlbarView {
oldResult.payload.buttons,
result.payload.buttons
) ||
+ // Reusing a non-heuristic as a heuristic is risky as it may have DOM
+ // nodes/attributes/classes that are normally not present in a heuristic
+ // result. This may happen for example when switching from a zero-prefix
+ // search not having a heuristic to a search string one.
+ result.heuristic != oldResult.heuristic ||
+ // Container switch-tab results have a more complex DOM content that is
+ // only updated correctly by another switch-tab result.
+ (oldResultType == lazy.UrlbarUtils.RESULT_TYPE.TAB_SWITCH &&
+ lazy.UrlbarProviderOpenTabs.isContainerUserContextId(
+ oldResult.payload.userContextId
+ ) &&
+ result.type != oldResultType) ||
result.testForceNewContent;
if (needsNewContent) {
@@ -1649,8 +1683,18 @@ export class UrlbarView {
item._content = this.#createElement("span");
item._content.className = "urlbarView-row-inner";
item.appendChild(item._content);
- item.removeAttribute("tip-type");
- item.removeAttribute("dynamicType");
+ // Clear previously set attributes and classes that may refer to a
+ // different result type.
+ for (const attribute of item.attributes) {
+ if (!item._sharedAttributes.has(attribute.name)) {
+ item.removeAttribute(attribute.name);
+ }
+ }
+ for (const className of item.classList) {
+ if (!item._sharedClassList.has(className)) {
+ item.classList.remove(className);
+ }
+ }
if (item.result.type == lazy.UrlbarUtils.RESULT_TYPE.DYNAMIC) {
this.#createRowContentForDynamicType(item, result);
} else if (result.isRichSuggestion) {
@@ -1875,7 +1919,6 @@ export class UrlbarView {
item.toggleAttribute("has-url", setURL);
let url = item._elements.get("url");
if (setURL) {
- item.setAttribute("has-url", "true");
let displayedUrl = result.payload.displayUrl;
let urlHighlights = result.payloadHighlights.displayUrl || [];
if (lazy.UrlbarUtils.isTextDirectionRTL(displayedUrl, this.window)) {
@@ -2093,7 +2136,7 @@ export class UrlbarView {
let visible = this.#isElementVisible(item);
if (visible) {
if (item.result.exposureResultType) {
- this.#addExposure(item.result);
+ this.controller.engagementEvent.addExposure(item.result);
}
this.visibleResults.push(item.result);
}
@@ -2200,6 +2243,10 @@ export class UrlbarView {
return null;
}
+ if (row.result.rowLabel) {
+ return row.result.rowLabel;
+ }
+
let engineName =
row.result.payload.engine || Services.search.defaultEngine.name;
@@ -2326,6 +2373,10 @@ export class UrlbarView {
row = next;
}
this.#updateIndices();
+
+ // Accept tentative exposures. This is analogous to unhiding the
+ // hypothetical non-stale hidden rows of hidden-exposure results.
+ this.controller.engagementEvent.acceptTentativeExposures();
}
#startRemoveStaleRowsTimer() {
@@ -2645,7 +2696,7 @@ export class UrlbarView {
if (
actionNode.classList.contains("urlbarView-userContext") &&
label &&
- actionNode.querySelector("span").innerText == label
+ actionNode.querySelector("span")?.innerText == label
) {
return;
}
@@ -2681,8 +2732,6 @@ export class UrlbarView {
if (identity.icon) {
let userContextIcon = this.#createElement("img");
userContextIcon.classList.add("urlbarView-userContext-icon");
-
- userContextIcon.classList.add("identity-icon-" + identity.icon);
userContextIcon.setAttribute("alt", label);
userContextIcon.src =
"resource://usercontext-content/" + identity.icon + ".svg";
@@ -3131,6 +3180,7 @@ export class UrlbarView {
let engine = this.oneOffSearchButtons.selectedButton?.engine;
let source = this.oneOffSearchButtons.selectedButton?.source;
+ let icon = this.oneOffSearchButtons.selectedButton?.image;
let localSearchMode;
if (source) {
@@ -3254,7 +3304,15 @@ export class UrlbarView {
}
// Update result favicons.
- let iconOverride = localSearchMode?.icon || engine?.getIconURL();
+ let iconOverride = localSearchMode?.icon;
+ // If the icon is the default one-off search placeholder, assume we
+ // don't have an icon for the engine.
+ if (
+ !iconOverride &&
+ icon != "chrome://browser/skin/search-engine-placeholder.png"
+ ) {
+ iconOverride = icon;
+ }
if (!iconOverride && (localSearchMode || engine)) {
// For one-offs without an icon, do not allow restyled URL results to
// use their own icons.
@@ -3273,7 +3331,7 @@ export class UrlbarView {
}
}
- on_blur(event) {
+ on_blur() {
// If the view is open without the input being focused, it will not close
// automatically when the window loses focus. We might be in this state
// after a Search Tip is shown on an engine homepage.
@@ -3416,15 +3474,6 @@ export class UrlbarView {
this.#populateResultMenu();
}
}
-
- /**
- * Add result to exposure set on the controller.
- *
- * @param {UrlbarResult} result UrlbarResult for which to record an exposure.
- */
- #addExposure(result) {
- this.controller.engagementEvent.addExposure(result);
- }
}
/**