UrlbarPrefs.sys.mjs (48979B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * This module exports the UrlbarPrefs singleton, which manages preferences for 7 * the urlbar. It also provides access to urlbar Nimbus variables as if they are 8 * preferences, but only for variables with fallback prefs. 9 */ 10 11 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; 12 13 const lazy = XPCOMUtils.declareLazy({ 14 NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs", 15 UrlbarUtils: "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs", 16 CustomizableUI: 17 "moz-src:///browser/components/customizableui/CustomizableUI.sys.mjs", 18 }); 19 20 const PREF_URLBAR_BRANCH = "browser.urlbar."; 21 22 /** 23 * @typedef {boolean|number|string|[number, string]} PreferenceDefaultAndType 24 * Prefs are defined as [pref name, default value] or [pref name, [default 25 * value, type]]. In the former case, the getter method name is inferred from 26 * the typeof the default value. 27 * 28 * @typedef {[string, PreferenceDefaultAndType]} PreferenceDefinition 29 */ 30 31 // NOTE: Don't name prefs (relative to the `browser.urlbar` branch) the same as 32 // Nimbus urlbar features. Doing so would cause a name collision because pref 33 // names and Nimbus feature names are both kept as keys in UrlbarPref's map. For 34 // a list of Nimbus features, see toolkit/components/nimbus/FeatureManifest.yaml. 35 const PREF_URLBAR_DEFAULTS = /** @type {PreferenceDefinition[]} */ ([ 36 // Whether we announce to screen readers when tab-to-search results are 37 // inserted. 38 ["accessibility.tabToSearch.announceResults", true], 39 40 // Feature gate pref for addon suggestions in the urlbar. 41 ["addons.featureGate", false], 42 43 // The number of times the user has clicked the "Show less frequently" command 44 // for addon suggestions. 45 ["addons.showLessFrequentlyCount", 0], 46 47 // Feature gate pref for AMP suggestions in the urlbar. 48 ["amp.featureGate", false], 49 50 // "Autofill" is the name of the feature that automatically completes domains 51 // and URLs that the user has visited as the user is typing them in the urlbar 52 // textbox. If false, autofill will be disabled. 53 ["autoFill", true], 54 55 // Whether enabling adaptive history autofill. This pref is a fallback for the 56 // Nimbus variable `autoFillAdaptiveHistoryEnabled`. 57 ["autoFill.adaptiveHistory.enabled", false], 58 59 // Minimum char length of the user's search string to enable adaptive history 60 // autofill. This pref is a fallback for the Nimbus variable 61 // `autoFillAdaptiveHistoryMinCharsThreshold`. 62 ["autoFill.adaptiveHistory.minCharsThreshold", 0], 63 64 // Threshold for use count of input history that we handle as adaptive history 65 // autofill. If the use count is this value or more, it will be a candidate. 66 // Set the threshold to not be candidate the input history passed approximately 67 // 30 days since user input it as the default. 68 ["autoFill.adaptiveHistory.useCountThreshold", [0.47, "float"]], 69 70 // Affects the frecency threshold of the autofill algorithm. The threshold is 71 // the mean of all origin frecencies plus one standard deviation multiplied by 72 // this value. See UrlbarProviderPlaces. 73 ["autoFill.stddevMultiplier", [0.0, "float"]], 74 75 // Feature gate pref for clipboard suggestions in the urlbar. 76 ["clipboard.featureGate", false], 77 78 // Whether to close other panels when the urlbar panel opens. 79 // This feature gate exists just as an emergency rollback in case of 80 // unexpected issues in Release. We normally want this behavior. 81 ["closeOtherPanelsOnOpen", true], 82 83 // Whether to show a link for using the search functionality provided by the 84 // active view if the the view utilizes OpenSearch. 85 ["contextualSearch.enabled", true], 86 87 // Whether using `ctrl` when hitting return/enter in the URL bar 88 // (or clicking 'go') should prefix 'www.' and suffix 89 // browser.fixup.alternate.suffix to the URL bar value prior to 90 // navigating. 91 ["ctrlCanonizesURLs", true], 92 93 // Whether copying the entire URL from the location bar will put a human 94 // readable (percent-decoded) URL on the clipboard. 95 ["decodeURLsOnCopy", false], 96 97 // The amount of time (ms) to wait after the user has stopped typing before 98 // fetching results. However, we ignore this for the very first result (the 99 // "heuristic" result). We fetch it as fast as possible. 100 ["delay", 50], 101 102 // Ensure we use trailing dots for DNS lookups for single words that could 103 // be hosts. 104 ["dnsResolveFullyQualifiedNames", true], 105 106 // Controls when to DNS resolve single word search strings, after they were 107 // searched for. If the string is resolved as a valid host, show a 108 // "Did you mean to go to 'host'" prompt. 109 // 0 - never resolve; 1 - use heuristics (default); 2 - always resolve 110 ["dnsResolveSingleWordsAfterSearch", 0], 111 112 // If Suggest is disabled before these seconds from a search, then send a 113 // disable event. 114 ["events.disableSuggest.maxSecondsFromLastSearch", 300], 115 116 // If a page is interacted with for less than these seconds, before navigating 117 // away via browser chrome, then send a bounce event. 118 ["events.bounce.maxSecondsFromLastSearch", 10], 119 120 // Whether the heuristic result is hidden. 121 ["experimental.hideHeuristic", false], 122 123 // Comma-separated list of `source.providers` combinations, that are used to 124 // determine if an exposure event should be fired. This can be set by a 125 // Nimbus variable and is expected to be set via nimbus experiment 126 // configuration. 127 ["exposureResults", ""], 128 129 // When we send events to (privileged) extensions (urlbar API), we wait this 130 // amount of time in milliseconds for them to respond before timing out. 131 ["extension.timeout", 400], 132 133 // When we send events to extensions that use the omnibox API, we wait this 134 // amount of time in milliseconds for them to respond before timing out. 135 ["extension.omnibox.timeout", 3000], 136 137 // When true, `javascript:` URLs are not included in search results. 138 ["filter.javascript", true], 139 140 // Feature gate pref for flight status suggestions in the urlbar. 141 ["flightStatus.featureGate", false], 142 143 // The minimum prefix length of a flight status keyword the user must type to 144 // trigger the suggestion. 0 means the min length should be taken from Nimbus 145 // or remote settings. 146 ["flightStatus.minKeywordLength", 0], 147 148 // The number of times the user has clicked the "Show less frequently" command 149 // for flight status suggestions. 150 ["flightStatus.showLessFrequentlyCount", 0], 151 152 // Focus the content document when pressing the Escape key, if there's no 153 // remaining typed history. 154 ["focusContentDocumentOnEsc", true], 155 156 // Applies URL highlighting and other styling to the text in the urlbar input. 157 ["formatting.enabled", true], 158 159 // Whether Firefox Suggest group labels are shown in the urlbar view in en-* 160 // locales. Labels are not shown in other locales but likely will be in the 161 // future. 162 ["groupLabels.enabled", true], 163 164 // Feature gate pref for important-dates suggestions in the urlbar. 165 ["importantDates.featureGate", false], 166 167 // Set default intent threshold value of 0.5 168 ["intentThreshold", [0.5, "float"]], 169 170 // Whether the results panel should be kept open during IME composition. 171 ["keepPanelOpenDuringImeComposition", false], 172 173 // Comma-separated list of result types that should trigger keyword-exposure 174 // telemetry. Only applies to results with an `exposureTelemetry` value other 175 // than `NONE`. 176 ["keywordExposureResults", ""], 177 178 // Feature gate pref for stock market suggestions in the urlbar. 179 ["market.featureGate", false], 180 181 // The minimum prefix length of a market keyword the user must type to 182 // trigger the suggestion. 0 means the min length should be taken from Nimbus 183 // or remote settings. 184 ["market.minKeywordLength", 0], 185 186 // The number of times the user has clicked the "Show less frequently" command 187 // for Market suggestions. 188 ["market.showLessFrequentlyCount", 0], 189 190 // As a user privacy measure, don't fetch results from remote services for 191 // searches that start by pasting a string longer than this. The pref name 192 // indicates search suggestions, but this is used for all remote results. 193 ["maxCharsForSearchSuggestions", 100], 194 195 // The maximum number of form history results to include. 196 ["maxHistoricalSearchSuggestions", 0], 197 198 // The maximum number of results in the urlbar popup. 199 ["maxRichResults", 10], 200 201 // Feature gate pref for mdn suggestions in the urlbar. 202 ["mdn.featureGate", true], 203 204 // Comma-separated list of client variants to send to Merino 205 ["merino.clientVariants", ""], 206 207 // The Merino endpoint URL, not including parameters. 208 ["merino.endpointURL", "https://merino.services.mozilla.com/api/v1/suggest"], 209 210 // OHTTP config URL for Merino requests. 211 ["merino.ohttpConfigURL", ""], 212 213 // OHTTP relay URL for Merino requests. 214 ["merino.ohttpRelayURL", ""], 215 216 // Comma-separated list of providers to request from Merino 217 ["merino.providers", ""], 218 219 // Timeout for Merino fetches (ms). 220 ["merino.timeoutMs", 200], 221 222 // Set default NER threshold value of 0.5 223 ["nerThreshold", [0.5, "float"]], 224 225 // Whether addresses and search results typed into the address bar 226 // should be opened in new tabs by default. 227 ["openintab", false], 228 229 // Once Perplexity has entered search mode at least once, 230 // we no longer show the Perplexity onboarding callout. 231 // This pref will be set to true when perplexity search mode is detected. 232 ["perplexity.hasBeenInSearchMode", false], 233 234 // If disabled, QuickActions will not be included in either the default search 235 // mode or the QuickActions search mode. 236 ["quickactions.enabled", true], 237 238 // The number of times we should show the actions onboarding label. 239 ["quickactions.timesToShowOnboardingLabel", 3], 240 241 // The number of times we have shown the actions onboarding label. 242 ["quickactions.timesShownOnboardingLabel", 0], 243 244 // Whether we will match QuickActions within a phrase and not only a prefix. 245 ["quickactions.matchInPhrase", true], 246 247 // The minumum amount of characters required for the user to input before 248 // matching actions. Setting this to 0 will show the actions in the 249 // zero prefix state. 250 ["quickactions.minimumSearchString", 3], 251 252 // Whether we show the Actions section in about:preferences. 253 ["quickactions.showPrefs", false], 254 255 // When non-zero, this is the character-count threshold (inclusive) for 256 // showing AMP suggestions as top picks. If an AMP suggestion is triggered by 257 // a keyword at least this many characters long, it will be shown as a top 258 // pick. 259 ["quicksuggest.ampTopPickCharThreshold", 5], 260 261 // Whether the Firefox Suggest data collection opt-in result is enabled. 262 ["quicksuggest.contextualOptIn", false], 263 264 // The last time (as seconds) the user dismissed the Firefox Suggest contextual 265 // opt-in result. 266 ["quicksuggest.contextualOptIn.lastDismissedTime", 0], 267 268 // Number that the user dismissed the Firefox Suggest contextual opt-in result. 269 ["quicksuggest.contextualOptIn.dismissedCount", 0], 270 271 // Period until reshow the Firefox Suggest contextual opt-in result when first dismissed. 272 ["quicksuggest.contextualOptIn.firstReshowAfterPeriodDays", 7], 273 274 // Period until reshow the Firefox Suggest contextual opt-in result when second dismissed. 275 ["quicksuggest.contextualOptIn.secondReshowAfterPeriodDays", 14], 276 277 // Period until reshow the Firefox Suggest contextual opt-in result when third dismissed. 278 ["quicksuggest.contextualOptIn.thirdReshowAfterPeriodDays", 60], 279 280 // Number of impression for the Firefox Suggest contextual opt-in result. 281 ["quicksuggest.contextualOptIn.impressionCount", 0], 282 283 // Limit for impression to dismiss the Firefox Suggest contextual opt-in 284 // result. 285 ["quicksuggest.contextualOptIn.impressionLimit", 20], 286 287 // The first impression time (seconds) for the Firefox Suggest contextual 288 // opt-in result. 289 ["quicksuggest.contextualOptIn.firstImpressionTime", 0], 290 291 // Days until dismiss the Firefox Suggest contextual opt-in result after first 292 // impression. 293 ["quicksuggest.contextualOptIn.impressionDaysLimit", 5], 294 295 // Comma-separated list of Suggest dynamic suggestion types to enable. 296 ["quicksuggest.dynamicSuggestionTypes", ""], 297 298 // Global toggle for whether the quick suggest feature is enabled, i.e., 299 // sponsored and recommended results related to the user's search string. 300 ["quicksuggest.enabled", false], 301 302 // Whether non-sponsored quick suggest results are subject to impression 303 // frequency caps. This pref is a fallback for the Nimbus variable 304 // `quickSuggestImpressionCapsNonSponsoredEnabled`. 305 ["quicksuggest.impressionCaps.nonSponsoredEnabled", false], 306 307 // Whether sponsored quick suggest results are subject to impression frequency 308 // caps. This pref is a fallback for the Nimbus variable 309 // `quickSuggestImpressionCapsSponsoredEnabled`. 310 ["quicksuggest.impressionCaps.sponsoredEnabled", false], 311 312 // JSON'ed object of quick suggest impression stats. Used for implementing 313 // impression frequency caps for quick suggest suggestions. 314 ["quicksuggest.impressionCaps.stats", ""], 315 316 // If the user has gone through a quick suggest prefs migration, then this 317 // pref will have a user-branch value that records the latest prefs version. 318 // See `QuickSuggest` for details on each version. 319 ["quicksuggest.migrationVersion", 0], 320 321 // Whether Suggest will use the ML backend in addition to Rust. 322 ["quicksuggest.mlEnabled", false], 323 324 // NOTE: You should most likely access this pref via its Nimbus variable 325 // instead: `UrlbarPrefs.get("quickSuggestOnlineAvailable"). It's listed here 326 // mainly so tests can access it easily via `UrlbarPrefs`. 327 // 328 // Whether online Suggest is available to the user. This is only relevant when 329 // Suggest overall is enabled. 330 ["quicksuggest.online.available", false], 331 332 // Whether online Suggest is enabled for the user. This is only relevant when 333 // Suggest overall is enabled and online Suggest is available to the user. 334 ["quicksuggest.online.enabled", true], 335 336 // The last time (as seconds) the user selected 'Not Now' on Realtime 337 // suggestion opt-in result. 338 ["quicksuggest.realtimeOptIn.notNowTimeSeconds", 0], 339 340 // Period until reshow Realtime suggestion opt-in after selecting "Not Now". 341 ["quicksuggest.realtimeOptIn.notNowReshowAfterPeriodDays", 7], 342 343 // The type of Realtime suggestion that the user selected 'Not Now' as CSV. 344 ["quicksuggest.realtimeOptIn.notNowTypes", ""], 345 346 // The type of Realtime suggestion that the user selected 'Dismiss' as CSV. 347 ["quicksuggest.realtimeOptIn.dismissTypes", ""], 348 349 // Whether Firefox Suggest will use the new Rust backend instead of the 350 // original JS backend. 351 ["quicksuggest.rustEnabled", true], 352 353 // The Suggest Rust backend will ingest remote settings every N seconds as 354 // defined by this pref. Ingestion uses nsIUpdateTimerManager so the interval 355 // will persist across app restarts. The default value is 24 hours, same as 356 // the interval used by the desktop remote settings client. 357 ["quicksuggest.rustIngestIntervalSeconds", 60 * 60 * 24], 358 359 // Which Suggest settings to show in the settings UI. See 360 // `QuickSuggest.SETTINGS_UI` for values. 361 ["quicksuggest.settingsUi", 0], 362 363 // We only show recent searches within the past 3 days by default. 364 // Stored as a string as some code handle timestamp sized int's. 365 ["recentsearches.expirationMs", (1000 * 60 * 60 * 24 * 3).toString()], 366 367 // Feature gate pref for recent searches being shown in the urlbar. 368 ["recentsearches.featureGate", true], 369 370 // Store the time the last default engine changed so we can only show 371 // recent searches since then. 372 // Stored as a string as some code handle timestamp sized int's. 373 ["recentsearches.lastDefaultChanged", "-1"], 374 375 // The maximum number of recent searches we will show. 376 ["recentsearches.maxResults", 5], 377 378 // When true, URLs in the user's history that look like search result pages 379 // are styled to look like search engine results instead of the usual history 380 // results. 381 ["restyleSearches", false], 382 383 // Allow the result menu button to be reached with the Tab key. 384 ["resultMenu.keyboardAccessible", true], 385 386 // Feature gate pref for rich suggestions being shown in the urlbar. 387 ["richSuggestions.featureGate", true], 388 389 // If true, we show tail suggestions when available. 390 ["richSuggestions.tail", true], 391 392 // Disable the urlbar OneOff panel from being shown. 393 ["scotchBonnet.disableOneOffs", false], 394 395 // A short-circuit pref to enable all the features that are part of a 396 // grouped release. 397 ["scotchBonnet.enableOverride", true], 398 399 // Allow searchmode to be persisted as the user navigates the 400 // search host. 401 ["scotchBonnet.persistSearchMode", false], 402 403 // Feature gate pref for search restrict keywords being shown in the urlbar. 404 ["searchRestrictKeywords.featureGate", false], 405 406 // Hidden pref. Disables checks that prevent search tips being shown, thus 407 // showing them every time the newtab page or the default search engine 408 // homepage is opened. 409 ["searchTips.test.ignoreShowLimits", false], 410 411 // Feature gate pref for secondary actions being shown in the urlbar. 412 ["secondaryActions.featureGate", false], 413 414 // Maximum number of actions shown. 415 ["secondaryActions.maxActionsShown", 3], 416 417 // Alternative switch to tab implementation using secondaryActions. 418 ["secondaryActions.switchToTab", false], 419 420 // Whether to show each local search shortcut button in the view. 421 ["shortcuts.bookmarks", true], 422 ["shortcuts.tabs", true], 423 ["shortcuts.history", true], 424 ["shortcuts.actions", true], 425 426 // Boolean to determine if the providers defined in `exposureResults` 427 // should be displayed in search results. This can be set by a 428 // Nimbus variable and is expected to be set via nimbus experiment 429 // configuration. For the control branch of an experiment this would be 430 // false and true for the treatment. 431 ["showExposureResults", false], 432 433 // Whether to show search suggestions before general results. 434 ["showSearchSuggestionsFirst", true], 435 436 // If true, show the search term in the Urlbar while on 437 // a default search engine results page. 438 ["showSearchTerms.enabled", true], 439 440 // Global toggle for whether the show search terms feature 441 // can be used at all, and enabled/disabled by the user. 442 ["showSearchTerms.featureGate", false], 443 444 // Whether speculative connections should be enabled. 445 ["speculativeConnect.enabled", true], 446 447 // If true, top sites may include sponsored ones. 448 ["sponsoredTopSites", false], 449 450 // Feature gate pref for realtime sports suggestions in the urlbar. 451 ["sports.featureGate", false], 452 453 // The minimum prefix length of sports keyword the user must type to trigger 454 // the suggestion. 0 means the min length should be taken from Nimbus or 455 // remote settings. 456 ["sports.minKeywordLength", 0], 457 458 // The number of times the user has clicked the "Show less frequently" command 459 // for sports suggestions. 460 ["sports.showLessFrequentlyCount", 0], 461 462 // If `browser.urlbar.addons.featureGate` is true, this controls whether 463 // addon suggestions are turned on. 464 ["suggest.addons", true], 465 466 // If `browser.urlbar.amp.featureGate` is true, this controls whether AMP 467 // suggestions are turned on. 468 ["suggest.amp", true], 469 470 // Whether results will include the user's bookmarks. 471 ["suggest.bookmark", true], 472 473 // Whether results will include a calculator. 474 ["suggest.calculator", false], 475 476 // Whether results will include clipboard results. 477 ["suggest.clipboard", true], 478 479 // Whether results will include search engines (e.g. tab-to-search). 480 ["suggest.engines", true], 481 482 // If `browser.urlbar.flightStatus.featureGate` is true, this controls whether 483 // flight status suggestions are turned on. 484 ["suggest.flightStatus", true], 485 486 // Whether results will include the user's history. 487 ["suggest.history", true], 488 489 // If `browser.urlbar.importantDates.featureGate` is true, this controls 490 // whether important-dates suggestions are turned on. 491 ["suggest.importantDates", true], 492 493 // Whether results will include Market suggestions. 494 ["suggest.market", true], 495 496 // If `browser.urlbar.mdn.featureGate` is true, this controls whether 497 // mdn suggestions are turned on. 498 ["suggest.mdn", true], 499 500 // Whether results will include switch-to-tab results. 501 ["suggest.openpage", true], 502 503 // Whether results will include QuickActions in the default search mode. 504 ["suggest.quickactions", false], 505 506 // Whether results will include Suggest suggestions. 507 ["suggest.quicksuggest.all", false], 508 509 // Whether results will include sponsored Suggest suggestions. Only relevant 510 // if the `all` pref is true. 511 ["suggest.quicksuggest.sponsored", false], 512 513 // Whether results will include Realtime suggestion opt-in result. 514 ["suggest.realtimeOptIn", true], 515 516 // If `browser.urlbar.recentsearches.featureGate` is true, this controls whether 517 // recentsearches are turned on. 518 ["suggest.recentsearches", true], 519 520 // Whether results will include synced tab results. The syncing of open tabs 521 // must also be enabled, from Sync preferences. 522 ["suggest.remotetab", true], 523 524 // Whether results will include search suggestions. 525 ["suggest.searches", false], 526 527 // Whether results will include realtime sports suggestions. 528 ["suggest.sports", true], 529 530 // Whether results will include top sites and the view will open on focus. 531 ["suggest.topsites", true], 532 533 // If `browser.urlbar.trending.featureGate` is true, this controls whether 534 // trending suggestions are turned on. 535 ["suggest.trending", true], 536 537 // If `browser.urlbar.weather.featureGate` is true, this controls whether 538 // weather suggestions are turned on. 539 ["suggest.weather", true], 540 541 // If `browser.urlbar.wikipedia.featureGate` is true, this controls whether 542 // Wikipedia suggestions are turned on. 543 ["suggest.wikipedia", true], 544 545 // If `browser.urlbar.yelp.featureGate` is true, this controls whether 546 // Yelp suggestions are turned on. 547 ["suggest.yelp", true], 548 549 // If `browser.urlbar.yelpRealtime.featureGate` is true, this controls whether 550 // Yelp realtime suggestions are turned on. 551 ["suggest.yelpRealtime", true], 552 553 // Whether history results with the same title and URL excluding the ref 554 // will be deduplicated. 555 ["deduplication.enabled", true], 556 557 // How old history results have to be to be deduplicated. 558 ["deduplication.thresholdDays", 0], 559 560 // semanticHistory search query minLength threshold to be enabled. 561 ["suggest.semanticHistory.minLength", 5], 562 563 // When using switch to tabs, if set to true this will move the tab into the 564 // active window. 565 ["switchTabs.adoptIntoActiveWindow", false], 566 567 // Controls whether searching for open tabs returns tabs from any container 568 // or only from the current container. 569 ["switchTabs.searchAllContainers", true], 570 571 // The minimum number of characters needed to match a tab group name. 572 ["tabGroups.minSearchLength", 1], 573 574 // The number of remaining times the user can interact with tab-to-search 575 // onboarding results before we stop showing them. 576 ["tabToSearch.onboard.interactionsLeft", 3], 577 578 // The number of times the user has been shown the onboarding search tip. 579 ["tipShownCount.searchTip_onboard", 0], 580 581 // The number of times the user has been shown the redirect search tip. 582 ["tipShownCount.searchTip_redirect", 0], 583 584 // Feature gate pref for trending suggestions in the urlbar. 585 ["trending.featureGate", true], 586 587 // The maximum number of trending results to show while not in search mode. 588 ["trending.maxResultsNoSearchMode", 10], 589 590 // The maximum number of trending results to show in search mode. 591 ["trending.maxResultsSearchMode", 10], 592 593 // Whether to only show trending results when the urlbar is in search 594 // mode or when the user initially opens the urlbar without selecting 595 // an engine. 596 ["trending.requireSearchMode", false], 597 598 // Remove 'https://' from url when urlbar is focused. 599 ["trimHttps", false], 600 601 // Remove redundant portions from URLs. 602 ["trimURLs", true], 603 604 // Enable the updated design combining the privacy and shield icon 605 // and panels in the Urlbar. 606 ["trustPanel.featureGate", false], 607 608 // Whether unit conversion is enabled. 609 ["unitConversion.enabled", false], 610 611 // The index where we show unit conversion results. 612 ["unitConversion.suggestedIndex", 1], 613 614 // Untrim url, when urlbar is focused. 615 // Note: This pref will be removed once the feature is stable. 616 ["untrimOnUserInteraction.featureGate", false], 617 618 // Whether or not Unified Search Button is shown always. 619 ["unifiedSearchButton.always", false], 620 621 // Feature gate pref for weather suggestions in the urlbar. 622 ["weather.featureGate", true], 623 624 // The minimum prefix length of a weather keyword the user must type to 625 // trigger the suggestion. 0 means the min length should be taken from Nimbus 626 // or remote settings. 627 ["weather.minKeywordLength", 0], 628 629 // The number of times the user has clicked the "Show less frequently" command 630 // for weather suggestions. 631 ["weather.showLessFrequentlyCount", 0], 632 633 // Feature gate pref for Wikipedia suggestions (part of Suggest). 634 ["wikipedia.featureGate", false], 635 636 // Feature gate pref for Yelp suggestions in the urlbar. 637 ["yelp.featureGate", false], 638 639 // The minimum prefix length of a Yelp keyword the user must type to trigger 640 // the suggestion. 0 means the min length should be taken from Nimbus. 641 ["yelp.minKeywordLength", 4], 642 643 // Whether Yelp suggestions will be served from the Suggest ML backend instead 644 // of Rust. 645 ["yelp.mlEnabled", false], 646 647 // Whether Yelp suggestions should be shown as top picks. This is a fallback 648 // pref for the `yelpSuggestPriority` Nimbus variable. 649 ["yelp.priority", false], 650 651 // Whether to distinguish service type subjects. If true, we show special 652 // titile for the suggestion. This is a fallback pref for the 653 // `yelpServiceResultDistinction` Nimbus variable. 654 ["yelp.serviceResultDistinction", false], 655 656 // The number of times the user has clicked the "Show less frequently" command 657 // for Yelp suggestions. 658 ["yelp.showLessFrequentlyCount", 0], 659 660 // Feature gate pref for Yelp realtime suggestions in the urlbar. 661 ["yelpRealtime.featureGate", false], 662 663 // The minimum prefix length of a Yelp keyword the user must type to trigger 664 // the suggestion. 0 means the min length should be taken from Nimbus or remote 665 // settings. 666 ["yelpRealtime.minKeywordLength", 0], 667 668 // The number of times the user has clicked the "Show less frequently" command 669 // for Yelp realtime suggestions. 670 ["yelpRealtime.showLessFrequentlyCount", 0], 671 ]); 672 673 // This is defined separately to PREF_URLBAR_DEFAULTS, to avoid re-indenting 674 // the array so that we can preserve blame. 675 const PREF_URLBAR_DEFAULTS_MAP = new Map(PREF_URLBAR_DEFAULTS); 676 677 const PREF_OTHER_DEFAULTS = new Map([ 678 ["browser.fixup.dns_first_for_single_words", false], 679 ["browser.ml.enable", false], 680 ["browser.search.openintab", false], 681 ["browser.search.suggest.enabled", true], 682 ["browser.search.suggest.enabled.private", false], 683 ["browser.search.widget.new", false], 684 ["keyword.enabled", true], 685 ["security.insecure_connection_text.enabled", true], 686 ["ui.popup.disable_autohide", false], 687 ]); 688 689 // Default values for Nimbus urlbar variables that do not have fallback prefs. 690 // Variables with fallback prefs do not need to be defined here because their 691 // defaults are the values of their fallbacks. 692 const NIMBUS_DEFAULTS = { 693 addonsShowLessFrequentlyCap: 0, 694 quickSuggestScoreMap: null, 695 realtimeMinKeywordLength: null, 696 realtimeShowLessFrequentlyCap: null, 697 weatherKeywordsMinimumLength: null, 698 weatherShowLessFrequentlyCap: null, 699 yelpMinKeywordLength: null, 700 yelpSuggestNonPriorityIndex: null, 701 }; 702 703 // Maps preferences under browser.urlbar.suggest to behavior names, as defined 704 // in mozIPlacesAutoComplete. 705 const SUGGEST_PREF_TO_BEHAVIOR = { 706 history: "history", 707 bookmark: "bookmark", 708 openpage: "openpage", 709 searches: "search", 710 }; 711 712 const PREF_TYPES = new Map([ 713 ["boolean", "Bool"], 714 ["float", "Float"], 715 ["number", "Int"], 716 ["string", "Char"], 717 ]); 718 719 /** 720 * Builds the default result groups and returns the root group. Result 721 * groups determine the composition of results in the muxer, i.e., how they're 722 * grouped and sorted. Each group is an object that looks like this: 723 * 724 * @typedef {object} ResultGroup 725 * @property {Values<typeof lazy.UrlbarUtils.RESULT_GROUP>} [group] 726 * This is defined only on groups without children, and it determines the 727 * result group that the group will contain. 728 * @property {number} [maxResultCount] 729 * An optional maximum number of results the group can contain. If it's 730 * not defined and the parent group does not define `flexChildren: true`, 731 * then the max is the parent's max. If the parent group defines 732 * `flexChildren: true`, then `maxResultCount` is ignored. 733 * @property {boolean} [flexChildren] 734 * If true, then child groups are "flexed", similar to flex in HTML. Each 735 * child group should define the `flex` property (or, if they don't, `flex` 736 * is assumed to be zero). `flex` is a number that defines the ratio of a 737 * child's result count to the total result count of all children. More 738 * specifically, `flex: X` on a child means that the initial maximum result 739 * count of the child is `parentMaxResultCount * (X / N)`, where `N` is the 740 * sum of the `flex` values of all children. If there are any child groups 741 * that cannot be completely filled, then the muxer will attempt to overfill 742 * the children that were completely filled, while still respecting their 743 * relative `flex` values. 744 * @property {number} [flex] 745 * The flex value of the group. This should be defined only on groups 746 * where the parent defines `flexChildren: true`. See `flexChildren` for a 747 * discussion of flex. 748 * @property {ResultGroup[]} [children] 749 * An array of child group objects. 750 * @property {string} [orderBy] 751 * Results should be ordered descending by this payload property. 752 * 753 * @param {object} options 754 * @param {boolean} options.showSearchSuggestionsFirst 755 * If true, the suggestions group will come before the general group. 756 */ 757 function makeDefaultResultGroups({ showSearchSuggestionsFirst }) { 758 /** 759 * @type {ResultGroup} 760 */ 761 let rootGroup = { 762 children: [ 763 // heuristic 764 { 765 maxResultCount: 1, 766 children: [ 767 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }, 768 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_EXTENSION }, 769 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_SEARCH_TIP }, 770 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_OMNIBOX }, 771 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_ENGINE_ALIAS }, 772 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_BOOKMARK_KEYWORD }, 773 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_AUTOFILL }, 774 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_TOKEN_ALIAS_ENGINE }, 775 { 776 group: 777 lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_RESTRICT_KEYWORD_AUTOFILL, 778 }, 779 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_HISTORY_URL }, 780 { group: lazy.UrlbarUtils.RESULT_GROUP.HEURISTIC_FALLBACK }, 781 ], 782 }, 783 // extensions using the omnibox API 784 { 785 group: lazy.UrlbarUtils.RESULT_GROUP.OMNIBOX, 786 }, 787 ], 788 }; 789 790 // Prepare the parent group for suggestions and general. 791 let mainGroup = { 792 flexChildren: true, 793 children: [ 794 // suggestions 795 { 796 children: [ 797 { 798 flexChildren: true, 799 children: [ 800 { 801 // If `maxHistoricalSearchSuggestions` == 0, the muxer forces 802 // `maxResultCount` to be zero and flex is ignored, per query. 803 flex: 2, 804 group: lazy.UrlbarUtils.RESULT_GROUP.FORM_HISTORY, 805 }, 806 { 807 flex: 99, 808 group: lazy.UrlbarUtils.RESULT_GROUP.RECENT_SEARCH, 809 }, 810 { 811 flex: 4, 812 group: lazy.UrlbarUtils.RESULT_GROUP.REMOTE_SUGGESTION, 813 }, 814 ], 815 }, 816 { 817 group: lazy.UrlbarUtils.RESULT_GROUP.TAIL_SUGGESTION, 818 }, 819 ], 820 }, 821 // general 822 { 823 group: lazy.UrlbarUtils.RESULT_GROUP.GENERAL_PARENT, 824 children: [ 825 { 826 availableSpan: 3, 827 group: lazy.UrlbarUtils.RESULT_GROUP.INPUT_HISTORY, 828 }, 829 { 830 flexChildren: true, 831 children: [ 832 { 833 flex: 1, 834 group: lazy.UrlbarUtils.RESULT_GROUP.REMOTE_TAB, 835 }, 836 { 837 flex: 2, 838 group: lazy.UrlbarUtils.RESULT_GROUP.GENERAL, 839 orderBy: "frecency", 840 }, 841 { 842 // We show relatively many about-page results because they're 843 // only added for queries starting with "about:". 844 flex: 2, 845 group: lazy.UrlbarUtils.RESULT_GROUP.ABOUT_PAGES, 846 }, 847 { 848 flex: 99, 849 group: lazy.UrlbarUtils.RESULT_GROUP.RESTRICT_SEARCH_KEYWORD, 850 }, 851 ], 852 }, 853 { 854 group: lazy.UrlbarUtils.RESULT_GROUP.INPUT_HISTORY, 855 }, 856 ], 857 }, 858 ], 859 }; 860 if (!showSearchSuggestionsFirst) { 861 mainGroup.children.reverse(); 862 } 863 mainGroup.children[0].flex = 2; 864 mainGroup.children[1].flex = 1; 865 rootGroup.children.push(mainGroup); 866 867 return rootGroup; 868 } 869 870 /** 871 * Preferences class. The exported object is a singleton instance. 872 */ 873 class Preferences { 874 /** 875 * Constructor 876 */ 877 constructor() { 878 this._map = new Map(); 879 this.QueryInterface = ChromeUtils.generateQI([ 880 "nsIObserver", 881 "nsISupportsWeakReference", 882 ]); 883 884 Services.prefs.addObserver(PREF_URLBAR_BRANCH, this, true); 885 for (let pref of PREF_OTHER_DEFAULTS.keys()) { 886 Services.prefs.addObserver(pref, this, true); 887 } 888 this._observerWeakRefs = []; 889 this.addObserver(this); 890 891 // These prefs control the value of the shouldHandOffToSearchMode pref. They 892 // are exposed as a class variable so UrlbarPrefs observers can watch for 893 // changes in these prefs. 894 this.shouldHandOffToSearchModePrefs = [ 895 "keyword.enabled", 896 "suggest.searches", 897 ]; 898 899 lazy.NimbusFeatures.urlbar.onUpdate(() => this._onNimbusUpdate()); 900 } 901 902 /** 903 * Returns the value for the preference with the given name. 904 * For preferences in the "browser.urlbar."" branch, the passed-in name 905 * should be relative to the branch. It's also possible to get prefs from the 906 * PREF_OTHER_DEFAULTS Map, specifying their full name. 907 * 908 * @param {string} pref 909 * The name of the preference to get. 910 * @returns {*} The preference value. 911 */ 912 get(pref) { 913 let value = this._map.get(pref); 914 if (value === undefined) { 915 value = this._getPrefValue(pref); 916 this._map.set(pref, value); 917 } 918 return value; 919 } 920 921 /** 922 * Sets the value for the preference with the given name. 923 * For preferences in the "browser.urlbar."" branch, the passed-in name 924 * should be relative to the branch. It's also possible to set prefs from the 925 * PREF_OTHER_DEFAULTS Map, specifying their full name. 926 * 927 * @param {string} pref 928 * The name of the preference to set. 929 * @param {*} value The preference value. 930 */ 931 set(pref, value) { 932 let { defaultValue, set } = this._getPrefDescriptor(pref); 933 if (typeof value != typeof defaultValue) { 934 throw new Error(`Invalid value type ${typeof value} for pref ${pref}`); 935 } 936 set(pref, value); 937 } 938 939 /** 940 * Adds a value to a preference that handles multiple comma-separated values. 941 * Throws an error if the preference does not have a comma-separated value. 942 * 943 * @param {string} pref 944 * The name of the preference to set. 945 * @param {*} value 946 * The preference value. 947 */ 948 add(pref, value) { 949 let maybeSet = this._getPrefValue(pref); 950 951 if (!(maybeSet instanceof Set)) { 952 throw new Error( 953 `The pref ${pref} should handle the values as Set but '${typeof maybeSet}'` 954 ); 955 } 956 957 maybeSet.add(value); 958 this.set(pref, [...maybeSet].join(",")); 959 } 960 961 /** 962 * Clears the value for the preference with the given name. 963 * 964 * @param {string} pref 965 * The name of the preference to clear. 966 */ 967 clear(pref) { 968 let { clear } = this._getPrefDescriptor(pref); 969 clear(pref); 970 } 971 972 /** 973 * Returns whether the given preference has a value on the user branch. 974 * 975 * @param {string} pref 976 * The name of the preference. 977 * @returns {boolean} 978 * Whether the pref has a value on the user branch. 979 */ 980 hasUserValue(pref) { 981 let { hasUserValue } = this._getPrefDescriptor(pref); 982 return hasUserValue(pref); 983 } 984 985 /** 986 * Gets a pref but allows the `scotchBonnet.enableOverride` pref to 987 * short circuit them so one pref can be used to enable multiple 988 * features. 989 * 990 * @param {string} pref 991 * The name of the preference to clear. 992 * @returns {*} The preference value. 993 */ 994 getScotchBonnetPref(pref) { 995 return this.get("scotchBonnet.enableOverride") || this.get(pref); 996 } 997 998 getResultGroups({ context }) { 999 // We try to cache result groups so we don't have to rebuild them each time. 1000 // This key may be modified per each SAP, and will track the cached groups, 1001 // any additionally used condition must be added to the key. 1002 let key = context.sapName; 1003 switch (context.sapName) { 1004 case "urlbar": { 1005 let showSearchSuggestionsFirst = 1006 context.searchString || 1007 (!this.get("suggest.trending") && 1008 !this.get("suggest.recentsearches")); 1009 1010 let inSearchEngineMode = !!context.searchMode?.engineName; 1011 1012 // If we're in a case were search suggestions would be shown first, but not 1013 // in search engine mode, then just use the user preference. 1014 if (!inSearchEngineMode && showSearchSuggestionsFirst) { 1015 showSearchSuggestionsFirst = this.get("showSearchSuggestionsFirst"); 1016 } 1017 1018 key += showSearchSuggestionsFirst; 1019 return this.#getOrCacheResultGroups(key, () => 1020 makeDefaultResultGroups({ showSearchSuggestionsFirst }) 1021 ); 1022 } 1023 case "searchbar": { 1024 // This is a temporary placeholder until searchbar gets its own config. 1025 return this.#getOrCacheResultGroups(key, () => 1026 makeDefaultResultGroups({ showSearchSuggestionsFirst: true }) 1027 ); 1028 } 1029 case "smartbar": { 1030 // This is a temporary placeholder until smartbar gets its own config. 1031 return this.#getOrCacheResultGroups(key, () => 1032 makeDefaultResultGroups({ showSearchSuggestionsFirst: false }) 1033 ); 1034 } 1035 default: { 1036 throw new Error(`Unknown SAP name: ${context.sapName}`); 1037 } 1038 } 1039 } 1040 1041 /** 1042 * Adds a preference observer. Observers are held weakly. 1043 * 1044 * @param {object} observer 1045 * An object that may optionally implement one or both methods: 1046 * - `onPrefChanged` invoked when one of the preferences listed here 1047 * change. It will be passed the pref name. For prefs in the 1048 * `browser.urlbar.` branch, the name will be relative to the branch. 1049 * For other prefs, the name will be the full name. 1050 * - `onNimbusChanged` invoked when a Nimbus value changes. It will be 1051 * passed the name of the changed Nimbus variable and the new value. 1052 */ 1053 addObserver(observer) { 1054 this._observerWeakRefs.push(Cu.getWeakReference(observer)); 1055 } 1056 1057 /** 1058 * Removes a preference observer. 1059 * 1060 * @param {object} observer 1061 * An observer previously added with `addObserver()`. 1062 */ 1063 removeObserver(observer) { 1064 for (let i = 0; i < this._observerWeakRefs.length; i++) { 1065 let obs = this._observerWeakRefs[i].get(); 1066 if (obs && obs == observer) { 1067 this._observerWeakRefs.splice(i, 1); 1068 break; 1069 } 1070 } 1071 } 1072 1073 /** 1074 * Observes preference changes. 1075 * 1076 * @param {nsISupports} subject 1077 * The subject of the notification. 1078 * @param {string} topic 1079 * The topic of the notification. 1080 * @param {string} data 1081 * The data attached to the notification. 1082 */ 1083 observe(subject, topic, data) { 1084 let pref = data.replace(PREF_URLBAR_BRANCH, ""); 1085 if (!PREF_URLBAR_DEFAULTS_MAP.has(pref) && !PREF_OTHER_DEFAULTS.has(pref)) { 1086 return; 1087 } 1088 this.#notifyObservers("onPrefChanged", pref); 1089 } 1090 1091 /** 1092 * Called when a pref tracked by UrlbarPrefs changes. 1093 * 1094 * @param {string} pref 1095 * The name of the pref, relative to `browser.urlbar.` if the pref is 1096 * in that branch. 1097 */ 1098 onPrefChanged(pref) { 1099 this._map.delete(pref); 1100 1101 // Some prefs may influence others. 1102 switch (pref) { 1103 case "autoFill.adaptiveHistory.useCountThreshold": 1104 this._map.delete("autoFillAdaptiveHistoryUseCountThreshold"); 1105 return; 1106 case "showSearchSuggestionsFirst": 1107 this.#cachedResultGroups.clear(); 1108 return; 1109 } 1110 1111 if (pref.startsWith("suggest.")) { 1112 this._map.delete("defaultBehavior"); 1113 } 1114 1115 if (this.shouldHandOffToSearchModePrefs.includes(pref)) { 1116 this._map.delete("shouldHandOffToSearchMode"); 1117 } 1118 } 1119 1120 /** 1121 * Called when the `NimbusFeatures.urlbar` value changes. 1122 */ 1123 _onNimbusUpdate() { 1124 let oldNimbus = this._clearNimbusCache(); 1125 let newNimbus = this._nimbus; 1126 1127 // Callback to observers having onNimbusChanged. 1128 let variableNames = new Set(Object.keys(oldNimbus)); 1129 for (let name of Object.keys(newNimbus)) { 1130 variableNames.add(name); 1131 } 1132 for (let name of variableNames) { 1133 if ( 1134 oldNimbus.hasOwnProperty(name) != newNimbus.hasOwnProperty(name) || 1135 oldNimbus[name] !== newNimbus[name] 1136 ) { 1137 this.#notifyObservers("onNimbusChanged", name, newNimbus[name]); 1138 } 1139 } 1140 } 1141 1142 /** 1143 * Clears cached Nimbus variables. The cache will be repopulated the next time 1144 * `_nimbus` is accessed. 1145 * 1146 * @returns {object} 1147 * The value of the cache before it was cleared. It's an object that maps 1148 * from variable names to values. 1149 */ 1150 _clearNimbusCache() { 1151 let nimbus = this.__nimbus; 1152 if (nimbus) { 1153 for (let key of Object.keys(nimbus)) { 1154 this._map.delete(key); 1155 } 1156 this.__nimbus = null; 1157 } 1158 return nimbus || {}; 1159 } 1160 1161 get _nimbus() { 1162 if (!this.__nimbus) { 1163 this.__nimbus = lazy.NimbusFeatures.urlbar.getAllVariables({ 1164 defaultValues: NIMBUS_DEFAULTS, 1165 }); 1166 } 1167 return this.__nimbus; 1168 } 1169 1170 /** 1171 * Returns the raw value of the given preference straight from Services.prefs. 1172 * 1173 * @param {string} pref 1174 * The name of the preference to get. 1175 * @returns {*} The raw preference value. 1176 */ 1177 _readPref(pref) { 1178 let { defaultValue, get } = this._getPrefDescriptor(pref); 1179 return get(pref, defaultValue); 1180 } 1181 1182 /** 1183 * Returns a validated and/or fixed-up value of the given preference. The 1184 * value may be validated for correctness, or it might be converted into a 1185 * different value that is easier to work with than the actual value stored in 1186 * the preferences branch. Not all preferences require validation or fixup. 1187 * 1188 * The values returned from this method are the values that are made public by 1189 * this module. 1190 * 1191 * @param {string} pref 1192 * The name of the preference to get. 1193 * @returns {*} The validated and/or fixed-up preference value. 1194 */ 1195 _getPrefValue(pref) { 1196 switch (pref) { 1197 case "shortcuts.actions": { 1198 return this.get("scotchBonnet.enableOverride") && this._readPref(pref); 1199 } 1200 case "defaultBehavior": { 1201 let val = 0; 1202 for (let type of Object.keys(SUGGEST_PREF_TO_BEHAVIOR)) { 1203 let behavior = `BEHAVIOR_${SUGGEST_PREF_TO_BEHAVIOR[ 1204 type 1205 ].toUpperCase()}`; 1206 val |= 1207 this.get("suggest." + type) && Ci.mozIPlacesAutoComplete[behavior]; 1208 } 1209 return val; 1210 } 1211 case "shouldHandOffToSearchMode": 1212 return this.shouldHandOffToSearchModePrefs.some( 1213 prefName => !this.get(prefName) 1214 ); 1215 case "autoFillAdaptiveHistoryUseCountThreshold": { 1216 const nimbusValue = 1217 this._nimbus.autoFillAdaptiveHistoryUseCountThreshold; 1218 return nimbusValue === undefined 1219 ? this.get("autoFill.adaptiveHistory.useCountThreshold") 1220 : parseFloat(nimbusValue); 1221 } 1222 case "exposureResults": 1223 case "keywordExposureResults": 1224 case "quicksuggest.dynamicSuggestionTypes": 1225 case "quicksuggest.realtimeOptIn.dismissTypes": 1226 case "quicksuggest.realtimeOptIn.notNowTypes": 1227 return new Set( 1228 this._readPref(pref) 1229 .split(",") 1230 .map(s => s.trim()) 1231 .filter(s => !!s) 1232 ); 1233 } 1234 return this._readPref(pref); 1235 } 1236 1237 /** 1238 * Returns a descriptor of the given preference. 1239 * 1240 * @param {string} pref The preference to examine. 1241 * @returns {object} An object describing the pref with the following shape: 1242 * { defaultValue, get, set, clear } 1243 */ 1244 _getPrefDescriptor(pref) { 1245 let branch = Services.prefs.getBranch(PREF_URLBAR_BRANCH); 1246 let defaultValue = PREF_URLBAR_DEFAULTS_MAP.get(pref); 1247 if (defaultValue === undefined) { 1248 branch = Services.prefs; 1249 defaultValue = PREF_OTHER_DEFAULTS.get(pref); 1250 if (defaultValue === undefined) { 1251 let nimbus = this._getNimbusDescriptor(pref); 1252 if (nimbus) { 1253 return nimbus; 1254 } 1255 throw new Error("Trying to access an unknown pref " + pref); 1256 } 1257 } 1258 1259 let type; 1260 if (!Array.isArray(defaultValue)) { 1261 type = PREF_TYPES.get(typeof defaultValue); 1262 } else { 1263 if (defaultValue.length != 2) { 1264 throw new Error("Malformed pref def: " + pref); 1265 } 1266 [defaultValue, type] = defaultValue; 1267 type = PREF_TYPES.get(type); 1268 } 1269 if (!type) { 1270 throw new Error("Unknown pref type: " + pref); 1271 } 1272 return { 1273 defaultValue, 1274 get: branch[`get${type}Pref`], 1275 // Float prefs are stored as Char. 1276 set: branch[`set${type == "Float" ? "Char" : type}Pref`], 1277 clear: branch.clearUserPref, 1278 hasUserValue: branch.prefHasUserValue, 1279 }; 1280 } 1281 1282 /** 1283 * Returns a descriptor for the given Nimbus property, if it exists. 1284 * 1285 * @param {string} name 1286 * The name of the desired property in the object returned from 1287 * NimbusFeatures.urlbar.getAllVariables(). 1288 * @returns {object} 1289 * An object describing the property's value with the following shape (same 1290 * as _getPrefDescriptor()): 1291 * { defaultValue, get, set, clear } 1292 * If the property doesn't exist, null is returned. 1293 */ 1294 _getNimbusDescriptor(name) { 1295 if (!this._nimbus.hasOwnProperty(name)) { 1296 return null; 1297 } 1298 return { 1299 defaultValue: this._nimbus[name], 1300 get: () => this._nimbus[name], 1301 set() { 1302 throw new Error(`'${name}' is a Nimbus value and cannot be set`); 1303 }, 1304 clear() { 1305 throw new Error(`'${name}' is a Nimbus value and cannot be cleared`); 1306 }, 1307 hasUserValue() { 1308 throw new Error( 1309 `'${name}' is a Nimbus value and does not have a user value` 1310 ); 1311 }, 1312 }; 1313 } 1314 1315 /** 1316 * Return whether or not persisted search terms is enabled. 1317 * 1318 * @returns {boolean} true: if enabled. 1319 */ 1320 isPersistedSearchTermsEnabled() { 1321 return ( 1322 this.getScotchBonnetPref("showSearchTerms.featureGate") && 1323 this.get("showSearchTerms.enabled") && 1324 !lazy.CustomizableUI.getPlacementOfWidget("search-container") 1325 ); 1326 } 1327 1328 /** 1329 * @type {Map<string, ResultGroup>} 1330 * Result groups cached by search access point and params used to build them. 1331 */ 1332 #cachedResultGroups = new Map(); 1333 #getOrCacheResultGroups(key, builder) { 1334 let groups = this.#cachedResultGroups.get(key); 1335 if (!groups) { 1336 groups = builder(); 1337 this.#cachedResultGroups.set(key, groups); 1338 } 1339 return groups; 1340 } 1341 1342 #notifyObservers(method, changed, ...rest) { 1343 for (let i = 0; i < this._observerWeakRefs.length; ) { 1344 let observer = this._observerWeakRefs[i].get(); 1345 if (!observer) { 1346 // The observer has been GC'ed, so remove it from our list. 1347 this._observerWeakRefs.splice(i, 1); 1348 continue; 1349 } 1350 if (method in observer) { 1351 try { 1352 observer[method](changed, ...rest); 1353 } catch (ex) { 1354 console.error(ex); 1355 } 1356 } 1357 ++i; 1358 } 1359 } 1360 } 1361 1362 export var UrlbarPrefs = new Preferences();